Seis fallos de caché que tumban producción y cómo se atajan
Un equipo de ingeniería repasa los seis modos de fallo de caché que les han tumbado producción, con las soluciones que aplicaron a cada uno.

Un equipo de ingeniería ha publicado el catálogo de fallos de caché que les ha ido rompiendo producción, con el remedio que aplicaron en cada caso. No hay benchmark ni métricas de latencia detrás: son seis escenarios vividos, contados por lo que costaron. La conclusión que repiten es sencilla: hasta que no ves caer el sistema, no sabes por dónde va a partirse.
Claves que no existen y claves que no se pueden leer
El primer caso es el más inocente. Alguien pide un dato por primera vez, la caché está vacía y no pasa nada. Se resuelve con cache-aside: se lee de la base de datos, se rellena la entrada y se devuelve. El patrón por defecto, y por defecto está bien, porque solo guarda lo que de verdad se pide.
El segundo es el que se disfraza. La clave está ahí, pero los bytes no se pueden deserializar: un cambio de esquema, un desfase de versión o una escritura mala dejaron el valor ilegible. Visto desde fuera parece un miss, y en realidad es un fallo de lectura que alguien se está tragando sin enterarse. La solución que adoptaron fue versionar las claves junto al esquema (user:v2:123, por ejemplo), de modo que los valores viejos e incompatibles dejen de consultarse en lugar de reventar al leerse, y registrar los fallos de deserialización en un contador aparte del de los misses reales. Mezclar ambos oculta bugs. A ellos les ocurrió con una deserialización de Jackson y el sistema entero se comportó de forma errática.
La estampida del TTL y la caché que no está
Cuando una clave caliente caduca, miles de peticiones concurrentes fallan a la vez y arremeten contra la base de datos en bloque. En claves frías da igual; en las calientes es un problema. Sus tres respuestas: un mutex que deja que la primera petición que llega recompute el valor mientras las demás esperan o reciben el dato viejo; expiración lógica, que consiste en no dejar que el TTL borre nunca la clave y en guardar un expires_at interno para servir el valor caducado al instante y refrescarlo en segundo plano —esto lo tienen en producción—; y TTL con jitter, para que las claves calientes no expiren todas al mismo tiempo.
Distinto es la penetración: alguien consulta un ID que tampoco está en la base de datos, así que nunca se cachea nada y cada repetición vuelve a golpear el backend. Puede ser un error o un bot probando identificadores al azar. Aquí entran la caché negativa, con TTL corto, que también tienen desplegada, y un filtro de Bloom por delante para descartar claves imposibles antes de llegar al backend.
El escenario grave es el cluster completo —nodo y réplicas— cayéndose a la vez. Cada petición del sistema acaba en la base de datos al mismo tiempo: no es un miss localizado, es una avalancha. En su caso los sentinels de lectura no estaban bien configurados. Lo que recomiendan es alta disponibilidad en la propia capa de caché con failover automático, un circuit breaker y limitación de tasa delante de la base de datos como última defensa, y una caché L1 en proceso por delante de Redis que amortigüe el golpe aunque el L2 esté muerto.
Queda el timeout de red. La caché está sana y tiene la clave, pero el cliente no recibe la respuesta a tiempo. Según la política de fail-open o fail-closed, la petición suele caer a la base de datos aunque el dato no faltara en absoluto.
Lo aprovechable de la lista es que casi todo se puede instrumentar: separar fallos de lectura de misses verdaderos en las métricas, añadir caché negativa y jitter, y decidir qué política de fallo quieres cuando la caché no contesta. Lo que el texto no da son números: ni latencias, ni tamaño de los clusters, ni tasas de acierto. Cada cual tendrá que medir los suyos antes de copiar los TTL.

