Yeda AI Tips · #163

English

Breaker por fuera, retry por dentro

Retry y circuit breaker en el orden equivocado rompen los dos en silencio. Ambos patrones protegen la misma llamada a una dependencia inestable, pero hacen trabajos opuestos — y cuál envuelves por fuera decide si una caída cuesta milisegundos o se lleva por delante tu presupuesto de latencia. La mayoría de las librerías te dejan anidarlos en cualquier orden, y el anidado equivocado se ve perfecto en cada prueba en verde.

Dos patrones, una llamada

Un retry asume que las fallas son transitorias: el timeout parpadeó, el pod estaba reiniciándose, la conexión se reseteó. Vuelve a intentar — idealmente unas pocas veces, con backoff y jitter — esperando que el siguiente intento tenga éxito.

Un circuit breaker asume lo contrario. Tras un umbral de fallas recientes se abre (trips open) y, durante una ventana de enfriamiento, rechaza las llamadas al instante en vez de dejarlas golpear una dependencia que ya está en el suelo. Es una máquina de estados — Closed, Open, Half-Open — que evita que sigas martillando un servicio mientras se recupera, y evita que una dependencia lenta amarre todos los hilos de tu app.

Tiran en direcciones distintas a propósito. El retry dice "intenta más fuerte". El breaker dice "deja de intentar". Justo por eso el orden importa.

La regla: breaker(retry(call))

Envuelve el breaker por fuera y el retry por dentro:

breaker(
  retry(
    call ) )

Ahora el breaker ve cada llamada como una sola unidad de trabajo. El retry gasta sus intentos adentro; el breaker registra un resultado — éxito o falla final. Una vez que el breaker está Open, toda la secuencia de retry se salta y la llamada falla en microsegundos.

Invierte el orden — retry(breaker(call)) — y cada intento del retry golpea el breaker por separado. Una sola petición lógica ahora registra tres o cinco fallas, así que el breaker se abre demasiado pronto por conteos inflados. Peor aún, cuando está Open, tu bucle de retry sigue llamándolo, recorriendo su backoff contra un breaker diseñado para que te detengas.

Lo que cuesta el orden equivocado

breaker(retry(call)) — correctoretry(breaker(call)) — invertido
Fallas que cuenta el breaker por petición1hasta N (una por intento)
Cuando el breaker está Opense salta todo el retry, falla rápidoel bucle de retry sigue girando sobre el backoff
Umbral de aperturaajustado a resultados realesse abre pronto por conteos inflados
Latencia durante una caídamicrosegundostodo el presupuesto de retry desperdiciado

Resilience4j es la trampa clásica: su orden de aspectos por defecto es Retry(CircuitBreaker(...)) — retry por fuera — que es la disposición invertida, y es una fuente documentada de conteos de falla inflados. Si lo usas, pon circuitBreakerAspectOrder por encima de retryAspectOrder para que el breaker envuelva al retry.

Movidas de experto

Recursos

¿Construyes sistemas de IA y backend resilientes? Yeda AI diseña, audita y despliega servicios en producción que fallan con gracia. Este es un tip de la serie Yeda AI Tips — patrones cortos y prácticos para ingenieros que construyen con IA.