Annuel Payez à l'année et profitez de 2 mois offerts sur chaque offre payante. Voir les offres
Se connecter Commencer gratuitement

La page de consommation affichait trop, et pas un centime n'a bougé

Commençons par la partie la plus facile à perdre en lisant le reste : aucun argent n'a été prélevé. Aucun portefeuille n'a été débité deux fois, aucune carte n'a été débitée, aucune allocation d'abonnement n'a été dépens…

Commençons par la partie la plus facile à perdre en lisant le reste : aucun argent n'a été prélevé. Aucun portefeuille n'a été débité deux fois, aucune carte n'a été débitée, aucune allocation d'abonnement n'a été dépensée pour un travail qui n'a jamais eu lieu.

Ce qui était cassé, c'était un nombre à l'écran. Sur certains comptes, la répartition de la consommation sur votre propre page de compte affichait trop — sur le pire que nous ayons mesuré, 2,4 fois le grand livre qu'elle est censée résumer. Le grand livre a été juste tout du long. Les plafonds mensuels aussi, qui se lisent ailleurs entièrement. C'était un défaut d'affichage, il est corrigé, et voici sa cause.

Une clé qui cherchait quelque chose que personne n'avait écrit

Le travail déclenché par ce que vous faites — mettre à jour un résumé quotidien de consommation, créditer un parrainage, diffuser une notification — n'a pas lieu à l'intérieur de votre requête. La requête écrit un message et un consommateur d'arrière-plan le reprend un instant plus tard : c'est ce qui empêche un effet secondaire lent de ralentir une page rapide.

Le danger de ce montage, c'est de faire deux fois le même travail, car un courtier relivre volontiers le même message après un accroc. La défense est une boîte de réception : avant traitement, un consommateur revendique le message en écrivant une ligne portant la clé de ce message, et un index unique rend une seconde revendication impossible.

La nôtre revendiquait sur une paire — le locataire auquel appartenait le message, plus la clé du message — et recherchait cette paire face à l'identifiant de locataire porté par l'enveloppe du message, lequel est bien renseigné.

Les lignes de revendication ne portaient aucun identifiant de locataire. Toutes étaient vides, et l'avaient été depuis le début. La documentation de l'entité affirmait, mot pour mot, que le framework l'estampillait à l'insertion. Il ne l'a jamais fait. Mesuré en production le jour de la découverte : les 3 344 lignes de la boîte de réception vides, tandis que 1 174 des 1 451 lignes côté producteur portaient un locataire réel — ce qui explique précisément que personne n'ait soupçonné le côté lecture.

La recherche demandait donc (locataire, clé), la ligne stockée était (rien, clé), et elle ne pouvait jamais correspondre.

Rien n'a échoué, et c'est pourquoi cela a duré des semaines

Une recherche qui ne peut jamais correspondre, cela ressemble à quelque chose qui devrait lever une erreur au premier essai. Elle a fait l'inverse.

La revendication a décidé que le message était neuf. L'insertion a ensuite heurté l'index unique et a été rejetée comme doublon — à juste titre, puisque la ligne était déjà là. Une branche de repli a attrapé ce rejet et l'a lu avec bienveillance : une autre livraison a dû revendiquer celui-ci, rien à faire ici. L'enveloppe est restée dans l'état « encore à traiter », et comme elle était encore à traiter, la livraison suivante l'a reprise et a refait le travail.

Chaque consommateur retraitait son propre arriéré, environ toutes les quarante-cinq secondes, indéfiniment. Vingt-six le faisaient. Une ligne de production s'est terminée 333 heures après son arrivée, et s'était terminée encore et encore pendant l'essentiel de ce délai.

Ce que cela a réellement cassé

Sur ces vingt-six consommateurs, quatre accumulent au lieu d'écraser. Ces quatre-là sont les projections de facturation que lit la page de consommation, et c'est là que la chose a fait surface.

Ils disposaient d'une protection explicite contre exactement cela, écrite après un incident d'août où un seul événement avait été appliqué neuf fois à une jauge. Cette protection marque une ligne comme réussie avant de la traiter, afin qu'une relivraison trouve la marque et s'arrête. La marque était écrite correctement à chaque fois. La revendication suivante la cherchait ensuite avec la même clé cassée et ne la trouvait pas. La garantie était vide depuis le jour de sa livraison.

