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

un

Gast
1 / ?

Form 1: State-Repair. Form 2: Verschwenderischer Report.

Ein vermessenes Herz schlägt nach der Uhr. Nicht nach Bedarf. Nicht nach Änderung. Nach einem Timer.

Zwei Formen, eine Ursache: ein geplanter Job als Ersatz für korrektes Design.

Form 1: State-Repair

Ein Zustandsübergang wird nicht atomar abgeschlossen. Anstatt den Übergang zu reparieren, läuft ein Hintergrundjob mit Verzögerung und gleicht ab. Benutzer sehen während des Abgleichfensters fehlerhaften Zustand.

GitHub-Beispiel (2026-04-08): Ein Pull Request’s Upstream-Repository wurde privat. GitHub versuchte einen Zustandsübergang: den PR schließen, den Branch-Status aktualisieren, den Merge-Status löschen. Der Übergang wurde nicht atomar abgeschlossen. Der PR-Status zeigte gleichzeitig „branch-forced-closed“ und „Merge status cannot be loaded“. Ein Sidekiq-Hintergrundjob lief Minuten später und führte die Abstimmung durch. Beobachter sahen den inkonsistenten Zustand während des gesamten Zeitfensters.

Das getaktete Herz: Der Sidekiq-Job lief nach einem Zeitplan. Er lief nicht, weil GitHub einen defekten Zustand erkannt hätte; er lief, weil der Timer ausgelöst wurde. Ein Benutzer, der den PR in Echtzeit beobachtete, sah einen PR, der sich selbst widersprach, bis der nächste Job ausgeführt wurde.

Form 2: Verschwendungsbericht

Ein Bericht oder eine Aggregation wird in einem festen Intervall von Grund auf neu berechnet. Keine Cache-Prüfung. Kein Idempotenz-Schutz. Kein inkrementelles Update. Jede Ausführung: ein vollständiger Scan.

Beispiele: Ein nächtlicher Cron-Job, der den gesamten Kaufsumme jedes Benutzers neu berechnet, indem er alle Bestellungen seit Beginn der Zeit scannt. Ein täglicher Analytics-Job, der ein Dashboard aus rohen Event-Logs neu generiert. Eine wöchentliche Zusammenfassungs-E-Mail, die jede Zeile der Aktivitätstabelle abfragt.

Jeder Job wird ausgeführt, unabhängig davon, ob sich Daten seit der letzten Ausführung geändert haben. Jeder scannt die vollständige Historie, auch wenn nur die letzten 24 Stunden neue Daten enthalten. Jeder ersetzt geplante Wiederholung durch inkrementelles Design.

Die gemeinsame Wurzel

Ein getaktetes Herz kann nicht die Wahrheit über seinen eigenen Zustand sagen. Es kennt nur die Uhr. Form 1: Der Reparaturjob läuft bei T+5 Minuten, unabhängig davon, ob der Zustand bei T+0 bereits defekt ist. Form 2: Der Berichtsjob läuft um 2 Uhr morgens, unabhängig davon, ob sich seit gestern Daten geändert haben.

Die Uhr trägt keine Informationen darüber, was zu tun ist. Ein Ereignis trägt diese Informationen: „ein Zustandsübergang ist gerade fehlgeschlagen“, „neue Bestellungen sind gerade eingegangen.“ Ein Metered Heart verwirft diese Informationen und ersetzt sie durch einen Zeitplan.

Kapitalverlust

Ein Metered Heart verbraucht lebendiges Kapital: Ingenieure, die für Vorfälle mit inkonsistentem Zustand bereitstehen. Er untergräbt das soziale Vertrauen: Nutzer sehen inkonsistente Daten und melden Fehler, die sich von selbst beheben. Er verstärkt andere MOADs: eine Zustandsreparatur, die alle Datensätze scannt, um inkonsistente Zustände zu finden, enthält oft MOAD-0001 (O(N²)-Scan). Ein Berichtsjob, der kalte Daten neu berechnet, kann MOAD-0005 (Cache Stampede) auslösen. MOAD-0009 verstärkt andere Defekte.

Die gemeinsame Wurzel

Form 1 und Form 2 erscheinen oberflächlich unterschiedlich: die eine repariert Zustände, die andere berechnet Daten neu. Die gemeinsame Ursache verbindet sie.

