Sistemas Distribuidos · Ingeniería de Datos

Fallos, timeouts y patrones de resiliencia

Explicar el fallo parcial en sistemas distribuidos y aplicar timeouts, reintentos con backoff, idempotencia, circuit breaker y bulkhead para contener el daño.

Fallos, timeouts y patrones de resiliencia

La pregunta no es si fallará, sino cuándo y de qué forma En un solo proceso un error es una excepción; en un sistema distribuido es una posibilidad constante y parcial: la llamada puede tardar, perderse o ejecutarse dos veces. La resiliencia no es evitar fallar, sino fallar sin derribar todo.

Cuando un servicio llama por red a otro, entran tres enemigos que no existen dentro de un proceso: latencia incierta, particiones de red y fallos parciales. No sabes si tu petición se perdió al ir o si la respuesta se perdió al volver. Todos los patrones de resiliencia nacen de aceptar esa incertidumbre.

El fallo parcial

Un nodo puede estar "vivo" para unos y "muerto" para otros. Ante una llamada sin respuesta, no hay forma de distinguir en el instante si la petición llegó, se procesó o se perdió; por eso la corrección exige idempotencia, no solo reintentos.

Timeouts: ponle reloj a todo

Toda llamada remota necesita un tiempo máximo. Sin timeout, un hilo espera para siempre, se agotan las conexiones y una dependencia lenta tumba a tu servicio. Un timeout demasiado corto, en cambio, cancela trabajo que iba a terminar y dispara reintentos inútiles.

Diagrama de tres bandas: arriba tu servicio que inicia la llamada; en el centro los patrones en el camino (timeout, retry con backoff, circuit breaker, bulkhead); abajo los servicios remotos a los que se llama, que pueden fallar de forma independiente.
Entre tu servicio y una dependencia lenta o caida se interponen los cuatro patrones que acotan el daño.

Reintentos: útiles y peligrosos

Reintentar salva fallos transitorios, pero reintento sin control amplifica la carga: si todos los clientes reintentan a la vez sobre un servicio caído, se arma una tormenta de reintentos que impide que se recupere.

Idempotencia: repetir sin dañar

Una operación es idempotente si ejecutarla varias veces tiene el mismo efecto que ejecutarla una. Es lo que hace seguros a los reintentos. Un PUT que fija un valor es idempotente; un POST que crea un cargo, no. Cuando la operación no es naturalmente idempotente, se añade una clave de idempotencia que el servidor usa para descartar duplicados.

Cada patrón cubre un modo distinto de fallo; se complementan.
PatrónQué protegeRiesgo si se omite
TimeoutQue una dependencia lenta congele recursosHilos y conexiones agotados
Retry + backoffFallos transitorios brevesTormenta de reintentos
Circuit breakerNo martillar un servicio caídoColapso en cascada
BulkheadAislar recursos por dependenciaUn fallo consume todo el servicio
IdempotenciaRepetir sin duplicar efectosCargos o escrituras duplicadas

Circuit breaker: descansar al servicio caído

Un interruptor cuenta fallos; si superan un umbral, se abre y deja de llamar durante una ventana, fallando rápido sin sobrecargar al dependiente. Tras el descanso pasa a medio abierto: deja pasar algunas llamadas para probar; si van bien, se cierra y se reanuda el tráfico normal.

Bulkhead: compartimentos estancos

Como en un barco, se divide recursos (hilos, conexiones) por dependencia, para que una llamada lenta a un servicio llene solo su compartimento y no hunda el buque completo. Un fallo aislado no consume la capacidad de todo el servicio.

Errores frecuentes

Ejemplo resuelto: llamada a una pasarela de pago

Un servicio cobra llamando a una pasarela externa que a veces se tarda o responde mal. Secuencia de defensa:

  1. Fijar un timeout por debajo del tiempo que el usuario está dispuesto a esperar.
  2. Enviar una clave de idempotencia para que un reintento no cobre dos veces.
  3. Reintentar con backoff exponencial y jitter, con presupuesto máximo.
  4. Abrir un circuit breaker si la pasarela falla de forma sostenida.
  5. Aislar con bulkhead las conexiones de pago para no afectar otras funciones.

¿Para qué sirve en la realidad?

Los grandes apagones de servicios en línea rara vez nacen de un solo fallo: nacen de un fallo pequeño que se amplificó por reintentos y esperas sin límites. La resiliencia es lo que convierte una molestia local en algo que el usuario casi no nota.

¿Qué problema intenta resolver principalmente un circuit breaker?

Reintentar una operación no idempotente puede provocar efectos duplicados, como un doble cargo.

La espera creciente entre reintentos, con variación aleatoria para que los clientes no coincidan, se llama backoff exponencial con .

Clasifica cada patrón según el problema que resuelve.

Aislar daño
Repetir seguro

Une cada patrón con su idea central.

Resiliencia distribuida

No ejecutar varias veces cambia el resultado
idempotencia
Deja de llamar a un servicio caído un rato
circuit breaker
Esperas aleatorias entre reintentos
jitter
Compartimentos de recursos por dependencia
bulkhead
Tiempo máximo de espera de una llamada
timeout

Fallos, timeouts y patrones de resiliencia

# Resiliencia
## Fallo parcial
### latencia incierta
### particion de red
## Timeouts
### acotar espera
## Reintentos
### backoff exponencial
### jitter y presupuesto
## Idempotencia
### repetir sin danar
### clave de idempotencia
## Aislar
### circuit breaker
### bulkhead

Timeouts, reintentos y backoff con jitter (AWS Builders' Library)
Explicación práctica y rigurosa de por qué y cómo reintentar sin amplificar fallos.

CircuitBreaker (Martin Fowler)
Descripción clásica del patrón con sus estados cerrado, abierto y medio abierto.

Tu servicio de checkout depende de una pasarela de pago que empieza a responder en 8 segundos aunque suele tardar 200 ms. Sin tocar la pasarela, ¿qué combinación de timeout, reintentos, breaker y bulkhead aplicarías para que ese deterioro no tumbe toda la tienda?