Simulateur de pannes
Simulez des pannes en production — services hors service, timeouts, erreurs réseau — et exercez-vous aux stratégies de résilience.
Objectif
Dans ce laboratoire, vous allez simuler des pannes courantes des systèmes distribués et vous exercer aux stratégies de résilience qui les atténuent. Chaque scénario présente une panne, vous demande d’en identifier l’impact et de proposer une solution.
En production, les pannes ne préviennent pas. Ici, vous pouvez vous exercer sans conséquences.
Scénario 1 : Timeout en cascade
La panne
Le service A appelle le service B, qui appelle le service C. Le service C commence à répondre lentement (3 secondes au lieu de 200 ms). Il n’est pas hors service — juste lent.
[Service A] ──timeout 5s──► [Service B] ──timeout 5s──► [Service C : LENT 3s]
Que se passe-t-il ?
- C répond en 3s → B attend 3s + son propre traitement (500 ms) = 3,5s
- A attend 3,5s pour B → les utilisateurs subissent une latence de 4s et plus
- Les threads de A et B sont bloqués en attente → le pool de connexions s’épuise
- Les nouvelles requêtes vers A commencent à échouer par timeout → cascade de pannes
Exercice : concevez l’atténuation
Répondez à ces questions :
-
Quel timeout mettriez-vous sur chaque service ?
- Indice : les timeouts doivent être décroissants dans la chaîne (A > B > C)
-
Où placeriez-vous un circuit breaker ?
- Indice : au point où la panne prend naissance (B → C)
-
Quelle réponse B renverrait-il lorsque le circuit breaker est ouvert ?
- Indice : réponse dégradée, cache, ou échec rapide
-
Comment détecteriez-vous ce problème avant qu’il n’affecte les utilisateurs ?
- Indice : métriques de latence p99, alertes par percentile
Solution recommandée
Voir la solution
[Service A] [Service B] [Service C]
timeout: 2s timeout: 1s ---
circuit breaker: ON
fallback: cache local
bulkhead: pool séparé
- Timeouts décroissants : A=2s, B=1s. Si C met plus de 1s, B coupe immédiatement.
- Circuit breaker sur B→C : Après 5 échecs consécutifs, B cesse d’appeler C pendant 30 secondes.
- Fallback : Lorsque le circuit breaker est ouvert, B répond avec des données du cache (même si elles sont périmées).
- Bulkhead : B utilise un pool de connexions séparé pour C, ainsi les pannes de C n’épuisent pas les connexions destinées aux autres services.
Scénario 2 : Base de données saturée
La panne
C’est le Black Friday. Le trafic a été multiplié par 5. La base de données PostgreSQL du service de catalogue est à 95 % de CPU. Les requêtes qui prennent normalement 50 ms en prennent désormais 2 secondes. Le connection pool est saturé.
Symptômes observés
- Latence de l’API : p50 = 500 ms (normal : 100 ms), p99 = 8s (normal : 300 ms)
- Connection pool : 100/100 connexions utilisées
- CPU de PostgreSQL : 95 %
- Erreurs 503 sur le frontend : 15 % des requêtes
Exercice : réponse immédiate
Vous êtes de garde. Que faites-vous dans les 30 prochaines minutes ?
-
Action immédiate (5 premières minutes) :
- Que pouvez-vous faire sans modifier le code ?
-
Atténuation à court terme (25 minutes suivantes) :
- Quels changements de configuration aideraient ?
-
Prévention future :
- Quels changements d’architecture éviteraient ce problème ?
Solution recommandée
Voir la solution
Immédiat (5 min) :
- Activer le mode lecture seule pour les opérations non critiques
- Augmenter la taille du connection pool si le serveur le permet
- Activer la read replica si elle existe mais n’est pas utilisée
Court terme (25 min) :
- Activer un cache Redis pour les requêtes de catalogue les plus fréquentes
- Réduire le timeout des requêtes lentes pour libérer des connexions
- Faire évoluer verticalement l’instance PostgreSQL (plus de CPU/RAM)
Prévention future :
- Mettre en place un cache de lecture (Redis) comme couche permanente
- Ajouter des read replicas pour répartir la charge de lecture
- Mettre en place du rate limiting pour protéger la base de données
- Configurer l’auto-scaling basé sur les métriques de CPU/connexions
- Analyser et optimiser les requêtes les plus coûteuses (EXPLAIN ANALYZE)
Scénario 3 : Message perdu dans la file
La panne
Le service de commandes publie un événement OrderCreated dans RabbitMQ. Le service de paiements consomme cet événement et traite le paiement. Mais un jour, vous découvrez que 50 commandes des dernières 24 heures n’ont jamais été traitées — l’événement a été publié mais le service de paiements ne l’a jamais reçu.
Investigation
Que s’est-il passé ? Analysez chaque possibilité :
-
Le message n’est jamais arrivé à RabbitMQ
- Le publisher utilise-t-il des confirmations (publisher confirms) ?
- Que se passe-t-il si RabbitMQ ne répond pas au publish ?
-
Le message est arrivé mais s’est perdu dans RabbitMQ
- La file est-elle durable ?
- Les messages sont-ils persistants ?
- RabbitMQ a-t-il redémarré et perdu des messages en mémoire ?
-
Le consumer a reçu le message mais a échoué à le traiter
- Le consumer fait-il l’ACK avant ou après le traitement ?
- Y a-t-il une dead letter queue pour les messages en échec ?
-
Le consumer était hors service à l’arrivée du message
- La file conserve-t-elle les messages lorsqu’il n’y a pas de consumers ?
- Y a-t-il un TTL qui fait expirer les messages non consommés ?
Exercice : concevez un système de messagerie résilient
[Service de Commandes]
│
▼ (publisher confirms)
[RabbitMQ]
│ (file durable, messages persistants)
▼
[Service de Paiements]
│ (ACK après traitement)
│
├── succès → ACK
└── échec → NACK → Dead Letter Queue → alerte
Checklist de résilience pour la messagerie
Voir la checklist
- Publisher confirms : Le publisher attend la confirmation de RabbitMQ avant de considérer le message comme envoyé
- File durable : La file survit aux redémarrages de RabbitMQ
- Messages persistants : Les messages sont écrits sur disque, pas seulement en mémoire
- ACK manuel : Le consumer fait l’ACK uniquement après un traitement réussi
- Dead letter queue : Les messages qui échouent N fois vont dans une file séparée pour investigation
- Idempotence : Le consumer peut traiter le même message deux fois sans effets dupliqués
- Monitoring : Alertes lorsque la file grandit plus que la normale ou qu’il y a des messages dans la DLQ
- Retry avec backoff : Les messages en échec sont réessayés avec des délais croissants
Scénario 4 : Split brain dans le cache
La panne
Vous avez un cluster Redis à 3 nœuds (1 master + 2 replicas). Pendant une partition réseau, une replica est promue master. Vous avez désormais 2 masters qui acceptent des écritures avec des données différentes.
Impact
- Certains utilisateurs voient l’ancien prix d’un produit (100 $)
- D’autres utilisateurs voient le nouveau prix (80 $)
- Lorsque la partition est résolue, quelles données l’emportent ?
Exercice
- Comment détecteriez-vous un split brain en production ?
- Quelle stratégie de résolution utiliseriez-vous ? (last-write-wins, merge, manuel)
- Comment préviendriez-vous ce scénario ?
Voir la solution
Détection :
- Surveiller le nombre de masters dans le cluster (alerte si > 1)
- Sentinel ou Redis Cluster détectent et résolvent automatiquement
Résolution :
- Redis utilise le last-write-wins par défaut à la reconnexion
- Les données du master « perdant » sont écartées
- Pour les données critiques (prix, inventaire), la source de vérité doit être la base de données, pas le cache
Prévention :
- Configurer
min-replicas-to-writepour que le master rejette les écritures s’il n’a pas de quorum - Utiliser Redis comme cache (et non comme source de vérité) — pouvoir toujours reconstruire depuis la DB
- Mettre en place des health checks qui détectent les partitions réseau
Réflexion
Après avoir terminé les scénarios :
- Combien de ces pannes avez-vous rencontrées en production ?
- Votre système actuel dispose-t-il de protections contre ces scénarios ?
- Quelle panne vous semble la plus probable dans votre contexte ?
- Quelle action concrète pouvez-vous entreprendre dès demain pour améliorer la résilience de votre système ?
Les systèmes ne tombent pas en panne de façons nouvelles — ils tombent en panne des mêmes façons, encore et encore. Connaître les patterns de panne est la meilleure des préparations.