Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/lovely-bulldogs-dress.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"next": patch
---

Always pass implicit/soft tags into the `CacheHandler.get` method
27 changes: 14 additions & 13 deletions packages/next/src/server/lib/cache-handlers/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,39 +77,40 @@ 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<undefined | CacheEntry>
get(cacheKey: string, softTags: string[]): Promise<undefined | CacheEntry>

/**
* 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.
*/
set(cacheKey: string, pendingEntry: Promise<CacheEntry>): Promise<void>

/**
* 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<void>

/**
* 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<Timestamp>

/**
* 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<void>
}
Expand Down
13 changes: 4 additions & 9 deletions packages/next/src/server/use-cache/use-cache-wrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 ?? []
Expand Down
6 changes: 3 additions & 3 deletions test/e2e/app-dir/use-cache-custom-handler/handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const cacheHandler = {
cacheKey,
JSON.stringify(softTags)
)
return defaultCacheHandler.get(cacheKey)
return defaultCacheHandler.get(cacheKey, softTags)
},

async set(cacheKey, pendingEntry) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Loading