07 Tester, provoquer une panne et réparer
Notes pour le formateur du module 07 — durée, trame d’animation, problèmes courants et procédures de réinitialisation.
Document d’accompagnement du formateur pour la leçon participant 07 Tester, provoquer une panne et réparer.
Durée
Environ 20 minutes. Il s’agit de l’exercice d’incident ; préservez assez de temps pour aboutir au diagnostic avant le module 08 et la conclusion. L’exercice porte sur la panne 01 (diagnostic en 5 à 15 min). Les pannes 02 (5 à 10 min) et 03 (10 à 20 min, uniquement avec le jeu complet d’environ 30 millions de lignes) restent disponibles comme incidents facultatifs si le groupe avance vite. Fixez une heure limite ferme pendant la répétition afin de ne pas écourter la conclusion.
Trame d’animation
- C’est l’aboutissement : utilisez tout ce qui a été construit pour traiter un incident réel.
- Décrivez le symptôme, pas sa cause, et laissez les agents converger à partir de la télémétrie.
- Vous pouvez projeter côte à côte les diagnostics de plusieurs participants.
- L’exercice porte sur la panne 01. Si le temps permet une manche supplémentaire, ajoutez la panne 02 (et la panne 03 uniquement si le jeu de données complet a été amorcé) ; entre les pannes, utilisez la réinitialisation commune par mise à l’abri et changement de branche ci-dessous.
Corrigé
Ne partagez pas cette section avec les participants : elle figure uniquement dans le guide
et n’est jamais versionnée dans le dépôt de l’application. Chaque panne correspond à une
petite modification isolée dans sa propre branche créée depuis build-workshop-v1 ; la
correction consiste à l’annuler. Exécutez une seule panne à la fois. L’incident de l’exercice
est la panne 01 (5 à 15 min, avec un piège : le back-end est innocent). Pour un groupe qui
avance vite, les compléments facultatifs sont la panne 02 (5 à 10 min, mise en jambes dont
la trace révèle tout) et la panne 03, uniquement si le jeu de données complet est disponible
(10 à 20 min, raisonnement ClickHouse plus approfondi).
Réinitialisation commune à toutes les pannes (la mise à l’abri conserve les modifications du participant et évite qu’elles ne bloquent le changement de branche) :
git stash push --include-untracked -m "module-07-fix"
git switch build-workshop-v1
docker compose --env-file .env.workshop \
-f docker-compose.workshop.yml -f docker-compose.otel.yml up -d --buildPanne 02 — erreur 500 des statistiques de zone (fault/02-zone-stats-500)
Message de commit visible par les participants : "backend: align zone stats column names with API params"
(modifie uniquement backend/app/query_builders.py zone_stats_sql). La requête regroupe
par pickup_zone_id, une colonne absente de taxi_trips, au lieu de
pickup_location_id.
- Symptôme : la carte choroplèthe est vide (les polygones s’affichent, mais toutes les
zones restent dans la classe la plus claire) et la légende "Query Nms" manque ; rafales
d’erreurs 500 sur
GET /api/metrics/zone_stats(React Query effectue trois nouvelles tentatives). Toutes les autres cartes fonctionnent. - Emplacement du signal : un span ClickStack
clickhouse.queryavecerror=True,error.category="query_failed"et undb.statementcontenantpickup_zone_id AS zone_id; l’exception enregistrée est le Code 47UNKNOWN_IDENTIFIERde ClickHouse. Journal ERROR correspondant : "ClickHouse query failed (category=query_failed, elapsed_ms=...): ... | sql=...". - Parcours de diagnostic : repérer le span 500 -> lire
db.statement->UNKNOWN_IDENTIFIERsurpickup_zone_id->DESCRIBE taxi_trips(ou comparer les générateurs de requêtes voisins) -> la véritable colonne estpickup_location_id. - Correction : remettre
pickup_location_idà la place depickup_zone_iddanszone_stats_sql(ou appliquergit revertau commit de la panne), puis reconstruire le back-end. - Réinitialisation : utilisez la procédure commune ci-dessus.
Panne 03 — tableau de bord lent (fault/03-slow-dashboard)
Message de commit : "backend: window the trend series by bucket in HAVING" (modifie
timeseries_sql). Il déplace le prédicat de fenêtre temporelle de WHERE vers une clause
HAVING portant sur l’alias du compartiment ; WHERE devient donc 1. ClickHouse ne peut
plus élaguer selon la clé primaire (car_type, pickup_datetime) et analyse entièrement
taxi_trips avec deux états quantileTDigest, dépassant la limite de 5 s définie par
max_execution_time.
- Symptôme : seule la carte de tendance "What's happening now?" échoue : elle charge, puis affiche "504 Query timed out...". Toutes les cartes voisines restent rapides.
- Emplacement du signal : un span
clickhouse.querydonterror.category="timeout", avec undb.elapsed_msproche de 5000 et undb.statementaffichantWHERE 1 ... GROUP BY ts HAVING ts >= ...; l’exception est le Code 159TIMEOUT_EXCEEDED. Le contraste avec les spans 200 rapides des autres points de terminaison est révélateur. - Parcours de diagnostic : une seule carte lente parmi des voisines rapides -> ouvrir
le span arrivé à expiration -> lire le SQL -> le filtre temporel se trouve dans
HAVINGetWHEREne contient aucune plage surpickup_datetime-> anti-patron d’élagage par clé primaire et de descente de prédicat (les requêtes voisines placent la fenêtre dansWHERE). - Correction : rétablir la fenêtre temporelle dans
WHERE(annuler le commit de la panne), puis reconstruire le back-end. - Réinitialisation : utilisez la procédure commune ci-dessus.
- Mise en garde pour le formateur : la gravité varie selon le volume de données et la
taille du service. Avec le jeu complet amorcé (environ 30 millions de lignes) sur un service
de niveau atelier, qui peut être en cours de réveil, l’expiration à 5 s se produit de façon
fiable ; avec le petit échantillon, la requête est simplement plus lente et ne renvoie pas
nécessairement une erreur 504. Les valeurs
send_receive_timeoutdu client etmax_execution_timedu serveur sont toutes deux de 5 s : une compétition entre les délais d’expiration de socket peut donc parfois produire une erreur 500 au lieu de l’erreur 504 attendue. Cette particularité vient des paramètres de référence, pas de la panne.
Panne 01 — la carte ne charge pas (fault/01-map-not-loading)
Message de commit : "frontend: serve map geojson from /static asset path" (modifie le chemin
de récupération dans frontend/src/ui/ZoneMap.tsx). Le code demande
/static/taxi_zones.geojson, qui n’existe pas. Nuance essentielle : le repli SPA de nginx
(try_files ... /index.html) renvoie le document HTML avec le statut HTTP 200 au lieu
d’une erreur 404 ; r.ok réussit donc, puis r.json() lève une SyntaxError telle que
Unexpected token '<'.
- Symptôme : la carte est inopérante ; les tuiles de fond s’affichent, mais aucun polygone de New York ni aucune carte choroplèthe n’apparaît, et un message d’erreur rouge s’affiche dans la carte. Tout le reste fonctionne.
- Emplacement du signal : rien dans ClickStack côté back-end, car la demande de la
ressource n’atteint jamais FastAPI. La console du navigateur affiche l’erreur d’analyse
JSON qui cite
/static/taxi_zones.geojson; l’onglet Network montre que cette requête renvoietext/htmlavec le statut 200 ; le journal d’accès de nginx montre le repli. - Parcours de diagnostic : les traces du back-end sont saines -> passer à la console
du navigateur ou à l’onglet Network -> une requête
.geojsonrenvoie dutext/htmlavec le statut 200 -> reconnaître le piège du repli SPA -> le fichier se trouve en réalité à la racine web, sous/taxi_zones.geojson. - Correction : annuler la modification du chemin de récupération (deux lignes) ou
appliquer
git revertau commit de la panne ; reconstruire le front-end. - Réinitialisation : utilisez la procédure commune ci-dessus.
- Point pédagogique : toutes les défaillances n’apparaissent pas dans les traces du back-end, et une erreur 404 peut se faire passer pour une réponse 200 derrière un repli SPA. Il faut donc lire le corps réel de la réponse et son type de contenu, pas seulement son code d’état.
- Remarque (SDK du navigateur) : lorsque le SDK navigateur HyperDX est activé
(surcouche du module 05), le front-end fait désormais apparaître cette erreur dans
ClickStack lui-même : l’échec d’analyse de
ZoneMapest capturé dans un spanconsole.errorsousServiceName=nyc-taxi-frontend, associé au span de ressource de la réponse 200text/html. L’agent peut donc localiser l’erreur à partir de la télémétrie sans quitter ClickStack ; la console du navigateur ou l’onglet Network constitue une solution de repli, et non le seul parcours possible.
Exemple de réponse de l’agent (panne 01, via le MCP ClickStack) :

