# Migración de Caché: Redis → Appwrite ## Problema Redis con 30MB de memoria ya estaba usando 2.4MB (8%) solo para el caché de configuración de guilds. Con el crecimiento del bot, esto podría saturar rápidamente la instancia de Redis. ## Solución: Usar Appwrite Database como Caché Appwrite Database ofrece: - ✅ **Más espacio**: Sin límites estrictos de 30MB - ✅ **Persistencia**: Los datos sobreviven reinicios - ✅ **Gratuito**: Ya lo tienes configurado en el proyecto - ✅ **Consultas avanzadas**: Permite búsquedas y filtros complejos ## Configuración en Appwrite ### 1. Crear la Colección En tu consola de Appwrite (`console.appwrite.io` o tu instancia): 1. Ve a **Databases** → Selecciona tu database 2. Crea una nueva colección llamada `guild_cache` 3. Configura los siguientes atributos: | Atributo | Tipo | Tamaño | Requerido | Único | Default | |----------|------|--------|-----------|-------|---------| | `guildId` | String | 32 | ✅ Sí | ✅ Sí | - | | `name` | String | 100 | ✅ Sí | ❌ No | - | | `prefix` | String | 10 | ❌ No | ❌ No | `null` | | `expiresAt` | DateTime | - | ✅ Sí | ❌ No | - | 4. Crea un **Índice** en `expiresAt` (tipo: Key, ascendente) para optimizar las búsquedas de limpieza ### 2. Configurar Permisos En la colección, ve a **Settings** → **Permissions**: - **Create**: API Key - **Read**: API Key - **Update**: API Key - **Delete**: API Key ### 3. Variables de Entorno Agrega a tu `.env`: ```env # Appwrite APPWRITE_ENDPOINT=https://cloud.appwrite.io/v1 APPWRITE_PROJECT_ID=tu_project_id APPWRITE_API_KEY=tu_api_key APPWRITE_DATABASE_ID=tu_database_id APPWRITE_COLLECTION_GUILD_CACHE_ID=guild_cache_collection_id ``` Para obtener el `APPWRITE_COLLECTION_GUILD_CACHE_ID`: 1. En la consola de Appwrite, abre la colección `guild_cache` 2. Copia el **Collection ID** que aparece en la parte superior ## Cambios Implementados ### Archivos Modificados 1. **`src/core/api/appwrite.ts`** - Agregado: `APPWRITE_COLLECTION_GUILD_CACHE_ID` - Nueva función: `isGuildCacheConfigured()` 2. **`src/core/database/guildCache.ts`** (REESCRITO COMPLETAMENTE) - Migrado de Redis a Appwrite Database - `getGuildConfig()`: Lee desde Appwrite, valida expiración - `invalidateGuildCache()`: Elimina documento de Appwrite - `updateGuildCache()`: Actualiza o crea documento - `cleanExpiredGuildCache()`: Limpia documentos expirados (nueva función) 3. **`src/main.ts`** - Agregado job de limpieza cada 10 minutos - Importa `cleanExpiredGuildCache()` 4. **`.env.example`** - Documentadas todas las variables de Appwrite ### Funciones #### `getGuildConfig(guildId, guildName, prisma)` ```typescript // 1. Intenta leer desde Appwrite // 2. Verifica si expiró (expiresAt < now) // 3. Si expiró o no existe, hace upsert en PostgreSQL // 4. Guarda en Appwrite con TTL de 5 minutos // 5. Retorna la configuración ``` #### `invalidateGuildCache(guildId)` ```typescript // Elimina el documento de Appwrite // Se llama cuando se actualiza: prefix, staff, AI role prompt ``` #### `cleanExpiredGuildCache()` ```typescript // Busca documentos con expiresAt < now // Elimina hasta 100 documentos expirados por ejecución // Se ejecuta cada 10 minutos automáticamente ``` ## Comparación: Redis vs Appwrite | Característica | Redis (Antes) | Appwrite (Ahora) | |----------------|---------------|------------------| | **Memoria** | 30MB límite | ~Ilimitado | | **Persistencia** | Volátil (se pierde al reiniciar) | Persistente | | **TTL** | Automático (`SETEX`) | Manual (verificación en lectura) | | **Costo** | Limitado por plan | Incluido en plan gratis | | **Queries** | Básicas (key-value) | Avanzadas (filtros, búsquedas) | | **Latencia** | ~1-5ms | ~50-100ms | ### Nota sobre Latencia Appwrite es **ligeramente más lento** que Redis (~50-100ms vs ~1-5ms), pero: - ✅ Solo se consulta cada 5 minutos por guild - ✅ El 99% de las consultas vienen de caché - ✅ La diferencia es imperceptible para el usuario final ## Testing ### 1. Verificar que funciona Busca en los logs: ``` ✅ Guild config obtenida desde caché (Appwrite) ✅ Guild config guardada en caché (Appwrite) 🗑️ Caché de guild invalidada (Appwrite) 🧹 Documentos expirados eliminados de caché ``` ### 2. Verificar en Appwrite Console 1. Ve a tu colección `guild_cache` 2. Deberías ver documentos con: - `guildId`: ID del servidor - `name`: Nombre del servidor - `prefix`: Prefix configurado (o vacío) - `expiresAt`: Fecha de expiración (5 minutos en el futuro) ### 3. Probar cambio de prefix 1. Ejecuta `!settings` en Discord 2. Cambia el prefix 3. Verifica en los logs: `🗑️ Caché de guild invalidada` 4. El próximo comando debería usar el nuevo prefix inmediatamente ## Uso de Memoria ### Estimación por Guild Cada documento en Appwrite ocupa aproximadamente: ``` guildId: 20 bytes name: 50 bytes (promedio) prefix: 3 bytes expiresAt: 8 bytes Metadata: ~50 bytes (Appwrite overhead) ─────────────────────── TOTAL: ~131 bytes ``` Para **1,000 guilds** = **~128 KB** (mucho menos que Redis) ### Redis Liberado Al migrar el caché de guilds a Appwrite: - **Antes**: ~2.4 MB en Redis - **Después**: ~0 MB en Redis (solo para otras cosas) - **Ahorro**: ~8% de la memoria de Redis ## Rollback (si algo falla) Si necesitas volver a Redis: 1. Restaura el archivo anterior: ```bash git checkout HEAD~1 src/core/database/guildCache.ts ``` 2. Comenta las líneas en `main.ts`: ```typescript // import { cleanExpiredGuildCache } from "./core/database/guildCache"; // setInterval(async () => { ... }, 10 * 60 * 1000); ``` 3. Redeploy ## Próximos Pasos (Opcional) Si quieres optimizar aún más: 1. **Migrar otros cachés a Appwrite**: - Cooldowns de usuarios - Stats frecuentes - Inventarios activos 2. **Implementar caché híbrido**: - Memoria local (LRU) para guilds muy activos - Appwrite para persistencia 3. **Agregar métricas**: - Cache hit rate - Latencia promedio - Documentos expirados/hora --- **Fecha**: 2025-10-07 **Cambio**: Migración de Redis → Appwrite para caché de guilds **Razón**: Ahorrar memoria en Redis (30MB limitados) **Estado**: ✅ COMPLETADO