Software Architecture
Patrones de resiliencia
Explica timeouts, circuit breakers, bulkheads, backoff, jitter, load shedding y degradación como patrones para contener fallos y recuperar capacidad.
- Última actualización
- Actualizada
- Nivel
- Aplicación
Software Architecture
Explica timeouts, circuit breakers, bulkheads, backoff, jitter, load shedding y degradación como patrones para contener fallos y recuperar capacidad.
La resiliencia no consiste en evitar todos los fallos. Consiste en limitar su alcance, conservar capacidades importantes, recuperar el estado y evitar que una dependencia degradada arrastre al resto del sistema.
En un sistema real, una dependencia no falla únicamente con un error claro. Puede:
La arquitectura resiliente parte de esta incertidumbre.
fallo local
→ detección
→ contención
→ degradación
→ recuperación
→ reconciliaciónEl objetivo no es responder siempre. Una respuesta rápida pero incorrecta puede ser peor que una indisponibilidad explícita.
Supón que el proveedor de pagos empieza a responder en 15 segundos.
Sin controles:
El fallo de una dependencia se convirtió en una caída general por falta de límites.
Cada flujo tiene un presupuesto limitado de:
Los patrones de resiliencia asignan y protegen ese presupuesto.
request total: 2 s
→ conexión: 100 ms
→ intento 1: 500 ms
→ backoff: 100 ms
→ intento 2: 500 ms
→ margen y respuesta: 800 msNo puedes añadir retries infinitos sin exceder el tiempo y la capacidad disponibles.
Un timeout limita cuánto espera una operación.
Debe basarse en:
Un timeout demasiado alto retiene recursos. Uno demasiado bajo convierte variabilidad normal en errores y retries.
Si una escritura hace timeout, el resultado puede ser ambiguo:
cliente envía pago
→ proveedor procesa
→ respuesta se pierde
→ cliente observa timeoutReintentar sin idempotencia puede cobrar dos veces. La aplicación debe guardar estado pending, consultar o reconciliar.
Un retry repite una operación cuando existe probabilidad de que el fallo sea transitorio.
429 o 503 con política explícita.Sin espera, todos los clientes reintentan al mismo tiempo.
intento 1 → 100 ms
intento 2 → 200 ms
intento 3 → 400 msEl jitter añade variación para evitar sincronización.
No uses backoff ilimitado en la ruta interactiva. Define máximo de intentos y deadline total.
Controla qué proporción de tráfico puede ser reintento.
Ejemplo:
requests originales: 10.000/min
budget de retry: 10 %
→ máximo aproximado: 1.000 retries/minEvita que una degradación duplique o triplique la carga.
Debe existir una capa responsable. Cliente, gateway y servicio no deberían repetir independientemente sin coordinación.
Un circuit breaker deja de enviar llamadas a una dependencia cuando el fallo supera un umbral.
Estados conceptuales:
closed
→ tráfico normal
→ fallos superan umbral
open
→ rechazar rápido
→ espera
half-open
→ pocas pruebas
→ closed u openDebe segmentarse por dependencia, operación y quizá región o tenant cuando el patrón de fallo lo requiera.
Separa recursos para que una carga no consuma todo.
Ejemplos:
pagos → pool 20
reportes → pool 5
notificaciones → pool 10Si reportes se atasca, pagos conserva capacidad.
La capacidad aislada puede quedar ociosa mientras otra cola espera. La separación se justifica por criticidad y riesgo de interferencia.
Un semáforo impide ejecutar más de N operaciones simultáneas hacia un recurso.
await limiter.run(async () => paymentGateway.authorize(command));En producción, el límite necesita:
Aceptar trabajo ilimitado solo mueve la saturación a memoria o conexiones.
Protege capacidad frente a productores externos o internos.
Puede aplicarse por:
Rate limiting controla entrada. Bulkhead controla interferencia interna. Pueden combinarse.
Rechaza trabajo antes del colapso.
Ejemplo:
capacidad crítica
→ aceptar pedidos y pagos
→ rechazar reportes pesados
→ desactivar recomendacionesUna respuesta 503 rápida puede ser mejor que esperar 30 segundos y agotar todos los recursos.
Define prioridad antes de la emergencia y comunica reintentos seguros.
Permite que un consumidor lento reduzca la velocidad del productor.
En mensajería:
En streams:
Sin backpressure, el backlog crece hasta agotar almacenamiento o superar el tiempo útil del dato.
Un fallback ofrece una respuesta alternativa válida.
Ejemplos:
No es válido:
La degradación debe conservar semántica y comunicar limitaciones.
Para contenido tolerante a staleness, se puede servir copia antigua cuando el origen falla.
origen caído
→ copia de hace 2 minutos
→ mostrar “actualización pendiente”Es apropiado para catálogo o configuración no crítica. No para decisiones financieras o permisos sensibles sin controles adicionales.
Si una lectura supera un percentil, se envía otra solicitud a una réplica diferente y se usa la primera respuesta válida.
Beneficio:
Costo:
Solo para operaciones idempotentes, con límite y cancelación.
Una cola desacopla picos de la capacidad de procesamiento.
pico de 10.000 jobs
→ cola
→ workers procesan a tasa sostenibleNecesita:
La cola no crea capacidad. Solo distribuye trabajo en el tiempo.
Diseña niveles:
nivel 0: operación completa
nivel 1: sin recomendaciones
nivel 2: catálogo stale
nivel 3: solo lectura
nivel 4: rechazar nuevas operaciones críticasCada nivel define:
La resiliencia no termina cuando la dependencia vuelve.
Después pueden existir:
La recuperación necesita una tasa controlada. Liberar todo el backlog puede causar una segunda caída.
Compara la autoridad con estados locales o externos.
Ejemplo pagos:
pending antiguos.Debe existir como proceso normal, no solo script de emergencia.
Algunos estados no pueden resolverse automáticamente.
Un runbook debe indicar:
La intervención manual necesita autorización y auditoría.
request
→ espera 20 s
→ timeout
→ retry en gateway
→ retry en cliente
→ saturaciónpending si el resultado es ambiguo.Las notificaciones no deberían bloquear confirmar un pedido.
transacción pedido + outbox
→ respuesta
→ worker envía correoSi proveedor falla:
El pedido sigue confirmado; la notificación tiene su propia disponibilidad.
La operación pudo completarse. Consulta por idempotency key antes de repetir.
Half-open debe probar con carga mínima y evitar abrir todos los clientes al mismo tiempo.
Define límite, expiración o rechazo. Un backlog infinito no es resiliencia.
Muestra staleness y limita acciones que dependen de frescura.
Dos servicios creen tener bulkheads, pero usan el mismo pool de conexiones global. El aislamiento es falso.
Un tenant consume todo el retry budget. Segmenta cuotas y observabilidad.
Amplifica carga y duplica efectos. Define ownership del retry.
Se abre o cierra sin entender impacto. Observa estado, causa y rejected calls.
El sistema parece sano mientras entrega datos incorrectos. Marca degradación.
Si la capacidad promedio es insuficiente, la cola solo retrasa el colapso.
Cada dependencia tiene distribución y semántica diferentes.
La cola delante del pool crece indefinidamente.
El backlog derriba una dependencia recién recuperada.
Mide por dependencia y operación:
Las métricas de negocio revelan impacto: pedidos atascados, pagos pendientes o notificaciones retrasadas.
Chaos testing debe empezar en entornos controlados y con hipótesis, no como destrucción aleatoria.
Siempre que exista espera remota o recurso finito.
Solo para fallos transitorios y operaciones seguras.
Cuando insistir empeora latencia o carga.
Cuando workloads tienen criticidad o riesgo de interferencia diferente.
Cuando existe una respuesta degradada semánticamente válida.
Cuando los picos pueden diferirse y el tiempo útil lo permite.
Idempotencia, retries y garantías de entrega profundiza en cómo repetir operaciones y mensajes sin duplicar efectos, especialmente cuando el resultado es incierto.