Error y resiliencia
Patrones de resiliencia en acción: circuit breaker, retry, timeout y fallback aplicados a flujos reales entre servicios.
Por qué la resiliencia es parte del flujo
En un sistema distribuido, los fallos no son excepciones: son parte normal de la operación. Servicios que se caen, redes que fallan, bases de datos que se saturan. La pregunta no es si va a fallar, sino cómo se comporta el sistema cuando falla.
Los patrones de resiliencia no son conceptos teóricos aislados. Se aplican en los flujos reales entre servicios para que el sistema siga funcionando de forma degradada en lugar de colapsar completamente.
Los cuatro patrones principales
Timeout
Establece un tiempo máximo de espera para una operación. Si no hay respuesta en ese tiempo, se cancela la petición y se maneja el error.
Retry
Reintenta una operación que falló, asumiendo que el fallo puede ser transitorio (un pico de carga, un error de red momentáneo).
Circuit Breaker
Detecta cuando un servicio está fallando repetidamente y deja de enviarle peticiones por un tiempo, permitiéndole recuperarse.
Fallback
Proporciona una respuesta alternativa cuando la operación principal falla.
Timeout en acción
sequenceDiagram
participant BFF as BFF
participant MS as ms-catalog
participant DB as Database
BFF->>MS: GET /products (timeout: 3s)
MS->>DB: SELECT * FROM products
Note over DB: La DB está lenta (5s)
BFF--xMS: Timeout después de 3s
BFF->>BFF: Manejar timeout
BFF->>BFF: Devolver respuesta de caché o error
Cómo configurar timeouts
Cada llamada entre servicios debe tener un timeout explícito. Sin timeout, una petición puede quedar colgada indefinidamente, consumiendo recursos.
Reglas prácticas:
- El timeout debe ser mayor que el tiempo de respuesta normal del servicio
- Pero menor que el tiempo que el usuario está dispuesto a esperar
- Diferentes operaciones pueden tener diferentes timeouts
| Operación | Timeout sugerido |
|---|---|
| Consulta de catálogo | 2–3 segundos |
| Creación de orden | 5 segundos |
| Pago externo | 10–30 segundos |
| Envío de notificación | 5 segundos |
Qué hacer cuando hay timeout
- Registrar el timeout en los logs con contexto (servicio, operación, duración)
- Devolver una respuesta degradada si es posible (datos de caché, valores por defecto)
- No asumir que la operación falló: puede haberse completado pero la respuesta se perdió
Retry en acción
sequenceDiagram
participant BFF as BFF
participant MS as ms-orders
BFF->>MS: POST /orders
MS-->>BFF: 503 Service Unavailable
Note over BFF: Esperar 1s (intento 2)
BFF->>MS: POST /orders
MS-->>BFF: 503 Service Unavailable
Note over BFF: Esperar 2s (intento 3)
BFF->>MS: POST /orders
MS-->>BFF: 201 Created
Estrategias de retry
Retry inmediato: reintentar sin espera. Solo útil para errores muy transitorios.
Retry con backoff fijo: esperar un tiempo fijo entre reintentos (1s, 1s, 1s).
Retry con backoff exponencial: duplicar el tiempo de espera en cada intento (1s, 2s, 4s, 8s). Es la estrategia más recomendada porque reduce la presión sobre el servicio que está fallando.
Retry con jitter: añadir un componente aleatorio al backoff para evitar que múltiples clientes reintenten al mismo tiempo (thundering herd).
Cuándo reintentar y cuándo no
Reintentar:
- Errores 503 (Service Unavailable)
- Errores 429 (Too Many Requests)
- Timeouts de red
- Errores de conexión
No reintentar:
- Errores 400 (Bad Request) — el request es inválido
- Errores 401/403 — problema de autenticación/autorización
- Errores 404 — el recurso no existe
- Errores 409 (Conflict) — conflicto de estado
Idempotencia y retry
Si se reintenta una operación de escritura (crear orden, procesar pago), el servicio receptor debe ser idempotente. De lo contrario, el retry puede crear duplicados.
Circuit Breaker en acción
stateDiagram-v2
[*] --> Closed
Closed --> Open: N fallos consecutivos
Open --> HalfOpen: Después del timeout
HalfOpen --> Closed: Request exitoso
HalfOpen --> Open: Request falla
Los tres estados
Closed (cerrado): el circuito funciona normalmente. Las peticiones pasan al servicio. Si se acumulan fallos, el circuito se abre.
Open (abierto): el circuito está cortado. Las peticiones no llegan al servicio; se devuelve un error o un fallback inmediatamente. Esto protege al servicio que está fallando y evita que el llamador desperdicie recursos esperando.
Half-Open (semi-abierto): después de un tiempo, el circuito permite pasar una petición de prueba. Si tiene éxito, el circuito se cierra. Si falla, vuelve a abrirse.
Ejemplo concreto
El BFF llama al microservicio de catálogo. Si 5 de las últimas 10 peticiones fallaron:
- El circuit breaker se abre
- Durante 30 segundos, todas las peticiones al catálogo reciben un fallback (datos de caché)
- Después de 30 segundos, se permite una petición de prueba
- Si funciona, el circuito se cierra y el tráfico normal se reanuda
Configuración típica
| Parámetro | Valor típico |
|---|---|
| Umbral de fallos | 50% en ventana de 10 requests |
| Tiempo en estado Open | 30 segundos |
| Requests de prueba en Half-Open | 1 |
Fallback en acción
Cuando una operación falla (por timeout, circuit breaker abierto o error), el fallback proporciona una alternativa:
| Servicio | Fallback |
|---|---|
| Catálogo | Devolver datos de caché (aunque estén obsoletos) |
| Recomendaciones | Devolver productos populares genéricos |
| Perfil de usuario | Devolver datos básicos del token JWT |
| Notificaciones | Encolar para envío posterior |
| Analytics | Descartar el evento (no es crítico) |
Niveles de degradación
No todos los fallos requieren la misma respuesta. Se pueden definir niveles:
- Degradación mínima: datos ligeramente obsoletos (caché de 5 minutos)
- Degradación moderada: funcionalidad reducida (catálogo sin filtros avanzados)
- Degradación severa: funcionalidad mínima (solo lectura, sin operaciones de escritura)
- Indisponibilidad: página de mantenimiento
Combinando patrones
En la práctica, estos patrones se combinan:
BFF → [Timeout: 3s] → [Retry: 3 intentos con backoff] → [Circuit Breaker] → ms-catalog
↓ (si abierto)
[Fallback: caché]
- El BFF hace una petición con timeout de 3 segundos
- Si hay timeout, reintenta hasta 3 veces con backoff exponencial
- Si los reintentos fallan, el circuit breaker se abre
- Con el circuito abierto, las siguientes peticiones reciben el fallback directamente
Observabilidad de la resiliencia
Cada patrón debe generar métricas y logs:
- Timeouts: cuántos, en qué servicio, duración
- Retries: cuántos reintentos, tasa de éxito en cada intento
- Circuit Breaker: transiciones de estado, tiempo en cada estado
- Fallbacks: cuántas veces se activó, qué tipo de fallback
Estas métricas permiten detectar problemas antes de que se conviertan en incidentes graves.
Resumen
La resiliencia no es un componente que se añade al final. Es una propiedad del sistema que se diseña en cada flujo. Timeout evita esperas infinitas, retry maneja fallos transitorios, circuit breaker protege servicios saturados y fallback mantiene la funcionalidad degradada. Combinados, permiten que el sistema siga funcionando incluso cuando partes de él fallan.