Wie der regionale Cache von OpenNext die CPU von Workers bei jedem Cache-Treffer reduziert
Next.js ist standardmäßig für die Ausführung auf Vercel konzipiert. Es erwartet ein Dateisystem, einen Bildoptimierungsdienst und ein bestimmtes Laufzeitmodell. Um Next.js woanders auszuführen, z. B. auf Cloudflare Workers, benötigen Sie einen Adapter, der diese Erwartungen an die Primitiven der Zielplattform anpasst.
OpenNext ist das Open-Source-Projekt, das genau das tut. Es begann als Next.js-Adapter für AWS Lambda und liefert jetzt auch offizielle Adapter für Cloudflare und Netlify. Das Paket, das uns hier interessiert, ist @opennextjs/cloudflare, mit dem Sie eine Next.js-App auf Cloudflare Workers bereitstellen und die Caching-Schicht von Next.js mit den Speicherprimitiven von Cloudflare verbinden können.
Wenn Sie die von OpenNext empfohlene Cloudflare-Einrichtung befolgt haben, verbindet Ihre open-next.config.ts zwei dieser Primitiven. Workers KV speichert den gecachten HTML-Code Ihrer vorgerenderten Seiten. D1 speichert die Cache-Tag-Metadaten, die revalidateTag() schreibt. (Standardmäßig verwendet defineCloudflareConfig() "dummy"-No-Op-Caches; KV und D1 sind die empfohlenen Ersetzungen, die Sie selbst einbinden.)
Sowohl KV als auch D1 sind entfernte Dienste. Beide verbrauchen bei jeder Anfrage echte CPU-Zeit, auch wenn sich die Seite tagelang nicht geändert hat. withRegionalCache ist der Wrapper, der das behebt.
Was es ist
withRegionalCache wird mit @opennextjs/cloudflare als Überschreibung geliefert, die Sie um Ihre bestehende inkrementelle Cache-Implementierung wickeln. Es ist eine Änderung mit einem Import und einer Umhüllung in open-next.config.ts.
import { defineCloudflareConfig } from "@opennextjs/cloudflare";
import kvIncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/kv-incremental-cache";
import { withRegionalCache } from "@opennextjs/cloudflare/overrides/incremental-cache/regional-cache";
import d1TagCache from "@opennextjs/cloudflare/overrides/tag-cache/d1-next-tag-cache";
export default defineCloudflareConfig({
incrementalCache: withRegionalCache(kvIncrementalCache, {
mode: "long-lived",
bypassTagCacheOnCacheHit: true,
}),
tagCache: d1TagCache,
});Es funktioniert genauso mit r2IncrementalCache oder staticAssetsIncrementalCache, wenn Sie diese als Backends gewählt haben. Der Wrapper ist unabhängig davon, welchen zugrunde liegenden Cache Sie verwenden.
Was es tut
Zwei Dinge, die beide verständlich sind.
1. Eine Workers Cache API-Schicht vor KV
Das erste Verhalten ist eine regionale Cache-Schicht, die caches.default, die Workers Cache API, verwendet. Die Cache API ist lokal für jedes Cloudflare-Rechenzentrum. Sie ist nicht global repliziert, aber das ist der Sinn der Sache. Lesevorgänge von caches.default sind in Bezug auf die CPU praktisch kostenlos und dauern 1 bis 5 ms.
Wenn also eine Anfrage für eine gecachte Seite eingeht, prüft der Worker zuerst die Cache API des lokalen Rechenzentrums. Wenn der Eintrag vorhanden ist, wird er sofort zurückgegeben. Wenn nicht, greift er auf KV zurück (ein globaler Lesezugriff von ca. 30 bis 100 ms), füllt die lokale Cache API für das nächste Mal und gibt die Antwort zurück.
Die Option mode: "long-lived" hält regionale Einträge standardmäßig bis zu 30 Minuten vor, was gut für ISR/SSG-Seiten funktioniert.
2. Optionaler D1-Tag-Cache-Umgehung bei Treffern
Das zweite Verhalten ist optional: bypassTagCacheOnCacheHit: true.
Standardmäßig fragt jede Anfrage an eine gecachte Seite auch D1 ab, um zu prüfen, ob eines der Revalidierungs-Tags dieser Seite ungültig geworden ist. D1-Lesevorgänge dauern ca. 30 bis 200 ms und verbrauchen CPU für die Abfrageeinrichtung und die Ergebnisinterpretation.
Mit aktivierter Umgehung überspringt der Worker diesen D1-Roundtrip bei Cache-Treffern. Der Kompromiss: Wenn Sie revalidateTag() aufrufen, kann der regionale Cache weiterhin die vorherige Version einer Seite ausliefern, bis ihr regionaler Eintrag abläuft. Für Inhalte, die sich nicht von Minute zu Minute ändern, ist dies für Benutzer unsichtbar.
Wie es hilft
Ohne den Wrapper sieht ein typischer "Cache-Treffer" auf dem Worker wie folgt aus:
- Isolierung aufwecken.
- D1 auf ungültige Tags für diese URL abfragen.
- Den gecachten HTML-Code aus KV lesen.
- Die Antwort erstellen und zurückgeben.
Zwei entfernte Roundtrips und sinnvolle CPU-Arbeit bei jeder einzelnen Anfrage.
Mit dem Wrapper:
- Isolierung aufwecken.
- Aus der lokalen Cache API lesen.
- Zurückgeben.
Gleiches Ergebnis aus Benutzersicht. Viel weniger Arbeit für den Worker.
Bei einer kleinen Inhaltsseite mit etwa 33.000 Anfragen/Tag sank die mittlere CPU pro Anfrage von 668 ms auf ca. 40 ms nach der Aktivierung, eine Reduzierung um etwa 94 %. Die gesamte CPU-Zeit, die gegen die Cloudflare-Gebühr von 0,02 $ pro Million CPU-ms abgerechnet wurde, sank von ca. 22 Mio. ms/Tag auf ca. 1,3 Mio. ms/Tag. Die Form der Kostenkurve im Abrechnungs-Dashboard änderte sich am selben Tag, an dem das Deployment erfolgte.
Die Einsparungen skalieren linear mit dem Traffic. Je geschäftiger die Website, desto größer die Lücke zwischen "zwei entfernte Roundtrips pro Anfrage" und "ein lokaler Cache API-Lesevorgang pro Anfrage".
Wann man es verwenden sollte
Wenn Ihre OpenNext-Konfiguration kvIncrementalCache mit d1TagCache verwendet (die standardmäßig empfohlene Einrichtung für Inhaltsseiten auf Cloudflare), aktivieren Sie withRegionalCache mit bypassTagCacheOnCacheHit: true. Blogs, Dokumentationsseiten, Marketingseiten und ähnliche, meist statische Workloads profitieren sofort, und der Kompromiss bei der Aktualität ist unsichtbar.
Wenn Sie eine Anwendung ausführen, bei der die Aktualität in Echtzeit wichtig ist (Preise, Lagerbestand, Chat), behalten Sie bypassTagCacheOnCacheHit: false bei. Sie erhalten weiterhin die regionale Cache API-Schicht, schnellere KV-Lesevorgänge, geringere CPU pro Anfrage, ohne das Aktualitätsfenster.
Es ist ein Fünf-Zeilen-Diff. Die Art von Änderung, die sich am ersten Tag selbst bezahlt macht.