Symptom ≠ Ursache: Warum der Auto-Healer zum eigentlichen Problem wurde
Ein PostgreSQL-Primary auf 91 % CPU. Der Auto-Healer kickt den lautesten Query. Eine Stunde später: wieder 91 %. Die Lektion: Quickfixes können sich in Dauerschleifen verbeißen, wenn niemand fragt, welches Pattern sich wiederholt.
Eine PostgreSQL-Primary mit 91 % CPU-Last. Der Auto-Healer läuft alle 5 Minuten, findet den lautesten Query, schickt pg_cancel_backend(), und die Load sinkt auf 35 %. Eine Stunde später: wieder 91 %. Der Healer kickt den nächsten Query. Wieder 35 %. Wieder eine Stunde. Wieder 91 %.
Man könnte das als „System arbeitet" deklarieren. Es tut es nicht. Es verbrennt CPU-Zyklen, um ein Symptom zu bekämpfen, das es selbst nie versteht.
Der Moment der Ehrlichkeit
Nach drei Zyklen haben wir aufgehört. Statt den Healer aggressiver zu tunen, die Frage gestellt, die wir von Anfang an hätten stellen müssen: Was bringt die Load zurück?
Antwort, nach 20 Minuten Audit: 14 verschiedene Endpoints im Backend rufen dieselbe 1,4-Millionen-Zeilen-COUNT(*) FILTER (...)-Aggregation auf. Ohne Cache-Lock. Bei jedem Cache-Miss feuern acht parallele Uvicorn-Worker gleichzeitig dieselbe teure Query. Thundering Herd.
Der Healer hatte Recht, dass da Queries laufen, die laufen sollten nicht. Der Healer hatte unrecht, dass ihn Töten das Problem löst. Das Problem war, dass 14 Clients dieselbe Kuh beim Melken gleichzeitig angefasst haben.
Was wir stattdessen tun mussten
- Pro Cache-Key genau einen Lock. Wir nutzen Redis
SET NX EXfür Distributed-Lock plusasyncio.Lockpro Worker als lokalen Short-Circuit. - 14 Endpoints gefunden, drei kritische sofort gefixt, die anderen elf in einem Ticket mit Zeitplan dokumentiert.
- Healer nicht getunt. Der Healer war gar nicht das Problem.
Nach dem Fix: CPU-Load dauerhaft bei 53 %. Keine Eskalation mehr. Kein Healer-Zyklus nötig.
Die Meta-Lektion
Jedes System hat einen Punkt, an dem das Symptom interessanter wirkt als die Ursache. Der Symptom ist laut, sichtbar, und lässt sich mit einem kleinen Skript schnell „lösen". Die Ursache sitzt drei Abstraktionsebenen tiefer, hat keinen Namen, und erfordert, dass jemand das Subsystem wirklich versteht.
Die Regel, die wir uns seitdem bei jedem wiederkehrenden Incident stellen:
„Wenn wir diesen Fix jetzt machen — was verhindert, dass wir in einer Stunde wieder hier stehen?"
Wenn die Antwort unklar ist, ist der geplante Fix kein Fix. Er ist ein Symptom-Unterdrücker, und das System wird eine andere, lautere Art finden, dieselbe Ursache zu signalisieren.
Anwendung jenseits von Datenbanken
Dasselbe Pattern kennt jede:r Senior Engineer:
- Flaky Test? Tot-Retry im CI ist Symptom-Unterdrückung. Der Test deckt ein echtes Race auf.
- Memory-Leak? Pod-Restart alle 6 h ist Symptom-Unterdrückung. Der Leak frisst Customer-Daten zwischendurch.
- Bounce-Rate auf einem Button? A/B-Test mit neuer Farbe ist Symptom-Unterdrückung. Das Feature löst das Nutzerproblem nicht.
Bei jedem wiederkehrenden Problem: Identifiziere die fünf Konsumenten des betroffenen Subsystems und prüfe das Pattern dort — nicht nur an der Stelle, wo das Alert laut war.
Ähnlicher Brand bei dir?
Wir haben vermutlich schon etwas ähnliches gesehen. Sprich mit uns.
Gespräch starten→