Come la cache regionale di OpenNext riduce la CPU dei Workers ad ogni cache hit
Next.js è progettato per funzionare su Vercel per impostazione predefinita. Si aspetta un filesystem, un servizio di ottimizzazione delle immagini e un modello di runtime specifico. Per eseguire Next.js altrove, come Cloudflare Workers, è necessario un adattatore che colleghi queste aspettative alle primitive della piattaforma di destinazione.
OpenNext è il progetto open-source che fa esattamente questo. È nato come adattatore Next.js per AWS Lambda e ora fornisce adattatori ufficiali anche per Cloudflare e Netlify. Il pacchetto che ci interessa qui è @opennextjs/cloudflare, che consente di distribuire un'app Next.js su Cloudflare Workers e collega il livello di cache di Next.js alle primitive di storage di Cloudflare.
Se hai seguito la configurazione Cloudflare consigliata da OpenNext, il tuo open-next.config.ts collega due di queste primitive. Workers KV memorizza l'HTML memorizzato nella cache delle tue pagine pre-renderizzate. D1 memorizza i metadati dei tag di cache che revalidateTag() scrive. (Per impostazione predefinita, defineCloudflareConfig() utilizza cache "dummy" no-op; KV e D1 sono i sostituti consigliati che colleghi tu stesso).
Sia KV che D1 sono servizi remoti. Entrambi consumano CPU reale ad ogni richiesta, anche quando la pagina non è cambiata da giorni. withRegionalCache è il wrapper che risolve questo problema.
Cos'è
withRegionalCache viene fornito con @opennextjs/cloudflare come override da applicare attorno alla tua attuale implementazione della cache incrementale. È una modifica di un'importazione e un wrapping 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,
});Funziona allo stesso modo con r2IncrementalCache o staticAssetsIncrementalCache se questi sono i backend che hai scelto. Il wrapper è indipendente dalla cache sottostante che utilizzi.
Cosa fa
Due cose, entrambe degne di nota.
1. Un livello API Cache Workers davanti a KV
Il primo comportamento è un livello di cache regionale che utilizza caches.default, l'API Cache di Workers. L'API Cache è locale a ogni data center di Cloudflare. Non è replicata a livello globale, ma questo è il punto. Le letture da caches.default sono essenzialmente gratuite in termini di CPU e terminano in 1-5 ms.
Quindi, quando arriva una richiesta per una pagina memorizzata nella cache, il worker controlla prima l'API Cache del data center locale. Se la voce è presente, la restituisce immediatamente. In caso contrario, ripiega su KV (una lettura globale di circa 30-100 ms), popola l'API Cache locale per la prossima volta e restituisce la risposta.
L'opzione mode: "long-lived" mantiene le voci regionali attive fino a 30 minuti per impostazione predefinita, il che funziona bene per le pagine ISR/SSG.
2. Bypass opzionale della cache dei tag D1 in caso di hit
Il secondo comportamento è opzionale: bypassTagCacheOnCacheHit: true.
Per impostazione predefinita, ogni richiesta a una pagina memorizzata nella cache interroga anche D1 per verificare se uno dei tag di revalidazione di quella pagina è stato invalidato. Le letture D1 richiedono circa 30-200 ms di tempo effettivo e consumano CPU per la configurazione della query e l'analisi dei risultati.
Con il bypass abilitato, il worker salta questo viaggio di andata e ritorno D1 in caso di hit della cache. Il compromesso: se chiami revalidateTag(), la cache regionale potrebbe continuare a servire la versione precedente di una pagina fino alla scadenza della sua voce regionale. Per i contenuti che non cambiano di minuto in minuto, questo è invisibile agli utenti.
Come aiuta
Senza il wrapper, un tipico "cache hit" appare così sul worker:
- Avvia l'isolato.
- Interroga D1 per le invalidazioni dei tag su questo URL.
- Leggi l'HTML memorizzato nella cache da KV.
- Costruisci la risposta e restituisci.
Due round trip remoti e un lavoro significativo di CPU, su ogni singola richiesta.
Con il wrapper:
- Avvia l'isolato.
- Leggi dall'API Cache locale.
- Restituisci.
Stesso risultato dal punto di vista dell'utente. Molto meno lavoro per il worker.
Su un piccolo sito di contenuti che gestisce circa 33.000 richieste/giorno, la CPU mediana per richiesta è scesa da 668 ms a circa 40 ms dopo l'attivazione, una riduzione di circa il 94%. Il tempo totale di CPU fatturato contro i $0,02 per milione di ms di CPU di Cloudflare è passato da circa 22 milioni di ms/giorno a circa 1,3 milioni di ms/giorno. La forma della curva dei costi nel dashboard di fatturazione è cambiata lo stesso giorno in cui è stata effettuata la distribuzione.
I risparmi scalano linearmente con il traffico. Più il sito è trafficato, maggiore è il divario tra "due round trip remoti per richiesta" e "una lettura locale dell'API Cache per richiesta".
Quando usarlo
Se la tua configurazione OpenNext utilizza kvIncrementalCache con d1TagCache (la configurazione consigliata predefinita per i siti di contenuti su Cloudflare), attiva withRegionalCache con bypassTagCacheOnCacheHit: true. Blog, siti di documentazione, pagine di marketing e carichi di lavoro prevalentemente statici simili beneficiano immediatamente e il compromesso sulla freschezza è invisibile.
Se stai eseguendo un'app in cui la freschezza è importante in tempo reale (prezzi, inventario, chat), mantieni bypassTagCacheOnCacheHit: false. Ottieni comunque il livello dell'API Cache regionale, letture KV più veloci, meno CPU per richiesta, senza la finestra di obsolescenza.
È una modifica di cinque righe. Il tipo di cambiamento che si ripaga il primo giorno.