English· Español· Deutsch· Nederlands· Français· 日本語· ქართული· 繁體中文· 简体中文· Português· Русский· العربية· हिन्दी· Italiano· 한국어· Polski· Svenska· Türkçe· Українська· Tiếng Việt· Bahasa Indonesia

un

invité
1 / ?
retour aux leçons

Forme 1 : Réparation d’état. Forme 2 : Rapport gaspilleur.

Un cœur métronomique bat sur une horloge. Pas sur le besoin. Pas sur le changement. Sur une minuterie.

Deux formes, une seule cause racine : un job planifié qui se substitue à une conception correcte.

Forme 1 : Réparation d’état

Une transition d’état ne se termine pas de manière atomique. Au lieu de corriger la transition, un job en arrière-plan s’exécute après un délai et réconcilie. Les utilisateurs voient un état cassé pendant la fenêtre de réconciliation.

Exemple GitHub (2026-04-08) : Une pull request dont le dépôt amont est devenu privé. GitHub a tenté une transition d’état : fermer la PR, mettre à jour le statut de la branche, effacer le statut de fusion. La transition ne s’est pas effectuée de manière atomique. Le statut de la PR affichait simultanément « branch-forced-closed » et « Merge status cannot be loaded ». Un job Sidekiq en arrière-plan s’est exécuté quelques minutes plus tard et a terminé la réconciliation. Les observateurs ont vu un état incohérent pendant toute la durée de la fenêtre.

Le Cœur Mesuré : le job Sidekiq s’exécutait selon un calendrier. Il ne s’exécutait pas parce que GitHub avait détecté un état incohérent ; il s’exécutait parce que le minuteur avait sonné. Un utilisateur observant la PR en temps réel voyait une PR qui se contredisait jusqu’à l’exécution suivante du job.

Forme 2 : Rapport gaspilleur

Un rapport ou une agrégation se recalcule entièrement à intervalles fixes. Aucun contrôle de cache. Aucune protection d’idempotence. Aucune mise à jour incrémentale. Chaque exécution : un balayage complet.

Exemples : un cron nocturne qui recalcule le montant total des achats de chaque utilisateur en scannant toutes les commandes depuis l’origine. Un job d’analytique quotidien qui régénère un tableau de bord à partir des logs d’événements bruts. Un e-mail de synthèse hebdomadaire qui interroge chaque ligne de la table d’activité.

Chacun s’exécute que les données aient changé ou non depuis la dernière exécution. Chacun scanne l’historique complet même lorsque seules les dernières 24 heures contiennent de nouvelles données. Chacun substitue la répétition planifiée à une conception incrémentale.

La Racine Commune

Un Cœur Mesuré ne peut pas dire la vérité sur son propre état. Il ne connaît que l’horloge. Forme 1 : le job de réparation d’état s’exécute à T+5 minutes, que l’état soit cassé ou non à T+0. Forme 2 : le job de rapport s’exécute à 2 h du matin, que des données aient changé ou non depuis la veille.

L’horloge ne porte aucune information sur ce qu’il faut faire. Un événement porte cette information : « une transition d’état vient d’échouer », « de nouvelles commandes viennent d’arriver ». Un Metered Heart jette cette information et la remplace par un planning.

Drain de capital

Un Metered Heart draine du capital vivant : des ingénieurs en astreinte pour les incidents d’état cassé. Il érode la confiance sociale : les utilisateurs voient des données incohérentes et signalent des défauts qui se résolvent d’eux-mêmes. Il amplifie les autres MOAD : une tâche de réparation d’état qui scanne tous les enregistrements pour trouver l’état cassé contient souvent MOAD-0001 (scan O(N²)). Une tâche de rapport qui recalcule à froid peut déclencher MOAD-0005 (stampede de cache). MOAD-0009 aggrave les autres défauts.

La racine partagée

La Forme 1 et la Forme 2 semblent différentes en surface : l’une répare l’état, l’autre recalcule des données. La cause racine les relie.

La Forme 1 et la Forme 2 partagent une cause racine. Décrivez-la en une phrase. Donnez ensuite un exemple de chaque forme dans un logiciel que vous avez utilisé.

