La caché que arregla tu latencia y crea tres problemas nuevos

La invalidación, la estampida y el día que la caché se llena son los tres. Un tiempo de vida es la respuesta aburrida y correcta al primero, y la mayoría de los problemas de caché resultan ser un índice que falta.

Una página va lenta, así que alguien añade una caché. La página se vuelve rápida, todo el mundo contento, y el sistema adquiere tres modos de fallo nuevos que no se van a presentar en meses: usuarios viendo datos rancios después de una actualización, una estampida cuando caduca una clave popular, y una política de expulsión que nadie eligió y que empieza a descartar en silencio datos de sesión bajo presión de memoria.

Cachear merece la pena. Es también el componente que más a menudo se añade sin decidir nada, y las decisiones son lo que determina si ayuda.

Primero, comprueba si el problema era la consulta

El primer paso honesto, y el que se salta.

Una caché delante de una consulta que hace un escaneo secuencial sobre una tabla grande esconde el problema a cambio de riesgo de corrección. Añadir el índice hace que la consulta sin caché sea lo bastante rápida como para que la caché sea innecesaria, y ayuda a todas las demás consultas que tocan esa tabla. Saca primero la lista de consultas lentas, como se describe en ajustar PostgreSQL gestionado.

Cachea cuando el trabajo sea genuinamente caro y genuinamente repetido: una agregación sobre un conjunto grande, una llamada a una API lenta de terceros, un fragmento renderizado ensamblado desde varias fuentes, un conjunto de permisos calculado. No cuando el trabajo sea una consulta mal escrita.

Invalidación, y por qué un tiempo de vida suele ser lo correcto

Hay tres enfoques y el sector ha convergido en buena medida en el menos ingenioso.

Caducidad por tiempo de vida. Fija una duración, acepta que el dato puede estar rancio ese tiempo, y sigue. Se cura solo: un error que deja un valor equivocado en la caché se arregla por sí mismo, y no hay que coordinar nada. Es el valor por defecto correcto en la abrumadora mayoría de los casos, y el trabajo de diseño es elegir la duración a partir de cuánto puede estar rancio el dato de forma aceptable, no de cuánto tarda en calcularse.

Invalidación explícita al escribir. Borra o actualiza la clave cuando cambia el dato subyacente. Preciso, y exige que todos los caminos de escritura conozcan todas las claves de caché derivadas de ese dato. Funciona bien cuando un servicio es dueño de los dos. Falla cuando un segundo servicio, un trabajo por lotes o un arreglo manual en la base de datos cambian el dato sin pasar por el camino de escritura, y ese fallo es silencioso.

Escritura directa. Actualizar la caché y el almacén a la vez. Consistente dentro de un proceso y no sobrevive a un fallo parcial entre las dos escrituras sin más maquinaria de la que la mayoría de los equipos quiere.

Nuestro valor por defecto: tiempo de vida en todas partes, más invalidación explícita en el número pequeño de claves donde el dato rancio sea genuinamente inaceptable, y nunca invalidación explícita sola.

Dos hábitos de apoyo. Versiona la clave cuando cambie la forma del valor cacheado, en vez de intentar purgar, porque un despliegue que cambia un formato de serialización contra una caché poblada produce errores difíciles de atribuir. Y añade variación aleatoria a la duración para que las claves pobladas juntas no caduquen todas a la vez.

La estampida y los tres arreglos

Caduca una clave popular. Doscientas peticiones simultáneas fallan, las doscientas ejecutan el cálculo caro, y la base de datos que la caché protegía recibe doscientas consultas simultáneas. Esto es la caché empeorando la caída en vez de evitarla, y ocurre en el peor momento por definición.

Tres mitigaciones, por esfuerzo creciente:

  • Variación aleatoria en la caducidad, que reparte los fallos y no ayuda cuando una sola clave está genuinamente caliente.
  • Un bloqueo para que solo una petición recalcule, mientras las demás esperan un momento o sirven el valor rancio. La forma de servir lo rancio mientras se revalida suele ser el mejor comportamiento: devuelve el valor antiguo de inmediato y refresca en segundo plano.
  • Refresco proactivo de las claves que se sabe que están calientes, lo que exige saber cuáles lo están.

Implementa al menos la variación aleatoria y el bloqueo de una sola ejecución en todo lo caro. Esto no es un problema teórico; es el segundo incidente más común relacionado con caché, después del dato rancio.

Redis o Valkey, tras el cambio de licencia