Ce que cela n'a pas touché mérite d'être énuméré précisément, car « projection de facturation » sonne alarmant et les faits ne le sont pas :

  • Le grand livre était correct. Chaque appel d'IA écrit exactement un enregistrement de facturation portant son propre instantané de prix, et rien sur ce chemin ne les écrit.
  • Aucun portefeuille n'a bougé. Les soldes proviennent du grand livre, jamais d'un résumé.
  • Aucun plafond n'a bougé. Le garde-budget lit un compteur distinct, seule source de vérité sur ce que vous avez dépensé ce mois-ci. Personne n'a été coupé trop tôt à cause d'un chiffre gonflé, et personne n'est passé qui n'aurait pas dû.

La seule chose qui lit le résumé concerné, c'est l'historique de consommation de votre propre page de compte : votre total, et la répartition de ce à quoi il est passé. Voilà tout le rayon d'impact, et ce qu'il faisait, c'était afficher trop.

La moitié que personne ne regardait

La même recherche cassée se trouvait à un second endroit : le chemin exécuté quand un message échoue. Là, elle ne trouvait jamais la ligne qu'elle devait mettre à jour, et juste en dessous se tenait une branche portant le commentaire « inatteignable en pratique » — qui renvoyait « cet échec est terminal » à chaque échec.

Le compteur de tentatives n'avançait donc jamais et aucun message empoisonné n'a jamais atteint la file des messages morts. Toute une couche de gestion d'erreurs était éteinte, derrière un commentaire qui disait à quiconque le lisait qu'il n'y avait rien à vérifier ici.

Le correctif

Les deux recherches passent désormais par la clé de message au sein du groupe de consommateurs, ce que l'index unique impose de toute façon. Les clés de message dérivent de l'identifiant de ce qui les a produites : elles ne peuvent donc pas entrer en collision entre locataires, par construction.

La documentation qui affirmait l'estampillage a été corrigée plutôt que supprimée. Cette phrase était le piège, et la supprimer n'aurait rien laissé qui dise que quelqu'un a enfin vérifié.

Neuf tests verrouillent le comportement. Six d'entre eux échouent face à l'ancien code, ce que nous avons confirmé en le remettant en place.

Le nettoyage ensuite

Les lignes gonflées se réparent d'elles-mêmes. Un worker reconstruit les résumés quotidiens à partir du grand livre toutes les heures, sur une fenêtre de deux jours. Il tournait depuis le début et perdait : une fois par heure contre un consommateur qui réapplique toutes les quarante-cinq secondes, ce n'est pas une course équitable.

Le consommateur ne se rejouant plus, il gagne. Les deux jours encore dans cette fenêtre ont été reconstruits depuis le grand livre dès la mise en production du correctif. Deux jours antérieurs en étaient déjà sortis, et ce qui y subsiste, c'est un compte à nous plus un écart négligeable ailleurs — nous les avons donc laissés plutôt que d'élargir une fenêtre en production pour courir après, ce qui semblait préférable à laisser croire que le balayage avait été total.

Ce que nous en retenons

Une clé de déduplication doit refléter ce que l'index impose réellement, pas ce que le code croit avoir écrit. Les deux moitiés de celle-ci étaient alimentées par des champs différents, et aucune n'avait jamais été mesurée face à l'autre. Les deux le sont maintenant, et le test qui le prouve passe au rouge face à la version qui était fausse.

Partager LinkedIn X E-mail
Essayez-le sur votre propre travailChaque réponse affiche ce qu'elle a coûté. Commencez avec le forfait gratuit. Commencer gratuitement
Des nouveautés produit, pas du bruit Nouveaux modèles, fonctionnalités et temps forts du changelog - un court e-mail quand quelque chose sort vraiment.
En vous abonnant, vous acceptez de recevoir les e-mails de ToRun sur les nouveautés du produit. Vous pouvez vous désabonner à tout moment. Politique de confidentialité