Déclencher sur le changement, pas sur l’horloge

Une conception pilotée par les événements se déclenche lorsqu’un élément change. Le changement d’état constitue l’événement. L’événement est le déclencheur.

Forme 1 : la transition atomique remplace la tâche de réparation.

Si une transition d’état peut laisser le système dans un état intermédiaire défectueux, le défaut réside dans la transition, et non dans l’absence d’une tâche de réparation. Corrigez la transition pour qu’elle s’exécute de manière atomique (ou transactionnelle). Lorsque la transition s’exécute de manière atomique, l’état défectueux n’existe jamais. La tâche de réparation n’a rien à réparer.

# DÉFAUT : transition non atomique laissant un état défectueux
def close_pr_on_repo_private(pr_id):
pr = PR.get(pr_id)
pr.status = 'branch-forced-closed'   # étape 1 : état partiel
pr.save()                             # visible aux utilisateurs MAINTENANT
# ... d'autres étapes peuvent échouer ...
pr.merge_status = 'not_applicable'
pr.save()                             # étape 2 : maintenant cohérent
# Job Sidekiq réconcilie si l'étape 2 échoue
# CORRECTION : transition atomique ; aucun état intermédiaire visible
def close_pr_on_repo_private(pr_id):
with db.transaction():
pr = PR.get(pr_id)
pr.status = 'branch-forced-closed'
pr.merge_status = 'not_applicable'
pr.save()   # les deux champs sont enregistrés de manière atomique ; jamais d’écriture partielle

Forme 2 : la mise à jour incrémentale remplace le recalcul complet.

Un rapport qui recalcule à partir de zéro se déclenche parce que anciennes données + nouvelles données = nouveau résultat. Mais ancien résultat + delta = même nouveau résultat, calculé de façon incrémentale. L’événement : arrivée de nouvelles données. Le déclencheur : mettre à jour l’agrégat uniquement pour les nouvelles données.

# DÉFAUT : recalcul complet planifié
def nightly_totals_job():
for user in all_users():
total = sum(o.amount for o in user.orders)  # scan all time
user.total_purchases = total
user.save()

# CORRECTION : mise à jour incrémentielle pilotée par événements
def on_order_placed(order):
order.user.total_purchases += order.amount   # delta uniquement
order.user.save()

La mise à jour incrémentielle se déclenche lorsqu’une commande arrive, pas à 2 h du matin. Elle met à jour uniquement l’utilisateur concerné. Elle lit uniquement la nouvelle commande, pas toutes les commandes de tous les temps. Le job nocturne disparaît.

Pourquoi le Formulaire 1 révèle une transition cassée

Un Form 1 Metered Heart révèle qu’une transition d’état est restée incomplète. Le job de réparation existe parce qu’un ingénieur a constaté un état cassé et a ajouté un mécanisme de réconciliation plutôt que de corriger la transition. Le job de réparation : un patch sur une décision architecturale défaillante.

MOAD-0009 en tant qu’amplificateur

MOAD-0009 amplifie les autres MOAD. Un job de réparation d’état qui scanne tous les enregistrements pour trouver l’état cassé : MOAD-0001 (scan O(N) ou O(N²) à chaque exécution du job). Un job de rapport qui recalcule tout à froid : MOAD-0005 (stampede de cache lorsque le job démarre et interroge un amont chaud). MOAD-0009 ne nuit pas seulement par lui-même ; il délivre les autres MOAD selon un planning.

Diagnostiquer & Redesigner

Une équipe exécute un cron job nocturne à 2 h du matin. Le job scanne toutes les commandes de tous les utilisateurs et recalcule de zéro le montant total des achats de chaque utilisateur. Le job prend 4 heures. À 6 h du matin, le tableau de bord affiche des totaux frais. Entre 2 h et 6 h du matin, le tableau de bord affiche les totaux de la veille.

Quelle forme de MOAD-0009 est-ce ? Quel événement doit déclencher la recomputation à la place ? Quelle structure de données intermédiaire rend la mise à jour incrémentale ?