Nivel 2 · 20 min
Estrategias de Invalidación de Caché
Phil Karlton: ''Solo hay dos problemas difíciles en Computer Science: invalidación de caché y nombrar cosas.'' La invalidación decide cuándo y cómo un valor en caché deja de ser válido. Las estrategias principales: TTL-based, eviction policies, event-driven invalidation y cache poisoning.
TTL y expiración
Time To Live (TTL) es la forma más simple de invalidación — el dato expira después de N segundos. Fácil de implementar, pero imprecisa: puede servir datos stale hasta que expire, o invalidar datos que siguen siendo válidos. El TTL óptimo depende de la frecuencia de cambio (si cambia cada 5 min, TTL de 1 hora genera stale) y la tolerancia a staleness (datos de perfil vs precios en tiempo real). EXPIRE en Redis, setex para set+expire atómico.
Políticas de evicción en Redis
Cuando Redis alcanza maxmemory, activa la política de evicción configurada. Las principales: noeviction (devuelve error en writes), allkeys-lru (evicta los menos recientemente usados de todas las claves), volatile-lru (evicta solo claves con TTL), allkeys-lfu (evicta los menos frecuentemente usados), allkeys-random (evicción aleatoria). LRU es buena para caché de acceso reciente. LFU es mejor para hot keys que siempre se necesitan. Configurar con maxmemory-policy.
Invalidación dirigida por eventos y cache poisoning
Event-driven invalidation: cuando la DB cambia, publicás un evento que invalida las entradas de caché afectadas. CDC (Change Data Capture) con Debezium puede capturar cambios de la DB y publicarlos a Kafka — los consumidores invalidan el caché. Más complejo pero preciso. Cache poisoning ocurre cuando el caché almacena datos incorrectos — puede ser por: cachear respuestas de error, race condition donde dos escrituras concurrentes producen un valor inconsistente, o inyección maliciosa.
Code example
// Invalidacion dirigida por eventos
onOrderUpdated(orderId) {
await redis.del(`order:${orderId}`);
await redis.del(`user:${userId}:orders`);
await redis.del(`stats:orders:${today}`);
}
// Evitar cache poisoning: solo cachear respuestas exitosas
const data = await fetchFromDB(id);
if (data !== null) {
await redis.setex(`entity:${id}`, 3600, JSON.stringify(data));
}