Timeout и circuit control
1) Por qué es necesario
Los sistemas no caen de una sola falla «fatal», sino de la acumulación de retrasos y retratos de «onda». Los temporizadores limitan el tiempo de espera y liberan recursos, y el control de circuitos (breaker + shedding + competencia adaptativa) no permite que la degradación se extienda por la cadena de dependencias. El objetivo es mantener p95/p99 dentro de los límites de destino y mantener la disponibilidad en caso de fallos parciales.
2) Definiciones básicas
2. 1 Tipos de temporizadores (L7/L4)
Connect timeout - Establecer una conexión TCP/TLS.
TLS/Handshake timeout - apretones de mano TLS/HTTP2 preface.
Tiempo escrito: enviar una solicitud (incluido el cuerpo).
Tiempo de lectura: espera el primer byte de respuesta y/o cuerpo completo.
Idle/Keep-Alive timeout es una conexión inactiva.
Overall deadline es un término «duro» para toda la solicitud (fin a fin).
2. 2 Presupuesto de tiempo de espera (budget deadline)
Resaltamos el objetivo 'deadline _ total' y lo dividimos por etapas:- `ingress (gateway) + authZ + app + DB/cache + outbound PSP`.
- Puerta de enlace: 30 ms,
- aplicación: 120 ms,
- DB: 120 ms,
- PSP: 100 ms,
- stock: 30 ms.
2. 3 Propagación y cancelación
'deadline '/' timeout' debe transmitirse por la cadena (contexto, encabezados, gRPC Deadline). Cuando caduca, cancela las operaciones de fondo (abort/ctx cancel), borra los bloqueos/semáforos.
3) Estrategias de instalación de temporizadores
1. Arriba-Abajo: Basado en SLO y p95 - Establecer el fin a fin deadline, a continuación, dividir en bajo-tiempo.
2. Identificar rutas «caras» (descarga de archivos, informes, PSPs externos) - individuales más largas pero limitadas.
- idempotent (GET/repeticiones de estado) - más corto, más agresivo;
- escritura/dinero - un poco más largo, pero con una sola repetición e idempotencia.
4. Graduar por planos/tenantes (enterprise puede tener un tiempo más largo, pero menor paralelismo).
4) Circuit breaker: modelos y parámetros
4. 1 Políticas de activación
Failure-rate: porcentaje de errores ≥ X% en la ventana N de consultas/tiempo.
Fallas consecutivas: M contratiempos consecutivos.
Tasa de llamada lenta: la proporción de llamadas es más larga que el umbral T.
Error classes: timeouts/5xx/connection-reset → «fatal», 4xx - no tenemos en cuenta.
4. 2 Estados
Cerrado - salta todo, acumula estadísticas.
Open es una falla instantánea (ahorra recursos, no presiona la dependencia).
Half-open son pequeños «muestreos» (N solicitudes) para «chequear el agua».
4. 3 Complementos útiles
Bulkhead (spangouts): grupo de hilos/conexiones por dependencia para que uno no «chupe» todo.
Concurrencia adaptativa: restricción automática de concurrencia (algoritmos similares a AIMD/Vegas) por latencia observada.
Carga Shedding: Falla/degradación temprana cuando falta un recurso local (colas, CPU, pausas de GC).
5) Interacción: temporizadores, retraídos, límites
Primero deadline, luego retrae: cada repetición debe acomodarse en un deadline común.
Backoff + jitter para repeticiones; respetar 'Retry-After' y retry-budget.
Rate limiting: con breaker abierto: reduce los límites para no aumentar la tormenta.
Idempotencia: obligatoria en operaciones de escritura (para evitar tomas en timautas «mudos»).
Dónde retraer: preferiblemente en el borde (cliente/gateway) en lugar de en el interior.
6) Valores objetivo prácticos (puntos de referencia)
API de lectura pública: end-to-end '200-500 ms', read timeout '100-300 ms'.
Escritura crítica (pagos): '300-800 ms' e2e; PSP externo ≤ '250-400 ms'.
Connect/TLS: '50-150 ms' (más - problema de la red/resolview).
Idle: '30-90 s' (los clientes móviles son más cortos para ahorrar batería).
Ajuste los valores por p95/p99 y regiones.
7) Configuraciones y ejemplos
7. 1 Envoy (cluster + route, pseudo)
yaml clusters:
- name: payments_psp connect_timeout: 100ms type: STRICT_DNS lb_policy: ROUND_ROBIN circuit_breakers:
thresholds:
- priority: DEFAULT max_connections: 2000 max_requests: 2000 max_retries: 50 outlier_detection:
consecutive_5xx: 5 interval: 5s base_ejection_time: 30s max_ejection_percent: 50
routes:
- match: { prefix: "/api/v1/payments" }
route:
cluster: payments_psp timeout: 350ms # per-request deadline idle_timeout: 30s retry_policy:
retry_on: "reset,connect-failure,refused-stream,5xx,gateways"
num_retries: 1 per_try_timeout: 200ms
7. 2 NGINX (perímetro)
nginx proxy_connect_timeout 100ms;
proxy_send_timeout 200ms; # write proxy_read_timeout 300ms; # read (первый байт/все тело)
keepalive_timeout 30s;
send_timeout 15s;
Быстрый отказ при перегрузке limit_conn_zone $binary_remote_addr zone=addr:10m;
limit_conn addr 50;
7. 3 gRPC (cliente, Go-pseudo)
go ctx, cancel:= context.WithTimeout(context.Background(), 350time.Millisecond)
defer cancel()
resp, err:= client.Pay(ctx, req) // Deadline передается вниз
7. 4 cliente HTTP (Go)
go client:= &http.Client{
Timeout: 350 time.Millisecond, // общий дедлайн на запрос
Transport: &http.Transport{
TLSHandshakeTimeout: 100 time.Millisecond,
ResponseHeaderTimeout: 250 time.Millisecond,
IdleConnTimeout: 30 time.Second,
MaxIdleConnsPerHost: 100,
},
}
7. 5 Resilience4j (Java, pseudo)
yaml resilience4j.circuitbreaker.instances.psp:
slidingWindowType: TIME_BASED slidingWindowSize: 60 failureRateThreshold: 50 slowCallDurationThreshold: 200ms slowCallRateThreshold: 30 permittedNumberOfCallsInHalfOpenState: 5 waitDurationInOpenState: 30s
resilience4j.timelimiter.instances.psp:
timeoutDuration: 350ms
8) Observabilidad y alertinges
8. 1 Métricas
`http_client_requests{endpoint, status}`, `client_latency_bucket`
8. 2 Tracks
Durmientes: ingress → handler → DB/Redis → externos.
Atributos: 'timeout _ ms _ target', 'circuit _ state', 'queue _ time _ ms'.
Exemplares: atar picos p99 a un trace-id específico.
8. 3 Alertas
'p99 _ latency {critical}'> objetivos X minutos seguidos.
'timeout _ rate {dependency}' salto> Y%.
Transiciones frecuentes en el breaker 'open '/' flapping'.
Crecimiento de 'shed _ requests _ total' en CPU/GC alto.
9) Adaptive Concurrency & Load Shedding
9. 1 Idea
La automatización reduce el paralelismo en el crecimiento de las colas de latencia:- AIMD: aumentar lentamente, reducir drásticamente.
- Vegas-similar: mantener la cola de destino (queue time).
- Token-based: cada solicitud «quema» un token; los tokens se emiten a la velocidad medida.
9. 2 Implementación
Semáforos locales per-route; objetivo: mantener 'queue _ time' por debajo del umbral.
Un «fusible» global (límite RPS/competencia) en la puerta de enlace.
En caso de escasez de CPU/conexiones, se produce una falla temprana antes de ejecutar la lógica (429/503 con 'Retry-After').
10) Pruebas y escenarios de caos
Latency injection: añadir artificialmente 50-300 ms por dependencia.
Packet loss/dup/drop (tc/tbf, Toxiproxy).
Knob turning: reducir los grupos de conexiones, elevar la carga a saturation.
Kill/Degrade una zona/shard (inaccesibilidad parcial).
Comprobaciones: no «falla» si la tormenta retray; breaker se abre previsiblemente; si la cola no está creciendo.
11) Antipattern
Un «read timeout» global sin detalle connect/TLS/PE.
La falta de una línea de salida → retrabajo común va más allá del SLO.
Retrés sin jitter y sin retry-budget.
Conexiones «eternas» sin idle-timeout → filtraciones de descriptores.
Breaker considera los 4xx como errores fatales.
No hay cancelación/abort → el trabajo de fondo continúa después del tiempo de espera del cliente.
Tiempos de espera demasiado largos para redes móviles/inestables.
12) Especificidad de iGaming/finanzas
Escritura crítica (depósitos/retiros): una repetición corta con Idempotency-Key, luego '202 Acepted' + polling en lugar de expectativas infinitas.
PSP/banca: políticas separadas por proveedor/región (algunas más lentas).
Pagos responsables y límites: cuando bloqueos/rugidos - rápido '423/409', no estirar las operaciones «colgantes».
Informes/agregaciones: ejecutar asincrónicamente (batch + status resource).
13) Lista de comprobación prod
- Definido por fin a fin por ruta crítica (GET/POST).
- Presupuesto distribuido por etapas; habilita la propagación de la línea de salida.
- Configuraciones connect/TLS/read/write/idle de temporizadores en gateway y clientes.
- Circuito breaker con los umbrales failure-rate y slow-call; lógica half-open correcta.
- Bulkheads en las dependencias; límites de paralelismo per-route.
- Carga de carga antes de ejecutar la lógica de negocio en caso de sobrecarga.
- Integración con retrés: backoff + jitter, retry-budget, respeto a 'Retry-After'.
- Idempotencia write, 'Idempotency-Key' y outbox para los eventos.
- Métricas: timeout/slow-call/estado breaker/queue time/competitividad.
- Pruebas de caos: inyección de retrasos/pérdidas/fallas, degradación de zonas.
- Documentación para clientes: ejemplos de temporizaciones, códigos de respuesta, consejos de repetición.
14) TL; DR
Dale a cada solicitud un deadline duro, esparcirlo por las etapas y esparcirlo por la cadena. Gestione las fallas a través de circuit breaker + bulkheads + adaptive concurrency + load shedding. Repeticiones - sólo dentro de la línea de salida, con el jitter y el presupuesto; write - sólo idempotente. Medir timeout/slow-call, estado breaker y 'queue _ time', correr pruebas de caos regularmente.