6.2 KiB
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):
- Ve a Databases → Selecciona tu database
- Crea una nueva colección llamada
guild_cache - 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 | - |
- 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:
# 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:
- En la consola de Appwrite, abre la colección
guild_cache - Copia el Collection ID que aparece en la parte superior
Cambios Implementados
Archivos Modificados
-
src/core/api/appwrite.ts- Agregado:
APPWRITE_COLLECTION_GUILD_CACHE_ID - Nueva función:
isGuildCacheConfigured()
- Agregado:
-
src/core/database/guildCache.ts(REESCRITO COMPLETAMENTE)- Migrado de Redis a Appwrite Database
getGuildConfig(): Lee desde Appwrite, valida expiracióninvalidateGuildCache(): Elimina documento de AppwriteupdateGuildCache(): Actualiza o crea documentocleanExpiredGuildCache(): Limpia documentos expirados (nueva función)
-
src/main.ts- Agregado job de limpieza cada 10 minutos
- Importa
cleanExpiredGuildCache()
-
.env.example- Documentadas todas las variables de Appwrite
Funciones
getGuildConfig(guildId, guildName, prisma)
// 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)
// Elimina el documento de Appwrite
// Se llama cuando se actualiza: prefix, staff, AI role prompt
cleanExpiredGuildCache()
// 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
- Ve a tu colección
guild_cache - Deberías ver documentos con:
guildId: ID del servidorname: Nombre del servidorprefix: Prefix configurado (o vacío)expiresAt: Fecha de expiración (5 minutos en el futuro)
3. Probar cambio de prefix
- Ejecuta
!settingsen Discord - Cambia el prefix
- Verifica en los logs:
🗑️ Caché de guild invalidada - 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:
- Restaura el archivo anterior:
git checkout HEAD~1 src/core/database/guildCache.ts
- Comenta las líneas en
main.ts:
// import { cleanExpiredGuildCache } from "./core/database/guildCache";
// setInterval(async () => { ... }, 10 * 60 * 1000);
- Redeploy
Próximos Pasos (Opcional)
Si quieres optimizar aún más:
-
Migrar otros cachés a Appwrite:
- Cooldowns de usuarios
- Stats frecuentes
- Inventarios activos
-
Implementar caché híbrido:
- Memoria local (LRU) para guilds muy activos
- Appwrite para persistencia
-
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