Altamar Labs
Volver a Blog
confiabilidad· 3 min de lectura

Reintentos que empeoran la caída: el problema de las retry storms

Reintentar una petición fallida parece la solución obvia. Sin coordinación, es una de las formas más comunes de convertir una falla pequeña en una caída total.

Reintentar una operación que falló es, casi siempre, la primera línea de defensa que se enseña contra fallos transitorios de red. Es también, sin la coordinación correcta, una de las formas más comunes de convertir un problema pequeño en una caída completa del sistema.

Cómo se forma una retry storm

Imaginemos un servicio que empieza a responder lento, no caído del todo, solo lento, quizás por presión de carga momentánea. Los clientes que dependen de él empiezan a recibir timeouts y, siguiendo la lógica razonable de "reintentar en caso de falla", disparan una nueva petición casi de inmediato. Ese reintento añade más carga exactamente al servicio que ya estaba luchando por mantenerse al día, lo que produce más timeouts, lo que produce más reintentos. El mecanismo diseñado para dar resiliencia termina acelerando el colapso.

Esto no es un caso hipotético raro. Es el patrón detrás de buena parte de las caídas en cascada que se ven en sistemas distribuidos reales: un componente se degrada un poco, y la reacción colectiva de sus clientes lo termina de tumbar.

Por qué "simplemente reintentar" no es suficiente

Hay tres ingredientes que separan un reintento razonable de uno peligroso:

Backoff exponencial. Reintentar inmediatamente después de un fallo es casi siempre la peor opción. Esperar un intervalo que crece con cada intento fallido (1 segundo, luego 2, luego 4) le da al sistema tiempo real para recuperarse antes del siguiente golpe.

Jitter. Si mil clientes fallan al mismo tiempo y todos usan exactamente el mismo backoff, todos van a reintentar exactamente al mismo tiempo, sincronizados, lo cual recrea el mismo pico de carga que causó el problema original. Añadir aleatoriedad al tiempo de espera (jitter) rompe esa sincronización y distribuye los reintentos en el tiempo.

Un límite real de reintentos. Sin un techo, un cliente puede seguir reintentando indefinidamente contra un servicio que simplemente no se va a recuperar pronto, consumiendo recursos propios y ajenos sin ningún beneficio.

El ingrediente que casi siempre falta: el circuit breaker

Backoff y jitter mejoran el comportamiento individual de un reintento, pero no resuelven el problema de fondo cuando un servicio está genuinamente caído, no solo lento. Ahí es donde entra el patrón de circuit breaker: después de cierto número de fallos consecutivos, el cliente deja de intentar por completo durante una ventana de tiempo, fallando rápido en lugar de seguir golpeando un servicio que no va a responder. Esto protege al servicio que falla (le da espacio para recuperarse sin tráfico adicional) y protege al cliente (deja de gastar tiempo y recursos en peticiones que casi con certeza van a fallar).

La lección

El reintento ingenuo optimiza para el caso feliz: una falla aislada y momentánea que se resuelve con un segundo intento. El diseño maduro asume el caso adverso: una falla que afecta a muchos clientes a la vez, donde la reacción colectiva no coordinada es, en sí misma, el siguiente incidente. Backoff exponencial, jitter y circuit breakers no son optimizaciones opcionales para sistemas a gran escala. Son la diferencia entre un fallo que se autocontiene y uno que se autoamplifica.