Redis cambió su licencia alejándose de un modelo permisivo de código abierto, lo que llevó a la Linux Foundation a adoptar una bifurcación, Valkey, que continúa con las condiciones permisivas originales. Redis ha vuelto a poner disponible además una licencia permisiva para versiones recientes, así que la situación está menos clara que durante la ruptura inicial.

Para la mayoría de los equipos esto cambia muy poco. El protocolo es el mismo, los clientes funcionan con los dos, y los proveedores de nube ofrecen servicios gestionados de uno o de ambos, con varios que han movido su oferta gestionada a la bifurcación.

Decide sobre la misma base que la pregunta equivalente de infraestructura en OpenTofu o Terraform: si usas un servicio gestionado, usa lo que ofrezca tu proveedor y no fabriques un problema. Si lo autoalojas y las condiciones de licencia importan a tu organización o a tu producto, la bifurcación elimina la pregunta. Si dependes de módulos concretos de Redis, comprueba la disponibilidad antes de cambiar, porque ahí es donde muerde de verdad la divergencia.

Memoria, expulsión y el día que se llena

Una caché tiene memoria acotada, y lo que pasa en el límite es una política que o elegiste o heredaste.

Fija la política de expulsión a conciencia. Expulsar la clave menos usada recientemente entre las que tienen caducidad es el valor por defecto sensato para una caché. Una política que no expulsa nunca devuelve errores al escribir cuando la memoria está llena, lo que convierte una caché en una caída. Una política que expulsa cualquier clave, incluidas las que no tienen caducidad, descartará alegremente los datos de sesión que también metiste ahí.

Lo que lleva al punto más importante: no mezcles una caché y un almacén duradero en una instancia. Si algunas claves no se pueden perder, van en algún sitio con otras garantías de persistencia, o como mínimo en una instancia separada con otra política.

Vigila la tasa de aciertos, las expulsiones y la fragmentación de memoria. Una tasa de aciertos baja significa que la caché te está costando latencia y dinero sin ayudar, y una caché con mucha expulsión está dando vueltas, lo que es peor que no cachear.

Qué no cachear

  • El dato que el usuario acaba de escribir. La lectura posterior a escritura a través de una caché produce la misma clase de error que el retraso de una réplica.
  • Nada personal sin pensar en la conservación. Una caché que guarda datos personales es un almacén de datos a efectos de privacidad, con las obligaciones de supresión de el RGPD como controles de ingeniería. Un tiempo de vida es al menos una regla de conservación, que es una razón más para preferirlo.
  • Decisiones de permisos, durante mucho tiempo. Una autorización cacheada que sobrevive a un acceso revocado es un hallazgo de seguridad.
  • Valores que son baratos de calcular. El viaje de red hasta la caché no es gratis, y para una consulta rápida puede ser más lento que la consulta.

Lo que se olvida

  • La caché es ahora una dependencia. ¿Qué pasa cuando no es alcanzable? La respuesta correcta suele ser degradar a la fuente, con un cortocircuito para que una caché lenta no añada latencia a todas las peticiones.
  • La serialización es una superficie de compatibilidad. Dos versiones de la aplicación leyendo los objetos cacheados de la otra durante un despliegue progresivo es un modo de fallo real.
  • Una instancia única es un punto único de fallo. Decide a conciencia si necesitas replicación, y recuerda que la conmutación lleva tiempo durante el cual la fuente recibe toda la carga.
  • Los límites de conexión también aplican aquí. Una función sin servidor que se despliega abre conexiones a la caché además de a la base de datos.
  • La caducidad de claves no es un planificador. Apoyar lógica de negocio en las notificaciones de caducidad es frágil.

Qué hacer esta semana

Mira la tasa de aciertos y la de expulsiones de tu caché de la última semana. Si la tasa de aciertos está por debajo de alrededor del ochenta por ciento, pregunta qué te está comprando la caché, porque estás pagando un viaje de red en cada fallo. Después coge tu cálculo cacheado más caro y comprueba si es posible una estampida cuando caduque su clave. Las dos cosas llevan veinte minutos y las dos suelen encontrar algo. Lo miramos en la fase de arquitectura de un proyecto cloud.

ConsultorIA

¿Quieres esto en tu nube?

El diagnóstico de diez días en solo lectura es gratuito, y con Skyline puedes ver tu entorno en un mapa antes de escribirnos.

Artículos relacionados

Módulos que la gente reutiliza en vez de copiar

Los dos fracasos son un módulo que envuelve un recurso y no aporta nada, y un módulo que lo hace todo y que nadie se atreve a cambiar. Una interfaz mínima, valores por defecto seguros y un versionado honesto son lo que los separa.