diff --git a/.changeset/two-paws-boil.md b/.changeset/two-paws-boil.md new file mode 100644 index 00000000..d49a39b6 --- /dev/null +++ b/.changeset/two-paws-boil.md @@ -0,0 +1,5 @@ +--- +"@opennextjs/cloudflare": patch +--- + +feat: prefix for kv cache keys diff --git a/packages/cloudflare/src/api/cloudflare-context.ts b/packages/cloudflare/src/api/cloudflare-context.ts index 27e11e05..45bf5fde 100644 --- a/packages/cloudflare/src/api/cloudflare-context.ts +++ b/packages/cloudflare/src/api/cloudflare-context.ts @@ -2,8 +2,10 @@ import type { Context, RunningCodeOptions } from "node:vm"; import type { GetPlatformProxyOptions } from "wrangler"; -import type { DOQueueHandler } from "./durable-objects/queue"; -import { DOShardedTagCache } from "./durable-objects/sharded-tag-cache"; +import type { DOQueueHandler } from "./durable-objects/queue.js"; +import type { DOShardedTagCache } from "./durable-objects/sharded-tag-cache.js"; +import type { PREFIX_ENV_NAME as KV_CACHE_PREFIX_ENV_NAME } from "./overrides/incremental-cache/kv-incremental-cache.js"; +import type { PREFIX_ENV_NAME as R2_CACHE_PREFIX_ENV_NAME } from "./overrides/incremental-cache/r2-incremental-cache.js"; declare global { interface CloudflareEnv { @@ -19,11 +21,13 @@ declare global { // KV used for the incremental cache NEXT_INC_CACHE_KV?: KVNamespace; + // Prefix used for the KV incremental cache key + [KV_CACHE_PREFIX_ENV_NAME]?: string; // R2 bucket used for the incremental cache NEXT_INC_CACHE_R2_BUCKET?: R2Bucket; // Prefix used for the R2 incremental cache bucket - NEXT_INC_CACHE_R2_PREFIX?: string; + [R2_CACHE_PREFIX_ENV_NAME]?: string; // D1 db used for the tag cache NEXT_TAG_CACHE_D1?: D1Database; diff --git a/packages/cloudflare/src/api/overrides/incremental-cache/kv-incremental-cache.ts b/packages/cloudflare/src/api/overrides/incremental-cache/kv-incremental-cache.ts index 4a3ed935..f6a275c0 100644 --- a/packages/cloudflare/src/api/overrides/incremental-cache/kv-incremental-cache.ts +++ b/packages/cloudflare/src/api/overrides/incremental-cache/kv-incremental-cache.ts @@ -1,30 +1,22 @@ -import { createHash } from "node:crypto"; - import { error } from "@opennextjs/aws/adapters/logger.js"; import type { CacheValue, IncrementalCache, WithLastModified } from "@opennextjs/aws/types/overrides.js"; import { IgnorableError } from "@opennextjs/aws/utils/error.js"; import { getCloudflareContext } from "../../cloudflare-context.js"; -import { debugCache, FALLBACK_BUILD_ID, IncrementalCacheEntry } from "../internal.js"; +import { computeCacheKey, debugCache, IncrementalCacheEntry } from "../internal.js"; export const NAME = "cf-kv-incremental-cache"; export const BINDING_NAME = "NEXT_INC_CACHE_KV"; -export type KeyOptions = { - isFetch?: boolean; - buildId?: string; -}; - -export function computeCacheKey(key: string, options: KeyOptions) { - const { isFetch = false, buildId = FALLBACK_BUILD_ID } = options; - const hash = createHash("sha256").update(key).digest("hex"); - return `${buildId}/${hash}.${isFetch ? "fetch" : "cache"}`.replace(/\/+/g, "/"); -} +export const PREFIX_ENV_NAME = "NEXT_INC_CACHE_KV_PREFIX"; /** * Open Next cache based on Cloudflare KV. * + * The prefix that the cache entries are stored under can be configured with the `NEXT_INC_CACHE_KV_PREFIX` + * environment variable, and defaults to `incremental-cache`. + * * Note: The class is instantiated outside of the request context. * The cloudflare context and process.env are not initialized yet * when the constructor is called. @@ -106,6 +98,7 @@ class KVIncrementalCache implements IncrementalCache { protected getKVKey(key: string, isFetch?: boolean): string { return computeCacheKey(key, { + prefix: getCloudflareContext().env[PREFIX_ENV_NAME], buildId: process.env.NEXT_BUILD_ID, isFetch, }); diff --git a/packages/cloudflare/src/api/overrides/incremental-cache/r2-incremental-cache.ts b/packages/cloudflare/src/api/overrides/incremental-cache/r2-incremental-cache.ts index 75a4c269..80c07faa 100644 --- a/packages/cloudflare/src/api/overrides/incremental-cache/r2-incremental-cache.ts +++ b/packages/cloudflare/src/api/overrides/incremental-cache/r2-incremental-cache.ts @@ -1,30 +1,15 @@ -import { createHash } from "node:crypto"; - import { error } from "@opennextjs/aws/adapters/logger.js"; import type { CacheValue, IncrementalCache, WithLastModified } from "@opennextjs/aws/types/overrides.js"; import { IgnorableError } from "@opennextjs/aws/utils/error.js"; import { getCloudflareContext } from "../../cloudflare-context.js"; -import { debugCache, FALLBACK_BUILD_ID } from "../internal.js"; +import { computeCacheKey, debugCache } from "../internal.js"; export const NAME = "cf-r2-incremental-cache"; export const BINDING_NAME = "NEXT_INC_CACHE_R2_BUCKET"; export const PREFIX_ENV_NAME = "NEXT_INC_CACHE_R2_PREFIX"; -export const DEFAULT_PREFIX = "incremental-cache"; - -export type KeyOptions = { - isFetch?: boolean; - directory?: string; - buildId?: string; -}; - -export function computeCacheKey(key: string, options: KeyOptions) { - const { isFetch = false, directory = DEFAULT_PREFIX, buildId = FALLBACK_BUILD_ID } = options; - const hash = createHash("sha256").update(key).digest("hex"); - return `${directory}/${buildId}/${hash}.${isFetch ? "fetch" : "cache"}`.replace(/\/+/g, "/"); -} /** * An instance of the Incremental Cache that uses an R2 bucket (`NEXT_INC_CACHE_R2_BUCKET`) as it's @@ -91,7 +76,7 @@ class R2IncrementalCache implements IncrementalCache { protected getR2Key(key: string, isFetch?: boolean): string { return computeCacheKey(key, { - directory: getCloudflareContext().env[PREFIX_ENV_NAME], + prefix: getCloudflareContext().env[PREFIX_ENV_NAME], buildId: process.env.NEXT_BUILD_ID, isFetch, }); diff --git a/packages/cloudflare/src/api/overrides/internal.ts b/packages/cloudflare/src/api/overrides/internal.ts index 7dd8c523..a253028c 100644 --- a/packages/cloudflare/src/api/overrides/internal.ts +++ b/packages/cloudflare/src/api/overrides/internal.ts @@ -1,3 +1,5 @@ +import { createHash } from "node:crypto"; + import { CacheValue } from "@opennextjs/aws/types/overrides.js"; export type IncrementalCacheEntry = { @@ -12,3 +14,17 @@ export const debugCache = (name: string, ...args: unknown[]) => { }; export const FALLBACK_BUILD_ID = "no-build-id"; + +export const DEFAULT_PREFIX = "incremental-cache"; + +export type KeyOptions = { + isFetch: boolean | undefined; + prefix: string | undefined; + buildId: string | undefined; +}; + +export function computeCacheKey(key: string, options: KeyOptions) { + const { isFetch = false, prefix = DEFAULT_PREFIX, buildId = FALLBACK_BUILD_ID } = options; + const hash = createHash("sha256").update(key).digest("hex"); + return `${prefix}/${buildId}/${hash}.${isFetch ? "fetch" : "cache"}`.replace(/\/+/g, "/"); +} diff --git a/packages/cloudflare/src/cli/commands/populate-cache.ts b/packages/cloudflare/src/cli/commands/populate-cache.ts index 04cd2684..dc48ad80 100644 --- a/packages/cloudflare/src/cli/commands/populate-cache.ts +++ b/packages/cloudflare/src/cli/commands/populate-cache.ts @@ -16,12 +16,11 @@ import { unstable_readConfig } from "wrangler"; import { BINDING_NAME as KV_CACHE_BINDING_NAME, - computeCacheKey as computeKVCacheKey, NAME as KV_CACHE_NAME, + PREFIX_ENV_NAME as KV_CACHE_PREFIX_ENV_NAME, } from "../../api/overrides/incremental-cache/kv-incremental-cache.js"; import { BINDING_NAME as R2_CACHE_BINDING_NAME, - computeCacheKey as computeR2CacheKey, NAME as R2_CACHE_NAME, PREFIX_ENV_NAME as R2_CACHE_PREFIX_ENV_NAME, } from "../../api/overrides/incremental-cache/r2-incremental-cache.js"; @@ -29,6 +28,7 @@ import { CACHE_DIR as STATIC_ASSETS_CACHE_DIR, NAME as STATIC_ASSETS_CACHE_NAME, } from "../../api/overrides/incremental-cache/static-assets-incremental-cache.js"; +import { computeCacheKey } from "../../api/overrides/internal.js"; import { BINDING_NAME as D1_TAG_BINDING_NAME, NAME as D1_TAG_NAME, @@ -113,8 +113,8 @@ function populateR2IncrementalCache( const assets = getCacheAssets(options); for (const { fullPath, key, buildId, isFetch } of tqdm(assets)) { - const cacheKey = computeR2CacheKey(key, { - directory: process.env[R2_CACHE_PREFIX_ENV_NAME], + const cacheKey = computeCacheKey(key, { + prefix: process.env[R2_CACHE_PREFIX_ENV_NAME], buildId, isFetch, }); @@ -146,7 +146,8 @@ function populateKVIncrementalCache( const assets = getCacheAssets(options); for (const { fullPath, key, buildId, isFetch } of tqdm(assets)) { - const cacheKey = computeKVCacheKey(key, { + const cacheKey = computeCacheKey(key, { + prefix: process.env[KV_CACHE_PREFIX_ENV_NAME], buildId, isFetch, });