Hace unas semanas en Q2BSTUDIO vivimos un incidente de producción que empezó silencioso y explotó justo en el pico de tráfico. La combinación fue Aurora MySQL, una función Lambda con timeout de 30 segundos y una estrategia de invalidación de caché mal diseñada que acabó inundando la base de datos. Aquí explico qué pasó, por qué falló y qué cambiamos para que no vuelva a ocurrir.

La noche anterior al incidente actualizamos la versión del motor Aurora MySQL. Todo parecía normal, sin alarmas. A la mañana siguiente la tarea diaria responsable de eliminar la caché obsoleta de los datos maestros y de volver a rellenarla arrancó como siempre. Ese dataset maestro es crítico para que la aplicación funcione con normalidad, y si la caché no está caliente la base de datos recibe todo el tráfico.

Tras la actualización, una consulta concreta dentro de la Lambda empezó a tardar más de 30 segundos. Nuestra Lambda tenía un timeout de 30 segundos, así que la secuencia eliminar caché luego reconstruir falló: la caché quedó vacía y cada petición de usuario hizo miss en caché y golpeó la base de datos. La CPU de Aurora se disparó hasta 99 por ciento y las respuestas de la aplicación se ralentizaron en masa. Clásico sello de estampida de caché.

Por suerte activamos un failover y en el nuevo escritor la misma consulta corrió en unos 28,7 segundos, justo por debajo del timeout, lo que nos dio unos minutos para estabilizar. Por la noche detectamos la causa real: la actualización del motor cambió el plan de ejecución y la consulta necesitaba un nuevo índice. Creamos el índice como hotfix y la base de datos se estabilizó. Pero el problema de fondo era la estrategia de invalidación de caché.

Nuestra lógica original era simple y peligrosa: borrar la clave de caché, leer la base de datos y volver a escribir el resultado. Si la lectura fallaba, la caché quedaba vacía y el sistema colapsaba. Aprendimos que esa fórmula delete then refresh es frágil cuando hay latencias o cambios en el plan de consultas tras un upgrade.

Estos son los cambios que implementamos y recomendamos a equipos que diseñan sistemas escalables:

1. Nunca borrar la caché antes de tener datos frescos Invertimos el flujo por uno seguro: primero leer datos frescos, luego validar y finalmente actualizar la caché. Solo eliminar la versión antigua cuando ya existe la nueva. Esto elimina la ventana en la que la caché está vacía.

2. Usar rollover de stale en lugar de borrado contundente Si la tarea de refresco falla ahora renombramos la clave Master-Data a Master-Data-Stale y mantenemos disponible el valor anterior mientras se dispara una notificación interna para investigación. De este modo, aunque la DB esté lenta, el sistema sigue sirviendo datos válidos pero potencialmente obsoletos, evitando un derrumbe.

3. La capa API devuelve datos stale cuando no hay frescos La lógica de la API ahora intenta leer Master-Data y, si no existe, intenta reconstruir solo si está permitido; si la reconstrucción falla devuelve los datos stale. Esto evita fallos en cascada por intentos simultáneos de reconstrucción.

4. Añadir un lock distribuido en Redis para evitar la estampida Sin un lock, múltiples nodos o Lambdas intentarán reconstruir al mismo tiempo y volverán a golpear la DB. Con un lock distribuido solo una petición gana y reconstruye; las demás devuelven datos stale y esperan a que la caché se rellene. Esta medida reduce dramáticamente el riesgo de estampida.

5. Mejor observabilidad sobre los tiempos de refresco Registramos tiempo de ejecución de consultas, duración de refrescos de caché, métricas de adquisición de lock y alertas cuando un refresco supera umbrales. El objetivo es detectar degradaciones antes de que provoquen timeouts.

Para equipos que desarrollan aplicaciones y software a medida, estos cambios son críticos. En Q2BSTUDIO, como empresa especializada en desarrollo de software a medida y aplicaciones a medida, aplicamos estas lecciones tanto en proyectos de backend como en arquitecturas serverless y servicios cloud. Si necesita migrar o diseñar sistemas resilientes en la nube podemos ayudarle con nuestros servicios cloud aws y azure y con arquitecturas que minimicen riesgos operativos. Conozca más sobre cómo diseñamos aplicaciones robustas en nuestra página de software y desarrollo de aplicaciones a medida y sobre nuestras ofertas en la nube en servicios cloud aws y azure.

Además, en Q2BSTUDIO integramos prácticas de ciberseguridad y pentesting en todo el ciclo de vida para garantizar que los mecanismos de caché y sincronización no introduzcan vectores de ataque. También trabajamos con soluciones de inteligencia artificial y ia para empresas que requieren modelos y agentes IA integrados de forma segura en sus plataformas, y con servicios de inteligencia de negocio y power bi para dar visibilidad a incidencias y rendimiento.

Lecciones clave para llevarse a casa: las actualizaciones de motor pueden cambiar planos de ejecución, por lo que siempre es recomendable benchmarkear consultas críticas tras cambios mayores; la invalidación de caché debe asumir que el refresco puede fallar y servir datos stale es preferible a servir errores; los locks distribuidos son una defensa eficaz contra la estampida; y la observabilidad es la forma de detectar problemas antes de que disparen timeouts.

El incidente fue estresante, pero las mejoras introducidas aumentaron significativamente la resiliencia de nuestras plataformas. Si su arquitectura usa patrones delete then refresh revise esa lógica cuanto antes para que no le revise a usted en horas punta.