Cache-Invalidierung: Zwei harte Lektionen
„Es gibt nur zwei schwere Dinge in der Informatik: Cache-Invalidierung und Dinge benennen.“ Der Satz wird meist Phil Karlton bei Netscape zugeschrieben. Nach zwei Produktions-Incidents im selben Jahr haben wir aufgehört, ihn als Witz zu behandeln.
Incident eins: der Cache-Key, der zur Datenexposition wurde
„Lass uns Caching einbauen, damit es schneller wird.“ Wir cachten eine API-Antwort, die nutzerspezifische Daten enthielt — Name, E-Mail-Adresse, Bestellhistorie. Der Cache-Key enthielt die User-ID nicht. Sechs Stunden lang sah jeder Nutzer, der diesen Endpoint aufrief, die Daten des ersten Nutzers.
Wir legten es zunächst als Korrektheitsbug ab. Das war es nicht: Es war ein Datenexpositions-Incident. Die personenbezogenen Daten einer Person wurden an alle ausgeliefert, was uns mitten ins Terrain der Datenschutzprüfung brachte. Wir mussten den Cache leeren, aus den Access-Logs rekonstruieren, wer was gesehen haben könnte, und unsere Meldepflichten bewerten — ein Tag Rechts- und Engineering-Arbeit, verursacht durch ein fehlendes Feld in einem Key. Wenn Sie irgendetwas Nutzerbezogenes cachen, behandeln Sie einen fehlerhaften Key als Security-Fehlermodus, nicht als Performance-Bug, und schicken Sie ihn wie jede andere Exposition durch Ihren Security-Operations-Prozess.
Incident zwei: der Invalidierungs-Fan-out, den wir nicht überblicken konnten
Monate später, ein anderes System. Unsere Invalidierungslogik bei einem Profil-Update sah gründlich aus:
- Den User-Cache invalidieren
- Den Cache der Posts des Nutzers invalidieren
- Die Feeds der Follower invalidieren
- Den Suchindex und den Empfehlungs-Cache invalidieren
Was wir vergessen hatten: die Kommentare des Nutzers unter den Posts anderer (alter Anzeigename), die Benachrichtigungshistorie (alter Avatar) und den E-Mail-Vorschau-Cache (alte Bio). Nutzer meldeten tagelang „Geister“-Versionen ihrer selbst.
Die Erkenntnis
Wenn ein System wächst, wächst die Zahl der Stellen, an denen ein Datum gecacht wird, schneller als Ihre Fähigkeit, sie zu verfolgen. Beide Incidents waren dieselbe Krankheit: Niemand konnte jeden Cache aufzählen, also konnte niemand vollständig über Keys oder Invalidierung nachdenken.
Was wir heute tun
- Eine zentrale Cache-Registry. Jeder Cache — Redis, CDN, In-Process — bekommt einen Eintrag: Owner, Key-Schema, TTL, Invalidierungs-Trigger, akzeptable Veraltung. Ein neuer Cache besteht das Design-Review nicht ohne einen. Das ist die einzelne Änderung mit der größten Hebelwirkung, die wir gemacht haben.
- Versionierte Cache-Keys. Ein hochgezähltes Versionspräfix setzt einen ganzen Namespace außer Dienst, ohne einzelne Keys zu jagen.
- Gezielte Invalidierung statt globalem Busting. Bei einer Änderung an Nutzerdaten alles zu flushen klingt sicher, tauscht aber Veraltung gegen eine Thundering Herd: Jeder Request geht auf einmal am Cache vorbei und stürmt die Datenbank. Versionspräfixe, TTLs mit Jitter und Request Coalescing verhindern, dass die Invalidierung zum eigenen Ausfall wird.
- Akzeptierte Veraltung, wo sie wirklich keine Rolle spielt — in der Registry dokumentiert, nicht ad hoc entschieden.
Fragen, die wir stellen, bevor wir irgendeinen Cache hinzufügen
- Sind die Daten nutzerspezifisch, und beweist der Key das?
- Wie oft ändern sie sich, und was kosten veraltete Daten?
- Können wir heute jeden Invalidierungspfad aufzählen?
- Wie groß ist der Blast Radius, wenn der Key falsch ist?
Lektion: Wenn Sie nicht jede Stelle auflisten können, an der ein Datum gecacht wird, können Sie es nicht zuverlässig invalidieren. Und der schnellste Cache ist immer noch der, den Sie nicht brauchen — ein Cache kann sogar Latenz hinzufügen —, also optimieren Sie zuerst die Abfrage und cachen Sie nur, was Sie gemessen haben.
Incidents wie dieser sind vermeidbar. Unser AWS Managed Services-Team überwacht Kundenumgebungen 24/7 mit KI-gestütztem Monitoring und Senior Engineers am Pager — damit Geschichten wie diese im Archiv bleiben.