Timeout и circuit control
1) Pourquoi est-ce nécessaire
Les systèmes ne tombent pas d'une seule défaillance « fatale », mais d'une accumulation de retards et de rétrogrades « vagues ». Les délais limitent les temps d'attente et libèrent des ressources, et le contrôle de circuit (breaker + shedding + concurrence adaptative) empêche la dégradation de se propager le long de la chaîne de dépendance. L'objectif est de maintenir p95/p99 dans les limites des cibles et de maintenir l'accessibilité en cas de défaillance partielle.
2) Définitions de base
2. 1 Types de temporisation (L7/L4)
Connect timeout - Établissement d'une connexion TCP/TLS.
TLS/Handshake timeout est une poignée de main TLS/HTTP2 preface.
Write timeout - Envoie une requête (y compris le corps).
Read timeout est l'attente du premier octet de réponse et/ou du corps entier.
Idle/Keep-Alive timeout est une connexion inactive.
Overall deadline est un délai « dur » pour l'ensemble de la demande (end-to-end).
2. 2 Budget du Taimaut (deadline budget)
Nous distinguons la cible 'deadline _ total' et divisons par étapes :- `ingress (gateway) + authZ + app + DB/cache + outbound PSP`.
- passerelle : 30 ms,
- annexe : 120 ms,
- OBD : 120 ms,
- PSP : 100 ms,
- réserve : 30 ms.
2. 3 Propagation et annulation
« deadline »/« timeout » doit être transmis en bas de la chaîne (contexte, en-têtes, gRPC Deadline). À l'expiration, annuler les opérations en arrière-plan (abort/ctx cancel), nettoyer les verrous/sémaphores.
3) Stratégies d'installation des timeouts
1. Top-Down : Basé sur SLO et p95 - définir la fin-à-fin deadline, puis décomposer en sous-temps.
2. Identifiez les chemins « chers » (téléchargement de fichiers, rapports, PSP externes) - séparés plus longs mais limités.
- idempotent (GET/répétitions de statut) - bref, plus agressif ;
- write/money - un peu plus long, mais avec une répétition et une idempotence.
4. Graduer par plans/tenants (l'entreprise peut avoir plus de timeout, mais moins de parallélisme).
4) Circuit breaker : modèles et paramètres
4. 1 Stratégies de déclenchement
Taux d'échec : Proportion d'erreurs ≥ X % sur la fenêtre N requêtes/heure.
Failures consultatives : M échecs consécutifs.
Slow-call rate : la proportion d'appels est plus longue que le seuil T.
Error classes : Les délais/5xx/connection-reset → « fatal », 4xx - ne sont pas pris en compte.
4. 2 états
Closed - rate tout, accumule les statistiques.
Open - panne instantanée (économise les ressources, ne pressent pas la dépendance).
Half-open - petits « échantillons » (N demandes) pour la « vérification de l'eau ».
4. 3 suppléments utiles
Bulkhead (cadres) : un pool de flux/connexions par dépendance pour que l'on ne « suce » pas tout.
Concurrence adaptative : limitation automatique du parallélisme (algorithmes AIMD/Vegas) par latence observée.
Load Shedding : défaillance/dégradation précoce en cas de pénurie de ressources locales (files d'attente, CPU, pauses GC).
5) Interaction : Délais, retraits, limites
D'abord la deadline, puis les retraits : chaque répétition doit être placée dans la deadline commune.
Backoff + jitter pour les répétitions ; respecter « Retry-After » et retry-budget.
Limite de taux : avec un breaker ouvert - abaissez les limites pour ne pas amplifier la tempête.
Idempotency : obligatoire sur les opérations de write (pour éviter les prises dans les temps « muets »).
Où rétracter : de préférence au bord (client/passerelle) plutôt qu'au fond.
6) Valeurs cibles pratiques (repères)
API de lecture publique : end-to-end '200-500 ms', read timeout '100-300 ms'.
Write critique (paiements) : '300-800 ms'e2e ; PSP externe ≤ '250-400 ms'.
Connect/TLS : "50-150 ms' (plus - problème de réseau/résolving).
Idle : « 30-90 s » (clients mobiles - bref pour économiser la batterie).
Ajustez les valeurs selon p95/p99 et les régions.
7) Configis et exemples
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 (périmètre)
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 (client, Go-pseudo)
go ctx, cancel:= context.WithTimeout(context.Background(), 350time.Millisecond)
defer cancel()
resp, err:= client.Pay(ctx, req) // Deadline передается вниз
7. 4 client 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) Observabilité et alerties
8. 1 Métriques
`http_client_requests{endpoint, status}`, `client_latency_bucket`
8. 2 Tracs
Spans : ingress → handler → DB/Redis → externe.
Attributs : 'timeout _ ms _ target', 'circuit _ state', 'queue _ time _ ms'.
Exemplars : lier les piques p99 à des traces-id spécifiques.
8. 3 Alert
'p99 _ latency {critical}> cibles X minutes consécutives.
'timeout _ rate {dependency}' saut> Y %.
Transitions fréquentes vers 'open '/' flapping' breaker.
Croissance 'shed _ requests _ total' à CPU/GC élevé.
9) Adaptive Concurrency & Load Shedding
9. 1 Idée
L'automatisation réduit le parallélisme lors de la croissance des queues de latence :- AIMD : augmenter lentement, réduire brusquement.
- Vegas-like : garder la file d'attente cible (queue time).
- Token-based : chaque demande « brûle » un token ; les jetons sont émis à la vitesse mesurée.
9. 2 Réalisation
Sémaphores locaux par route ; le but est de maintenir 'queue _ time' en dessous du seuil.
Un « fusible » global (limite RPS/concurrence) sur la passerelle.
En cas de pénurie de CPU/connexions, une défaillance précoce avant l'exécution de la logique (429/503 avec « Retry-After »).
10) Tests et scénarios de chaos
Injection de latitude : ajouter artificiellement 50-300 ms par dépendance.
Packet loss/dup/drop (tc/tbf, Toxiproxy).
Knob turning : réduire les pools de connexion, augmenter la charge de travail jusqu'à la saturation.
Kill/Degrade une zone/shard (indisponibilité partielle).
Contrôles : si la tempête rétrospective n'échoue pas ; le breaker s'ouvre de manière prévisible ; N'est-ce pas que la file augmente.
11) Anti-modèles
Un seul « read timeout » global sans détail connect/TLS/per-etage.
L'absence de dédain commun → de rétractation va au-delà du SLO.
Retrai sans jitter et sans retry-budget.
Connexions « éternelles » sans idle-timout → fuite de descripteurs.
Breaker considère le 4xx comme une erreur fatale.
Pas d'annulation/abort → les travaux de fond continuent après le délai d'attente du client.
Délais trop longs pour les réseaux mobiles/instables.
12) Spécificité de l'iGaming/Finance
Write critique (dépôts/conclusions) : Une courte répétition avec Idempotency-Key, puis "202 Accepted' + polling au lieu d'attentes infinies.
PSP/services bancaires : politiques séparées par fournisseur/région (certaines plus lentes).
Paiements responsables et limites : en cas de blocage/revue - rapide '423/409', ne pas étirer les opérations « suspendues ».
Reporting/agrégation : Exécute asynchrone (batch + ressource d'état).
13) Chèque-liste prod-prêt
- Défini comme étant la fin de la fin de la vie sur les itinéraires critiques (GET/POST).
- Le budget est décomposé en étapes ; propagation debline incluse.
- Configurations connect/TLS/read/write/idle timeouts sur la passerelle et les clients.
- Circuit breaker avec des seuils de taux d'échec et de slow-call ; logique half-open correcte.
- Bulkheads sur les dépendances ; limites du parallélisme par route.
- Load shedding avant d'exécuter la logique d'entreprise en cas de surcharge.
- Intégration avec les retraits : backoff + gitter, retry-budget, respect de « Retry-After ».
- Idempotence write, 'Idempotency-Key' et outbox pour les événements.
- Métriques : timeout/slow-call/état breaker/queue time/concurrence.
- Tests de chaos : injection de retards/pertes/défaillances, dégradation des zones.
- Documentation pour les clients : exemples de timing, codes de réponse, conseils de répétition.
14) TL; DR
Donnez à chaque demande une deadline rigide, répartissez-la en étapes et distribuez-la en bas de la chaîne. Gérer les pannes via circuit breaker + bulkheads + concurrence adaptive + load shedding. Répétitions - seulement dans le cadre de la date limite, avec un gitter et un budget ; write - idempotent seulement. Mesurer timeout/slow-call, état breaker et 'queue _ time', courir régulièrement des tests de chaos.