Phil Karlton sagte, dass es in der Informatik nur zwei schwierige Dinge gibt: Cache-Ungültigmachung und Benennung von Dingen. KV macht das erste noch schwieriger, da Sie nicht steuern, wann die Invalidierung an jedem Präsenzpunkt eintrifft.
Durch das Löschen eines Schlüssels in KV wird dieser nicht sofort aus der Welt entfernt. Der Löschvorgang geht an den zentralen Speicher und breitet sich mit der gleichen letztendlichen Konsistenzdynamik wie die Schreibvorgänge an die PoPs aus: Es dauert bis zu 60 Sekunden, um alle Präsenzpunkte zu erreichen. Während dieses Fensters stellen PoPs, die die Löschung noch nicht erhalten haben, weiterhin den alten Wert für alle eingehenden Anforderungen bereit. Sie wissen nicht, welche PoPs es erhalten haben und welche nicht. Es gibt keine Möglichkeit, eine sofortige Ausbreitung zu erzwingen.
Dies macht die Cache-Ungültigmachung in KV zu einem Entwurfsproblem und nicht zu einem Betriebsproblem. Sie lösen das Problem nicht mit der Schaltfläche „Jetzt ungültig machen“, sondern durch die Auswahl eines Schlüsselmodells, das die Auswirkungen der Ausbreitungsverzögerung minimiert.
Versionierte Schlüssel: die robusteste Lösung
Der solideste Standard für Inhalte, die ungültig gemacht werden müssen, besteht darin, eine explizite Indirektion zwischen dem logischen Namen der Daten und dem physischen Schlüssel, in dem sie gespeichert sind, aufrechtzuerhalten.
Anstatt den gerenderten HTML-Code einer Seite in homepage-html zu schreiben und ihn dann zu löschen, wenn sich der Inhalt ändert, schreiben Sie ihn in homepage-html-v42. Ein zweiter Schlüssel, homepage-html-version, enthält nur die Zeichenfolge v42. Der Worker liest zuerst den Versionsschlüssel, erstellt den Namen des Inhaltsschlüssels und ruft den Wert ab.
Wenn sich der Inhalt ändert, schreiben Sie den neuen HTML-Code in homepage-html-v43 und aktualisieren homepage-html-version auf v43. Der alte Schlüssel muss nicht sofort gelöscht werden – er wird einfach nicht mehr referenziert. Die Speicherkosten bleiben bis zur Bereinigung bestehen, aber die Inkonsistenz bei der Ungültigmachung stellt kein Problem mehr dar: Jeder PoP, der das homepage-html-version-Update erhalten hat, erhält bereits den neuen Schlüssel. Alte Inhalte werden nur PoPs bereitgestellt, die das Versionsschlüssel-Update noch nicht erhalten haben, und dies geschieht innerhalb des 60-Sekunden-Fensters, unabhängig davon, was Sie tun.
Die Kosten für diesen Ansatz betragen einen zusätzlichen Lesevorgang pro Anfrage – zuerst den Versionsschlüssel, dann den Inhalt. In den meisten Fällen sind diese Kosten im Hinblick auf die Latenz irrelevant: Die beiden Lesevorgänge sind parallel, wenn der Worker sie mit Promise.all ausführt, und beide kommen im heißen Zustand in weniger als Millisekunden aus dem PoP-Cache an.
KV mit CDN-Bereinigung koordinieren
Für Inhalte, die vom Cloudflare-CDN (nicht direkt vom Worker) bereitgestellt werden, gibt es eine zusätzliche Caching-Ebene über dem KV. Das CDN hat möglicherweise eine Antwort zwischengespeichert, die mit einem alten KV-Wert generiert wurde, und selbst wenn der KV bereits in allen PoPs über den neuen Wert verfügt, wird die im CDN zwischengespeicherte Antwort weiterhin bereitgestellt, bis sie abläuft.
Die Lösung besteht darin, das KV-Update mit einer CDN-Bereinigung zu koordinieren. Mit der Cache Purge API von Cloudflare können Sie bestimmte URLs oder Cache-Tags über die API ungültig machen. Sie schreiben den neuen Wert in den KV und rufen im selben Verwaltungsvorgang die Purge-API für die betroffenen URLs auf. Aus der Sicht der Kunden, die das CDN durchlaufen, erscheint die neue Antwort unmittelbar nach der Bereinigung – unabhängig davon, wie viele PoPs den KV-Wert noch verbreiten.
Dieses Muster erfordert, dass Sie den Inhaltsaktualisierungsprozess steuern und Zugriff auf die Bereinigungs-API haben. Für CMS-Flows, bei denen ein Herausgeber Inhalte veröffentlicht, ist dies eine sinnvolle Integration: Der Veröffentlichungs-Webhook aktualisiert den KV und löst die Bereinigung aus.
Stale-while-revalidate mit waitUntil
Das Stale-While-Revalidate-Muster stellt potenziell veraltete Inhalte bereit und löst gleichzeitig eine Hintergrundaktualisierung aus. Im Workers-Kontext ermöglicht ctx.waitUntil() die Ausführung asynchroner Arbeit, nachdem die Antwort an den Client gesendet wurde.
Typische Implementierung: Arbeiter liest den aktuellen KV-Wert und serviert sofort. Gleichzeitig löst es über ctx.waitUntil() eine Funktion aus, die prüft, ob der Wert aktualisiert werden muss – zum Beispiel durch Abfrage einer Quelle – und den neuen Wert bei Bedarf in den KV schreibt. Der Client erhält die Antwort, ohne auf das Update zu warten. Abhängig davon, wann die Weitergabe abgeschlossen ist, erhält die nächste Anfrage möglicherweise bereits den neuen Wert.
Der Kompromiss liegt auf der Hand: Sie tauschen keine Latenz für den Client gegen ein Zeitfenster, in dem veraltete Inhalte bereitgestellt werden können. Für die meisten Inhalts-Caching-Szenarien ist dieser Kompromiss akzeptabel. Für Daten, bei denen Veraltung betriebliche Konsequenzen hat – Preise, Lagerverfügbarkeit, Zugriffsberechtigungen – ist der Standard nicht geeignet.
TTL als Ersatz für die explizite Invalidierung
Für Fälle, in denen eine präzise Invalidierung nicht erforderlich ist, ist TTL der einfachste Mechanismus. KV unterstützt expirationTtl (Sekunden in der Zukunft) und expiration (Unix-Zeitstempel), die zum Zeitpunkt des Schreibens definiert waren.
Ein Feature-Flag mit einer TTL von 60 Sekunden läuft automatisch ab. Die nächste Anfrage nach Ablauf ruft den zentralen Speicher ab und gibt den neuesten Wert zurück – oder einen leeren Wert, den der Worker als „Flag deaktiviert“ interpretieren kann. Sie müssen den Status nicht explizit löschen oder verfolgen.
Bei Inhalten mit vorhersehbarer Aktualisierungshäufigkeit entfällt durch die auf den Aktualisierungszyklus abgestimmte TTL die Notwendigkeit einer aktiven Invalidierung. Ein jedes Mal generierter Bericht kann eine TTL von 3600 Sekunden haben. Der älteste mögliche Wert, den ein Benutzer sehen kann, beträgt ungefähr eine Stunde. Dabei handelt es sich um eine Entwurfsentscheidung und nicht um einen Konsistenzfehler.
Was nicht geht: sofort löschen und neu schreiben
Ein Muster, das häufig auftritt und das Problem nicht löst, besteht darin, den alten Schlüssel zu löschen und nacheinander den neuen zu schreiben. Dadurch wird das Inkonsistenzfenster nicht beseitigt – die beiden Vorgänge werden unabhängig voneinander an die PoPs weitergegeben. Ein PoP kann den Löschvorgang empfangen, aber den neuen Schreibvorgang noch nicht erhalten haben, und während dieses Fensters wird für diesen Schlüssel „Nicht gefunden“ zurückgegeben. Abhängig davon, wie der Worker diesen Fall behandelt, kann dies zu einem Fehler oder einem unerwarteten Fallback führen.
Beim Löschen und Neuschreiben gibt es doppelt so viele Weitergabevorgänge und dreimal so viele mögliche Zwischenzustände: alt, nicht gefunden, neu. Versionierte oder TTL-Schlüssel sind immer vorzuziehen.
Wie letztendliche Konsistenz das Design verändert
Das gesündeste Muster für die Arbeit mit KV besteht darin, eventuelle Konsistenz als ein Merkmal des Systems zu akzeptieren und nicht als eine Einschränkung, die umgangen werden muss. Das bedeutet, Abläufe zu entwerfen, bei denen ein Inkonsistenzfenster von bis zu 60 Sekunden tolerierbar ist, und andere Tools zu verwenden, wenn dies nicht der Fall ist.
Für Daten, die eine sofortige Konsistenz über alle PoPs hinweg erfordern, ist KV nicht das richtige Werkzeug. D1 mit Messwerten, die an das Primärobjekt gerichtet sind, oder dauerhafte Objekte für den Status mit serialisiertem Zugriff decken Fälle ab, in denen das KV-Eventualkonsistenzmodell nicht funktioniert.
Eine elegante Invalidierung in KV muss nicht dringend erfolgen, da das Systemdesign so konzipiert ist, dass es das Ausbreitungsfenster toleriert. Jeder Versuch, sofortige Konsistenz zu erzwingen, wirkt sich gegen die Architektur aus, nicht mit ihr.
Lesen Sie auch
- Cloudflare KV: Was bedeutet global verteilt, wenn Sie schreiben müssen
- KV für Ratenbegrenzung, Feature-Flags und verteilte Konfiguration: wo es funktioniert und wo es kaputt geht
- KV in der Produktion: die Muster, die am Anfang funktionieren und die täuschen
- KV vs. R2 vs. Cache-API: Wann die einzelnen Cloudflare-Speicherebenen verwendet werden sollten
- Was nur Arbeiter tun, was nur Seiten tun und wo sich die beiden treffen
- Cloudflare D1: Die SQLite-Datenbank am Rande – und warum „Edge“ nicht das bedeutet, was es scheint
