- Vers 22 h sur jumpcomedy.com, tous les appels HTTP POST de RTK Query ont commencé à échouer, cassant les fonctionnalités du site, et comme tout fonctionnait en local, il était difficile d’en identifier la cause
- Pendant que les plaintes clients s’accumulaient, l’exploitant a dû gérer seul l’incident, sans support production, SRE, ingénieur senior ni manager
- La TypeError liée à
fetchdans le navigateur n’a pas fourni d’indice direct, d’autant que les requêtes GET et DELETE fonctionnaient normalement - Vérifier Sentry, la base de données de production, Cloudflare, une mise à jour de Chrome et même un rollback vers des versions antérieures n’a rien changé, mais le problème a pu être reproduit en ajoutant la clé api_key de PostHog laissée vide en local
- Après avoir supprimé PostHog, les fonctionnalités sont revenues à la normale, et des issues GitHub côté PostHog et Redux Toolkit ont ensuite confirmé qu’il s’agissait d’un impact d’un outil externe
La pression créée par l’incident
- À partir d’environ 22 h, sur jumpcomedy.com, tous les appels HTTP POST basés sur RTK Query se sont mis à échouer, empêchant le bon fonctionnement des principales fonctionnalités
- Des changements avaient bien été déployés récemment, mais rien ne semblait pouvoir expliquer le problème, et comme il ne se reproduisait pas en local, l’investigation était d’autant plus difficile
- Une demande d’aide a été envoyée sur les Discord de NextJS et Vercel, sans réponse, et il n’y avait pas non plus de structure de support production à qui passer la main
- Les e-mails clients continuaient d’arriver
- Demandes indiquant qu’il était impossible de modifier le prix d’un événement
- Demandes indiquant qu’il était impossible de supprimer un code promotionnel
- Sachant que de petites entreprises dépendaient du service, l’exploitant a ressenti de la honte, de la tristesse, un sentiment d’incompétence et le syndrome de l’imposteur
Processus de débogage et identification de la cause
- L’erreur affichée dans le navigateur était une TypeError indiquant que
fetchs’exécutait avec un request object déjà utilisé, mais elle ne révélait pas la cause réelle - Plusieurs
console.log()et points d’arrêt ont été ajoutés pour vérifier les en-têtes, la longueur du jeton API, l’ordre des appels, etc., sans permettre d’identifier clairement la cause - La possibilité d’une mise à jour de Chrome a été suspectée, mais le problème se reproduisait aussi sur Firefox et Edge, ce qui excluait un problème propre à un seul navigateur
- Les échecs continuaient même après un retour à des versions antérieures
- Échec également sur une version d’il y a un mois
- Échec également sur une version d’il y a trois mois
- Échec également sur une version d’il y a un an
- Pour réduire les différences entre l’environnement local et la production, plusieurs pistes ont été vérifiées
- Suppression de Sentry en production : aucun changement
- Connexion du local à la base de données de production : aucun changement
- Désactivation de Cloudflare : aucun changement
- En local, pour réduire les coûts, la clé api_key de PostHog était laissée vide, et le même problème a pu être reproduit dès qu’elle a été ajoutée
- Une fois PostHog supprimé dans le commit suivant, toutes les fonctionnalités ont de nouveau marché normalement
- Le même problème a ensuite été confirmé via des issues GitHub
1 commentaires
Avis de Hacker News
Après avoir travaillé pendant un an comme SRE dans une grande entreprise mondiale, j’ai réussi à sortir du mode « panique » décrit dans l’article.
Du point de vue du business, chaque problème ressemble à une fin du monde, et il est facile de paniquer dans ce genre de situation ; mais en réalité, c’est rarement aussi grave, et même quand ça l’est, on s’en sort généralement sans dommage.
Dans ces situations, l’essentiel est de s’arrêter 5 à 10 minutes avant de toucher quoi que ce soit pour corriger immédiatement, et d’essayer de se faire une idée aussi claire que possible de la situation. La peur empêche le jugement rationnel, et si l’on appuie sur des boutons au hasard en état de panique, on peut encore plus embrouiller le problème. Mon astuce consiste à me passer de l’eau très froide sur le visage et les mains pour couper le circuit de la peur.
Après avoir vécu ça quelques fois, on se rend compte que ça se passe mieux qu’on ne l’imaginait, et on gagne en confiance parce qu’on a déjà géré de mauvaises situations auparavant ; on comprend qu’on peut tenir le coup même sans personne à qui demander de l’aide.
Par exemple, des logiciels achetés qui ne servent à rien à cause d’un échec de staffing ou de configuration, des employés qui perdent des milliers d’heures chaque année à cause d’une mauvaise expérience utilisateur et d’exigences absurdes, des capacités qui ne remplissent aucune fonction et restent en friche, des réunions inutiles qui gaspillent du temps tous les jours, des fonctionnalités qui n’existent que pour satisfaire des exigences d’audit, ou des dirigeants qui continuent de gaspiller l’argent de l’entreprise.
Le downtime ne me paraît pas pire que ces problèmes, mais il attire beaucoup plus d’attention et de panique. Ça me fait penser au contraste entre terrorisme et maladies cardiaques. L’entreprise ne se soucie pas de votre sommeil ni de votre santé mentale, et vous poussera aussi loin que possible. Je ne dis pas que c’est malveillant, mais sur ce point, elle se comporte comme un harceleur : plus vous reculez, plus elle pousse.
L’une de mes devises en programmation est : « pas de magie noire ». Si vous ne comprenez pas pourquoi ça marche, ce n’est pas terminé.
Je vois la réponse aux incidents de la même façon. Si l’on ne peut pas expliquer de manière cohérente pourquoi la proposition de quelqu’un aurait un effet, je pense qu’il ne faut pas l’exécuter. Il arrivera peut-être un jour où il faudra simplement appuyer sur la gâchette, mais avec le recul, je crois que ce cas ne s’est finalement jamais présenté.
C’était assez choquant de voir des cadres dirigeants, d’ordinaire très calmes, commencer à lancer au hasard des idées de correctifs pendant un incident.
Imaginez à quel point les dégâts auraient été plus importants si quelqu’un avait arrêté ce développeur solo en lui disant de « ne pas essayer de populariser fetch ».
Il est juste de dire que la peur entrave le jugement rationnel, et j’ajouterais que la peur est extrêmement contagieuse. Quand les opérationnels voient un leader, un manager ou un collègue paniquer, ils paniquent souvent à leur tour. Heureusement, mon VP restait toujours calme et privilégiait la clarté à l’action.
Je ne suis pas sûr qu’il s’agisse vraiment d’un effondrement mental, et cela pourrait donner une fausse impression aux personnes qui connaissent un véritable effondrement lié au stress technique.
Dans mon cas, ça ne m’est arrivé qu’une seule fois, et c’était une crise d’angoisse. J’ai eu énormément de chance que ma femme soit à côté de moi, m’explique la situation et m’aide à comprendre ce que je traversais. Elle en avait vécu plusieurs, mais pour moi c’était la première et, heureusement, la dernière.
Ce genre de choses peut arriver à quelqu’un, et ce n’est pas une faute en soi. Il est vraiment important d’intérioriser que cela ne veut pas dire qu’on est défectueux ou faible.
Dans mon cas, ce qui a fini par m’arrêter, c’était le Xanax, et comme cela m’a permis de dormir, je pense que cela vaut la peine d’en avoir à portée de main.
Ce que je veux dire, c’est qu’il y a une différence entre des pensées intrusives et un état réellement incontrôlable et paralysant, comme une crise d’angoisse ou une crise de panique. Quand cela arrive, on devient incapable de travailler, et ce n’est pas grave.
Tous les effondrements ne prennent pas la forme d’une crise de panique ou d’angoisse. Ils peuvent se manifester ainsi, mais ce n’est pas la seule manière. Le stress se manifeste très différemment selon les personnes, et même selon les facteurs de stress.
Comme on ne peut pas savoir ce que cette personne a réellement vécu dans sa tête, il est presque impossible de « diagnostiquer » de l’extérieur. Même si ce n’était pas une crise de panique complète, on dirait bien qu’elle a été fonctionnellement paralysée pendant plusieurs heures.
En cherchant en ligne, il semble que le Xanax puisse créer une dépendance.
https://www.drugs.com/xanax.html
Ça ne semble pas être le genre de chose à prendre à la légère.
Il y a de fortes chances qu’on voie une grande cohorte arrivée dans la tech au milieu des années 2000 mourir de maladies liées au stress.
En tant que personne ayant souffert d’une forte anxiété toute sa vie, l’idée qu’un comprimé addictif puisse faire disparaître tout ça me fait peur. J’ai l’impression que j’y resterais accroché toute ma vie.
Le stress de cette personne venait d’une ligne de code de PostHog. Le commit annulé est ici : https://github.com/PostHog/posthog-js/pull/1371/commits/7598...
J’y vois deux leçons. Premièrement, si vous l’avez déployé, vous en êtes propriétaire. Donc moins vous déployez, mieux c’est, et il faut minimiser les dépendances. Deuxièmement, il faut garder ce qui n’est pas important hors du chemin critique. Un moteur ne devrait pas s’arrêter parce que le compresseur de climatisation est en panne. Dans un navigateur, c’est très difficile à obtenir, mais ça vaut la peine d’essayer.
Pire encore, PostHog semble mettre à jour dynamiquement une partie de son propre code à l’exécution, plutôt que de l’inclure dans le build
La documentation propose une option avancée pour inclure toutes les dépendances dans le build. Je comprends pourquoi ils font ça, et je me trompe peut-être, mais en tant qu’utilisateur, je m’attendrais à ce que le chargement différé du code exécuté ne soit pas le comportement par défaut, mais une option d’optimisation. À mon avis, il ne devrait être utilisé que lorsque le bundle complet entraîne de sérieux retards de livraison.
Le bug semble avoir été dans le
window.fetchmonkey-patchéhttps://github.com/PostHog/posthog-js/blob/759829c67fcb8720f...
La grande leçon ici, c’est que si l’on crée une bibliothèque populaire tout en faisant du monkey-patching de fonctions globales, les tests doivent vraiment être solides.
« Mettons les appels à PostHog dans un try/catch au cas où » et « à cause de PostHog, on ne peut littéralement pas envoyer une requête POST avec
fetch()» sont deux choses complètement différentes.fetchet un mocking excessif : https://github.com/PostHog/posthog-js/blob/main/src/_tests...Les fonctions fetch et XHR étant entièrement mockées pour ne rien faire, il est logique que les problèmes d’interaction avec les API natives sous-jacentes ou d’autres bibliothèques ne soient pas détectés. Cypress est aussi configuré, donc je ne comprends pas pourquoi ils cherchent à mocker les API du navigateur.
Si elle avait été intégrée de façon raisonnable, je pensais qu’au pire le traitement des événements de monitoring échouerait.
Le fait que PostHog patche une fonction globale aussi critique est une fonctionnalité qui devrait être très bien documentée. Ainsi, les utilisateurs le sauraient et pourraient raisonnablement y penser lorsqu’ils déboguent des problèmes difficiles à expliquer en apparence.
Par exemple, Heap Analytics, encore ce mois-ci, interfère avec quelque chose dans les internals de Hotwire, casse aléatoirement Hotwire de façon complète et transforme tous les clics en chargements de page complets. D’après mon expérience, cela affecte 30 à 60 % des chargements de page. On peut corriger le problème, mais il m’a fallu plus de 50 heures de débogage pour faire en sorte que Heap se charge après tout le JavaScript de Hotwire.
Comme d’autres l’ont dit, le bug qui a mené à ce stress nocturne était un changement d’une ligne dans la bibliothèque PostHog[0]
J’y vois un rappel de l’importance de donner aux variables des noms précis.
Le code
res = await originalFetch(url, init)a l’air assez inoffensif. Mais comme le montre la déclaration TypeScript, le paramètreurln’est pas nécessairement une URL :url: URL | RequestInfoSi ce n’est pas une URL mais un objet RequestInfo, ça pose problème. Au début de l’implémentation de la fonction, un objet Request a été créé et a déjà été « consommé » ; il ne peut donc pas être réutilisé ici.
Si le paramètre avait eu un nom plus précis, comme
urlOrRequestInfo, il aurait été plus difficile de rater ce problème lors de ce changement.C’est une idée beaucoup plus spéculative, mais comme les types linéaires issus de la logique linéaire permettent de formaliser le fait qu’une valeur est « consommée », un système de types approprié pourrait peut-être empêcher ce genre de bug.
[0] https://github.com/PostHog/posthog-js/pull/1351/commits/2497...
Il suffit de regarder la sémantique de propriété dans un langage comme Rust. Ce n’est pas infranchissable, et ça s’améliore notamment avec l’expérience, mais c’est suffisamment lourd pour être le point dont les apprenants se plaignent le plus.
C’était un article stressant, mais aussi drôle. Cela dit, la partie sur l’auto-culpabilisation m’a semblé beaucoup trop familière.
Je maintiens une application iOS/macOS assez réussie, et il m’est déjà arrivé de pousser une release qui a complètement cassé plus de 350 000 installations. Ce n’était pas entièrement ma faute, mais comme c’est mon produit, ça ne changeait pas grand-chose.
La sueur froide et la honte à ce moment-là étaient vraiment intenses. Et comme c’était sur l’App Store, le correctif devait aussi passer par le processus de revue, ce qui rallongeait le délai. Heureusement, l’app est passée en revue 30 minutes après la soumission et a été approuvée en quelques minutes.
En avançant dans ma carrière et en passant au leadership, j’ai compris que c’était une expérience très précieuse. À l’époque, j’ai peut-être été stressé, mais aujourd’hui ce souvenir est si lointain qu’il ne m’atteint même plus. Je ne suis clairement plus stressé par ça.
C’est peut-être controversé, mais il m’arrive de laisser des membres en début de carrière casser la production, quand je le vois venir et que je suis convaincu que nous pouvons rétablir rapidement la situation.
Tout le monde sait qu’il est important de laisser de la place à l’échec, mais beaucoup de responsables tracent la limite dès que l’échec affecte de vrais clients. Dans le cas très courant et chanceux où l’on ne construit pas quelque chose de critique comme un logiciel qui fait atterrir des avions, il faut permettre à l’équipe de vivre une panne de production, même si cela coûte à quelqu’un à Spokane, dans l’État de Washington, quelques minutes d’indisponibilité du produit.
Merci d’avoir écrit ce genre de billet. J’aime lire comment les gens traversent ce type d’épreuve, surtout sous pression, généralement toute la nuit.
C’est encore mieux parce qu’on y trouve non seulement une analyse technique post-mortem, mais aussi le point de vue humain qui disparaît habituellement de ces récits. Ce type de narration technique est probablement quelque chose que seuls de petits développeurs, des développeurs solo ou des fondateurs peuvent partager librement.
Rien qu’à voir la façon dont le problème a été traqué, on voit d’abord que c’est un programmeur. Il est allé vers son code, puis vers les logs. Les deux sont rationnels, et les deux pouvaient être la cause, mais il a manqué l’indice le plus important qu’il avait : « ça marchait en localhost »
SRE, DevOps, ingénieur plateforme, peu importe le titre qu’on colle ce jour-là : je me serais concentré sur la différence entre le système qui fonctionne et celui qui ne fonctionne pas. J’aurais ajouté puis retiré les différences une par une, ou retiré puis réajouté, jusqu’à ce que quelque chose fonctionne
Je vois deux choses. 1) Il existe un environnement qui fonctionne. 2) L’environnement qui échoue fonctionnait lui aussi à l’origine, puis a commencé à échouer
Je ne veux pas dire que ma méthode est supérieure. Je veux seulement montrer la différence de façon d’aborder le problème. Les deux resserrent autour de ce qu’ils connaissent. Moi, je connais les systèmes ; vous, vous connaissez le code
Je pensais que c’était sans espoir, mais un technicien plus âgé et plus sage m’a appris une méthode
On branchait une bonne carte sur un extenseur, et on faisait tourner en boucle le programme de diagnostic qui échouait. Avec un oscilloscope, on observait et notait toutes les broches du connecteur. On remplaçait par une mauvaise carte et on recommençait
Quel signal est différent ? On remonte ce signal. Si le schéma ne correspond pas, on dessine, avec un voltmètre et ses yeux, un schéma qui reflète le câblage réel
Il appelait ça « bonne carte - mauvaise carte », et ça marchait vraiment. Je ne prétendrai pas que c’était rentable, mais on a réparé toutes les cartes, et mes compétences en dépannage de circuits électroniques numériques ont énormément progressé
C’était une sorte de travail de « pompier ». On attendait que le système tombe en panne, donc ce n’était pas grave que deux techniciens passent une semaine sur une seule carte électronique
« Revenons à la version d’il y a un mois. Ça ne marche pas. Et trois mois ? Ça ne marche pas. Toujours en échec. Un an ? Pas du tout. »
Est-ce qu’il ne revenait en arrière que sur son propre code, tout en continuant à utiliser la mise à jour de PostHog qui avait cassé le même jour ? La leçon que j’en tire, c’est qu’il faut pouvoir revenir en arrière sur tout, dépendances comprises
https://github.com/PostHog/posthog/issues/24471#issuecomment...
Il existe aussi l’option de les intégrer directement au bundle
https://github.com/PostHog/posthog/issues/24471#issuecomment...
L’auteur du billet original s’en est bien sorti. Le côté positif de ce genre d’incident, c’est qu’il en sort une montagne de leçons précieuses
Bon article qui rappelle qu’il y a des personnes derrière les services, et qui montre aussi bien le processus de débogage
En réalité, la pression ne vous aide pas à déboguer plus vite. En général, elle gêne la réflexion. Il faut ignorer autant que possible les conséquences et rester aussi calme que possible
La plupart d’entre nous ont probablement vécu des situations similaires, à des degrés divers. Bien sûr, le stress lié au fait de diriger sa propre entreprise est particulièrement intense