Diagnostic attendu : l’agent relie l’échec à la requête geojson qui renvoie le statut 200
avec le type text/html (repli SPA), à l’erreur JSON.parse qui en résulte et est capturée
sous nyc-taxi-frontend, puis recommande de livrer la ressource sous /static/ ou de
protéger l’appel .json().
Problèmes courants
- La télémétrie de référence est insuffisante pour que l’agent localise l’erreur ; le module 05 doit avoir été exécuté tôt.
- Les participants passent à la correction avant d’avoir confirmé la cause racine à partir des preuves.
- Le symptôme de la panne n’est pas encore visible, car la pile vient de démarrer, ou, pour la panne 03, parce que trop peu de données historiques ont été chargées. Mesure effectuée pendant la répétition : avec l’amorçage par défaut sur un seul mois (environ 3,17 millions de lignes), la panne WHERE/HAVING n’est pas observable : l’analyse complète s’exécute en environ 300 à 560 ms, sans différence par rapport à la référence. Amorcez plusieurs mois avant de présenter la panne 03, ou restez sur la panne 01, qui se reproduit immédiatement, et traitez la panne 03 comme une illustration à grande échelle.
- La panne 01 ne produit aucune trace côté back-end et la mauvaise requête renvoie le statut
200 avec
text/htmlpar le repli SPA, au lieu d’une erreur 404 ; les participants risquent de chercher indéfiniment dans les traces. Orientez-les vers la console du navigateur ou l’onglet Network, où apparaissent l’erreur d’analyse JSON et la réponsetext/html. - Oubli de
--buildaprès le passage sur une branche de panne : l’ancienne image continue donc de s’exécuter.
Procédures de réinitialisation
- La procédure commune de mise à l’abri et de changement de branche rétablit l’application complète sans supprimer la tentative de correction du participant.
- Réinjectez une panne en activant l’une des branches
fault/01-map-not-loading/fault/02-zone-stats-500/fault/03-slow-dashboard, puis reconstruisez avec les fichiers Compose de l’atelier et d’OTel. - Les symptômes, signaux et corrections propres à chaque panne figurent dans le corrigé ci-dessus. Conservez ce corrigé uniquement dans le guide ; il ne doit jamais être versionné dans le dépôt de l’application.