Timeouts, retries et backoff
En distribué, « lent » devient « mort » : sans timeout, un service lent fige tous ses appelants en cascade.
Prérequis : cours 5.3 (doublons) ; notions HTTP et latence (Réseaux 7.1, 1.3).
Pourquoi
En distribué, « lent » devient « mort » : sans timeout, un service lent fige tous ses appelants en cascade. Retry sans stratégie, lui, transforme une panne en tempête. Trois réglages font 80 % de la résilience.
Concepts
- Timeout : durée max d'attente (connexion + lecture, séparément !). Sans timeout explicite : défauts à 30 s-2 min = threads bloqués = cascade. Règle : timeout < patience de l'appelant, avec marge (si l'API répond en 200 ms, timeout à 2 s, pas 30).
- Retry : réessayer les échecs transitoires (timeout, 503, 429) — jamais les définitifs (400, 404, 422 : réessayer est inutile voire nocif).
- Backoff exponentiel + jitter : attendre 1 s, 2 s, 4 s… + aléa. Sans jitter, 1000 clients réessaient à la même seconde (thundering herd, cf. DevOps 6.2/cache). Avec : la charge s'étale.
Exemple : le trio
timeout: connect 2s, read 5s
retry: 3 tentatives, backoff 1s→2s→4s + jitter, seulement GET/idempotents et 503/429/timeout
budget: abandon après ~10 s cumulées (fail fast > agonie lente)
Cas réel
Paiement qui timeout à 30 s (défaut lib) pendant un pic : 500 threads bloqués, app entière down pour un prestataire lent. Timeout à 3 s + retry ciblée + page d'attente : l'app survit, le prestataire se remet, personne ne remarque.
Pièges
- Retry sur POST non idempotent : double-facturation (cf. 5.3 : clé d'idempotence d'abord).
- Timeout unique global : connexion (réseau) et lecture (traitement) ont des ordres de grandeur différents — deux réglages.
- Retry infini « tant que ça ne passe pas » : boucle qui achève un service convalescent. Compteur + abandon + alerte.
À retenir
Timeouts explicites et courts, retry transitoire + backoff + jitter, budget total, jamais de retry infini.