Form 1 und Form 2 teilen eine gemeinsame Ursache. Beschreibe sie in einem Satz. Gib dann je ein Beispiel für beide Formen aus Software, die du verwendet hast.

Bei Änderung auslösen, nicht bei Uhrzeit

Event-getriebenes Design feuert, wenn sich etwas ändert. Die Zustandsänderung ist das Event. Das Event ist der Auslöser.

Form 1: der atomare Übergang ersetzt den Reparatur-Job.

Wenn ein Zustandsübergang das System in einem defekten Zwischenzustand hinterlassen kann, liegt der Defekt im Übergang selbst, nicht in der Abwesenheit eines Reparatur-Jobs. Behebe den Übergang so, dass er atomar (oder transaktional) abläuft. Wenn der Übergang atomar abgeschlossen ist, existiert kein defekter Zustand. Der Reparatur-Job hat nichts zu reparieren.

# DEFECT: nicht-atomarer Übergang hinterlässt defekten Zustand
def close_pr_on_repo_private(pr_id):
pr = PR.get(pr_id)
pr.status = 'branch-forced-closed'   # Schritt 1: partieller Zustand
pr.save()                             # jetzt für Benutzer sichtbar
# ... weitere Schritte können fehlschlagen ...
pr.merge_status = 'not_applicable'
pr.save()                             # Schritt 2: nun konsistent
# Sidekiq-Job gleicht aus, falls Schritt 2 fehlschlägt
# FIX: atomarer Übergang; kein Zwischenzustand sichtbar
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()   # beide Felder werden atomar geschrieben; niemals halb geschrieben

Form 2: das inkrementelle Update ersetzt die vollständige Neuberechnung.

Ein Bericht, der von Grund auf neu berechnet wird, feuert, weil alte Daten + neue Daten = neues Ergebnis. Aber altes Ergebnis + Delta = dasselbe neue Ergebnis, inkrementell berechnet. Das Event: neue Daten sind eingetroffen. Der Trigger: aktualisiere nur das Aggregat für die neuen Daten.

# DEFECT: vollständige Neuberechnung nach Zeitplan
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()

# FIX: ereignisgesteuerte inkrementelle Aktualisierung
def on_order_placed(order):
order.user.total_purchases += order.amount   # nur Delta
order.user.save()

Das inkrementelle Update wird ausgelöst, wenn eine Bestellung eintrifft, nicht um 2 Uhr nachts. Es aktualisiert nur den betroffenen Benutzer. Es liest nur die neue Bestellung, nicht alle Bestellungen aller Zeiten. Der nächtliche Job verschwindet.

Warum Form 1 eine kaputte Transition offenbart

Ein Form-1-gemessenes Herz zeigt, dass ein Zustandsübergang unvollständig geblieben ist. Der Reparatur-Job existiert, weil ein Ingenieur einen kaputten Zustand bemerkt und einen Abgleichsmechanismus hinzugefügt hat, anstatt den Übergang zu reparieren. Der Reparatur-Job: ein Pflaster über einer kaputten Architekturentscheidung.

MOAD-0009 als Verstärker

MOAD-0009 verstärkt andere MOADs. Ein Zustandsreparatur-Job, der alle Datensätze scannt, um kaputten Zustand zu finden: MOAD-0001 (O(N)- oder O(N²)-Scan pro Job-Ausführung). Ein Report-Job, der alles kalt neu berechnet: MOAD-0005 (Cache-Stempelwede, wenn der Job startet und ein warmes Upstream trifft). MOAD-0009 schadet nicht nur allein; er liefert andere MOADs nach Plan.

Diagnose & Redesign

Ein Team führt einen nächtlichen Cron-Job um 2 Uhr nachts aus. Der Job scannt alle Bestellungen aller Benutzer und berechnet den Gesamtkaufbetrag jedes Benutzers von Grund auf neu. Der Job dauert 4 Stunden. Um 6 Uhr morgens zeigt das Dashboard frische Summen. Zwischen 2 Uhr und 6 Uhr morgens zeigt das Dashboard die Summen des Vortags.

Welche Form von MOAD-0009 ist das? Welches Ereignis sollte die Neuberechnung auslösen? Welche Zwischenstruktur macht die Aktualisierung inkrementell?