diff --git a/.changeset/lovely-bulldogs-dress.md b/.changeset/lovely-bulldogs-dress.md new file mode 100644 index 000000000000..cf8a76baebdb --- /dev/null +++ b/.changeset/lovely-bulldogs-dress.md @@ -0,0 +1,5 @@ +--- +"next": patch +--- + +Always pass implicit/soft tags into the `CacheHandler.get` method diff --git a/packages/next/src/server/lib/cache-handlers/types.ts b/packages/next/src/server/lib/cache-handlers/types.ts index 8cab966d6902..8cbb91257224 100644 --- a/packages/next/src/server/lib/cache-handlers/types.ts +++ b/packages/next/src/server/lib/cache-handlers/types.ts @@ -77,14 +77,15 @@ export interface CacheHandler { export interface CacheHandlerV2 { /** - * Retrieve a cache entry for the given cache key, if available. + * Retrieve a cache entry for the given cache key, if available. Will return + * undefined if there's no valid entry, or if the given soft tags are stale. */ - get(cacheKey: string): Promise + get(cacheKey: string, softTags: string[]): Promise /** * Store a cache entry for the given cache key. When this is called, the entry * may still be pending, i.e. its value stream may still be written to. So it - * needs to be awaited first. If a `get` for the same cache key is called + * needs to be awaited first. If a `get` for the same cache key is called, * before the pending entry is complete, the cache handler must wait for the * `set` operation to finish, before returning the entry, instead of returning * undefined. @@ -92,24 +93,24 @@ export interface CacheHandlerV2 { set(cacheKey: string, pendingEntry: Promise): Promise /** - * Next.js will call this method periodically, but always before starting a - * new request. When working with a remote tags service, this method should - * communicate with the tags service to refresh the local tags manifest - * accordingly. + * This function may be called periodically, but always before starting a new + * request. If applicable, it should communicate with the tags service to + * refresh the local tags manifest accordingly. */ refreshTags(): Promise /** - * Next.js will call this method for each set of soft tags that are relevant - * at the start of a request. The result is the maximum timestamp of a - * revalidate event for the tags. Returns `0` if none of the tags were ever - * revalidated. + * This function is called for each set of soft tags that are relevant at the + * start of a request. The result is the maximum timestamp of a revalidate + * event for the tags. Returns `0` if none of the tags were ever revalidated. + * Returns `Infinity` if the soft tags are supposed to be passed into the + * `get` method instead to be checked for expiration. */ getExpiration(...tags: string[]): Promise /** - * Next.js will call this method when `revalidateTag` or `revalidatePath()` is - * called. It should update the tags manifest accordingly. + * This function is called when tags are revalidated/expired. If applicable, + * it should update the tags manifest accordingly. */ expireTags(...tags: string[]): Promise } diff --git a/packages/next/src/server/use-cache/use-cache-wrapper.ts b/packages/next/src/server/use-cache/use-cache-wrapper.ts index 6f7c21d51954..ad2fde25292f 100644 --- a/packages/next/src/server/use-cache/use-cache-wrapper.ts +++ b/packages/next/src/server/use-cache/use-cache-wrapper.ts @@ -744,15 +744,10 @@ export function cache( let entry = shouldForceRevalidate(workStore, workUnitStore) ? undefined - : 'getExpiration' in cacheHandler - ? await cacheHandler.get(serializedCacheKey) - : // Legacy cache handlers require implicit tags to be passed in, - // instead of checking their staleness here, as we do for modern - // cache handlers (see below). - await cacheHandler.get( - serializedCacheKey, - workUnitStore?.implicitTags?.tags ?? [] - ) + : await cacheHandler.get( + serializedCacheKey, + workUnitStore?.implicitTags?.tags ?? [] + ) if (entry) { const implicitTags = workUnitStore?.implicitTags?.tags ?? [] diff --git a/test/e2e/app-dir/use-cache-custom-handler/handler.js b/test/e2e/app-dir/use-cache-custom-handler/handler.js index 9aa97cb4d7dd..3ba1ab1ee69e 100644 --- a/test/e2e/app-dir/use-cache-custom-handler/handler.js +++ b/test/e2e/app-dir/use-cache-custom-handler/handler.js @@ -7,9 +7,9 @@ const defaultCacheHandler = * @type {import('next/dist/server/lib/cache-handlers/types').CacheHandlerV2} */ const cacheHandler = { - async get(cacheKey) { - console.log('ModernCustomCacheHandler::get', cacheKey) - return defaultCacheHandler.get(cacheKey) + async get(cacheKey, softTags) { + console.log('ModernCustomCacheHandler::get', cacheKey, softTags) + return defaultCacheHandler.get(cacheKey, softTags) }, async set(cacheKey, pendingEntry) { diff --git a/test/e2e/app-dir/use-cache-custom-handler/legacy-handler.js b/test/e2e/app-dir/use-cache-custom-handler/legacy-handler.js index b68700f46ac1..3244b7d68611 100644 --- a/test/e2e/app-dir/use-cache-custom-handler/legacy-handler.js +++ b/test/e2e/app-dir/use-cache-custom-handler/legacy-handler.js @@ -13,7 +13,7 @@ const cacheHandler = { cacheKey, JSON.stringify(softTags) ) - return defaultCacheHandler.get(cacheKey) + return defaultCacheHandler.get(cacheKey, softTags) }, async set(cacheKey, pendingEntry) { diff --git a/test/e2e/app-dir/use-cache-custom-handler/use-cache-custom-handler.test.ts b/test/e2e/app-dir/use-cache-custom-handler/use-cache-custom-handler.test.ts index a00608c50390..182116b1d4a0 100644 --- a/test/e2e/app-dir/use-cache-custom-handler/use-cache-custom-handler.test.ts +++ b/test/e2e/app-dir/use-cache-custom-handler/use-cache-custom-handler.test.ts @@ -28,7 +28,7 @@ describe('use-cache-custom-handler', () => { expect(cliOutput).toContain('ModernCustomCacheHandler::refreshTags') expect(next.cliOutput.slice(outputIndex)).toMatch( - /ModernCustomCacheHandler::get \["(development|[A-Za-z0-9_-]{21})","([0-9a-f]{2})+",\[\]\]/ + /ModernCustomCacheHandler::get \["(development|[A-Za-z0-9_-]{21})","([0-9a-f]{2})+",\[\]\] \[ '_N_T_\/layout', '_N_T_\/page', '_N_T_\/' \]/ ) expect(next.cliOutput.slice(outputIndex)).toMatch(