Erreur et résilience
Les patterns de résilience en action : circuit breaker, retry, timeout et fallback appliqués à des flux réels entre services.
Pourquoi la résilience fait partie du flux
Dans un système distribué, les pannes ne sont pas des exceptions : elles font partie du fonctionnement normal. Des services qui tombent, des réseaux qui échouent, des bases de données qui saturent. La question n’est pas de savoir si le système va tomber en panne, mais comment il se comporte lorsqu’il tombe en panne.
Les patterns de résilience ne sont pas des concepts théoriques isolés. Ils s’appliquent aux flux réels entre services pour que le système continue de fonctionner de manière dégradée plutôt que de s’effondrer complètement.
Les quatre patterns principaux
Timeout
Il définit un temps d’attente maximal pour une opération. Si aucune réponse n’arrive dans ce délai, la requête est annulée et l’erreur est traitée.
Retry
Il réessaie une opération qui a échoué, en partant du principe que la panne peut être passagère (un pic de charge, une erreur réseau momentanée).
Circuit Breaker
Il détecte lorsqu’un service échoue de façon répétée et cesse de lui envoyer des requêtes pendant un certain temps, lui permettant ainsi de se rétablir.
Fallback
Il fournit une réponse alternative lorsque l’opération principale échoue.
Le timeout en action
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
Comment configurer les timeouts
Chaque appel entre services doit avoir un timeout explicite. Sans timeout, une requête peut rester suspendue indéfiniment, en consommant des ressources.
Règles pratiques :
- Le timeout doit être supérieur au temps de réponse normal du service
- Mais inférieur au temps que l’utilisateur est prêt à attendre
- Différentes opérations peuvent avoir différents timeouts
| Opération | Timeout suggéré |
|---|---|
| Consultation du catalogue | 2–3 secondes |
| Création de commande | 5 secondes |
| Paiement externe | 10–30 secondes |
| Envoi de notification | 5 secondes |
Que faire en cas de timeout
- Enregistrer le timeout dans les logs avec son contexte (service, opération, durée)
- Renvoyer une réponse dégradée si possible (données en cache, valeurs par défaut)
- Ne pas supposer que l’opération a échoué : elle a peut-être abouti mais la réponse s’est perdue
Le retry en action
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
Stratégies de retry
Retry immédiat : réessayer sans attendre. Utile uniquement pour des erreurs très passagères.
Retry avec backoff fixe : attendre un temps fixe entre les tentatives (1s, 1s, 1s).
Retry avec backoff exponentiel : doubler le temps d’attente à chaque tentative (1s, 2s, 4s, 8s). C’est la stratégie la plus recommandée car elle réduit la pression sur le service qui est en train de tomber en panne.
Retry avec jitter : ajouter une composante aléatoire au backoff pour éviter que plusieurs clients ne réessaient au même moment (thundering herd).
Quand réessayer et quand ne pas le faire
Réessayer :
- Erreurs 503 (Service Unavailable)
- Erreurs 429 (Too Many Requests)
- Timeouts réseau
- Erreurs de connexion
Ne pas réessayer :
- Erreurs 400 (Bad Request) — la requête est invalide
- Erreurs 401/403 — problème d’authentification/autorisation
- Erreurs 404 — la ressource n’existe pas
- Erreurs 409 (Conflict) — conflit d’état
Idempotence et retry
Si l’on réessaie une opération d’écriture (créer une commande, traiter un paiement), le service récepteur doit être idempotent. Sinon, le retry risque de créer des doublons.
Le circuit breaker en action
stateDiagram-v2
[*] --> Closed
Closed --> Open: N fallos consecutivos
Open --> HalfOpen: Después del timeout
HalfOpen --> Closed: Request exitoso
HalfOpen --> Open: Request falla
Les trois états
Closed (fermé) : le circuit fonctionne normalement. Les requêtes passent jusqu’au service. Si les pannes s’accumulent, le circuit s’ouvre.
Open (ouvert) : le circuit est coupé. Les requêtes n’atteignent pas le service ; une erreur ou un fallback est renvoyé immédiatement. Cela protège le service en difficulté et évite que l’appelant ne gaspille des ressources à attendre.
Half-Open (semi-ouvert) : après un certain temps, le circuit laisse passer une requête de test. Si elle réussit, le circuit se ferme. Si elle échoue, il se rouvre.
Exemple concret
Le BFF appelle le microservice de catalogue. Si 5 des 10 dernières requêtes ont échoué :
- Le circuit breaker s’ouvre
- Pendant 30 secondes, toutes les requêtes vers le catalogue reçoivent un fallback (données en cache)
- Après 30 secondes, une requête de test est autorisée
- Si elle aboutit, le circuit se ferme et le trafic normal reprend
Configuration typique
| Paramètre | Valeur typique |
|---|---|
| Seuil de pannes | 50 % sur une fenêtre de 10 requêtes |
| Temps à l’état Open | 30 secondes |
| Requêtes de test en Half-Open | 1 |
Le fallback en action
Lorsqu’une opération échoue (par timeout, circuit breaker ouvert ou erreur), le fallback fournit une alternative :
| Service | Fallback |
|---|---|
| Catalogue | Renvoyer les données en cache (même obsolètes) |
| Recommandations | Renvoyer des produits populaires génériques |
| Profil utilisateur | Renvoyer les données de base du token JWT |
| Notifications | Mettre en file d’attente pour un envoi ultérieur |
| Analytics | Écarter l’événement (non critique) |
Niveaux de dégradation
Toutes les pannes n’exigent pas la même réponse. On peut définir des niveaux :
- Dégradation minimale : données légèrement obsolètes (cache de 5 minutes)
- Dégradation modérée : fonctionnalité réduite (catalogue sans filtres avancés)
- Dégradation sévère : fonctionnalité minimale (lecture seule, sans opérations d’écriture)
- Indisponibilité : page de maintenance
Combiner les patterns
En pratique, ces patterns se combinent :
BFF → [Timeout: 3s] → [Retry: 3 intentos con backoff] → [Circuit Breaker] → ms-catalog
↓ (si abierto)
[Fallback: caché]
- Le BFF effectue une requête avec un timeout de 3 secondes
- En cas de timeout, il réessaie jusqu’à 3 fois avec un backoff exponentiel
- Si les tentatives échouent, le circuit breaker s’ouvre
- Avec le circuit ouvert, les requêtes suivantes reçoivent directement le fallback
Observabilité de la résilience
Chaque pattern doit générer des métriques et des logs :
- Timeouts : combien, sur quel service, quelle durée
- Retries : combien de tentatives, taux de succès à chaque tentative
- Circuit Breaker : transitions d’état, temps passé dans chaque état
- Fallbacks : combien de fois activé, quel type de fallback
Ces métriques permettent de détecter les problèmes avant qu’ils ne se transforment en incidents graves.
Résumé
La résilience n’est pas un composant que l’on ajoute à la fin. C’est une propriété du système que l’on conçoit dans chaque flux. Le timeout évite les attentes infinies, le retry gère les pannes passagères, le circuit breaker protège les services saturés et le fallback maintient une fonctionnalité dégradée. Combinés, ils permettent au système de continuer à fonctionner même lorsque certaines de ses parties tombent en panne.