Skip to content

Commit feacb44

Browse files
authored
Add debug logging to default cache handler and "use cache" wrapper (#77827)
When the env variable `NEXT_PRIVATE_DEBUG_CACHE` is defined (see https://nextjs.org/docs/app/building-your-application/data-fetching/incremental-static-regeneration#verifying-correct-production-behavior), the default cache handler and the `"use cache"` wrapper will now emit debug logs. Note: This variable can already be used for `"use cache"` debug logging when deploying to Vercel.
1 parent 6d537d3 commit feacb44

File tree

3 files changed

+75
-13
lines changed

3 files changed

+75
-13
lines changed

packages/next/src/server/lib/cache-handlers/default.ts

+36-3
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,23 @@ const memoryCache = new LRUCache<PrivateCacheEntry>(
4141
)
4242
const pendingSets = new Map<string, Promise<void>>()
4343

44+
const debug = process.env.NEXT_PRIVATE_DEBUG_CACHE
45+
? console.debug.bind(console, 'DefaultCacheHandler:')
46+
: undefined
47+
4448
const DefaultCacheHandler: CacheHandlerV2 = {
4549
async get(cacheKey) {
46-
await pendingSets.get(cacheKey)
50+
const pendingPromise = pendingSets.get(cacheKey)
51+
52+
if (pendingPromise) {
53+
debug?.('get', cacheKey, 'pending')
54+
await pendingPromise
55+
}
4756

4857
const privateEntry = memoryCache.get(cacheKey)
4958

5059
if (!privateEntry) {
60+
debug?.('get', cacheKey, 'not found')
5161
return undefined
5262
}
5363

@@ -59,22 +69,35 @@ const DefaultCacheHandler: CacheHandlerV2 = {
5969
// In-memory caches should expire after revalidate time because it is
6070
// unlikely that a new entry will be able to be used before it is dropped
6171
// from the cache.
72+
debug?.('get', cacheKey, 'expired')
73+
6274
return undefined
6375
}
6476

6577
if (isStale(entry.tags, entry.timestamp)) {
78+
debug?.('get', cacheKey, 'had stale tag')
79+
6680
return undefined
6781
}
6882
const [returnStream, newSaved] = entry.value.tee()
6983
entry.value = newSaved
7084

85+
debug?.('get', cacheKey, 'found', {
86+
tags: entry.tags,
87+
timestamp: entry.timestamp,
88+
revalidate: entry.revalidate,
89+
expire: entry.expire,
90+
})
91+
7192
return {
7293
...entry,
7394
value: returnStream,
7495
}
7596
},
7697

7798
async set(cacheKey, pendingEntry) {
99+
debug?.('set', cacheKey, 'start')
100+
78101
let resolvePending: () => void = () => {}
79102
const pendingPromise = new Promise<void>((resolve) => {
80103
resolvePending = resolve
@@ -100,8 +123,11 @@ const DefaultCacheHandler: CacheHandlerV2 = {
100123
errorRetryCount: 0,
101124
size,
102125
})
103-
} catch {
126+
127+
debug?.('set', cacheKey, 'done')
128+
} catch (err) {
104129
// TODO: store partial buffer with error after we retry 3 times
130+
debug?.('set', cacheKey, 'failed', err)
105131
} finally {
106132
resolvePending()
107133
pendingSets.delete(cacheKey)
@@ -113,11 +139,18 @@ const DefaultCacheHandler: CacheHandlerV2 = {
113139
},
114140

115141
async getExpiration(...tags) {
116-
return Math.max(...tags.map((tag) => tagsManifest.get(tag) ?? 0))
142+
const expiration = Math.max(
143+
...tags.map((tag) => tagsManifest.get(tag) ?? 0)
144+
)
145+
146+
debug?.('getExpiration', { tags, expiration })
147+
148+
return expiration
117149
},
118150

119151
async expireTags(...tags) {
120152
const timestamp = Math.round(performance.timeOrigin + performance.now())
153+
debug?.('expireTags', { tags, timestamp })
121154

122155
for (const tag of tags) {
123156
// TODO: update file-system-cache?

packages/next/src/server/use-cache/handlers.ts

+10-10
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const debug = process.env.NEXT_PRIVATE_DEBUG_CACHE
55
? (message: string, ...args: any[]) => {
66
console.log(`use-cache: ${message}`, ...args)
77
}
8-
: () => {}
8+
: undefined
99

1010
const handlersSymbol = Symbol.for('@next/cache-handlers')
1111
const handlersMapSymbol = Symbol.for('@next/cache-handlers-map')
@@ -32,40 +32,40 @@ const reference: typeof globalThis & {
3232
export function initializeCacheHandlers(): boolean {
3333
// If the cache handlers have already been initialized, don't do it again.
3434
if (reference[handlersMapSymbol]) {
35-
debug('cache handlers already initialized')
35+
debug?.('cache handlers already initialized')
3636
return false
3737
}
3838

39-
debug('initializing cache handlers')
39+
debug?.('initializing cache handlers')
4040
reference[handlersMapSymbol] = new Map<string, CacheHandlerCompat>()
4141

4242
// Initialize the cache from the symbol contents first.
4343
if (reference[handlersSymbol]) {
4444
let fallback: CacheHandlerCompat
4545
if (reference[handlersSymbol].DefaultCache) {
46-
debug('setting "default" cache handler from symbol')
46+
debug?.('setting "default" cache handler from symbol')
4747
fallback = reference[handlersSymbol].DefaultCache
4848
} else {
49-
debug('setting "default" cache handler from default')
49+
debug?.('setting "default" cache handler from default')
5050
fallback = DefaultCacheHandler
5151
}
5252

5353
reference[handlersMapSymbol].set('default', fallback)
5454

5555
if (reference[handlersSymbol].RemoteCache) {
56-
debug('setting "remote" cache handler from symbol')
56+
debug?.('setting "remote" cache handler from symbol')
5757
reference[handlersMapSymbol].set(
5858
'remote',
5959
reference[handlersSymbol].RemoteCache
6060
)
6161
} else {
62-
debug('setting "remote" cache handler from default')
62+
debug?.('setting "remote" cache handler from default')
6363
reference[handlersMapSymbol].set('remote', fallback)
6464
}
6565
} else {
66-
debug('setting "default" cache handler from default')
66+
debug?.('setting "default" cache handler from default')
6767
reference[handlersMapSymbol].set('default', DefaultCacheHandler)
68-
debug('setting "remote" cache handler from default')
68+
debug?.('setting "remote" cache handler from default')
6969
reference[handlersMapSymbol].set('remote', DefaultCacheHandler)
7070
}
7171

@@ -135,7 +135,7 @@ export function setCacheHandler(
135135
throw new Error('Cache handlers not initialized')
136136
}
137137

138-
debug('setting cache handler for "%s"', kind)
138+
debug?.('setting cache handler for "%s"', kind)
139139
reference[handlersMapSymbol].set(kind, cacheHandler)
140140
reference[handlersSetSymbol].add(cacheHandler)
141141
}

packages/next/src/server/use-cache/use-cache-wrapper.ts

+29
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ export interface UseCachePageComponentProps {
6666

6767
const isEdgeRuntime = process.env.NEXT_RUNTIME === 'edge'
6868

69+
const debug = process.env.NEXT_PRIVATE_DEBUG_CACHE
70+
? console.debug.bind(console, 'use-cache:')
71+
: undefined
72+
6973
function generateCacheEntry(
7074
workStore: WorkStore,
7175
outerWorkUnitStore: WorkUnitStore | undefined,
@@ -716,6 +720,7 @@ export function cache(
716720
entry &&
717721
(await shouldDiscardCacheEntry(entry, workStore, implicitTags))
718722
) {
723+
debug?.('discarding stale entry', serializedCacheKey)
719724
entry = undefined
720725
}
721726

@@ -756,6 +761,19 @@ export function cache(
756761
// Note: It is important that we await at least once before this because it lets us
757762
// pop out of any stack specific contexts as well - aka "Sync" Local Storage.
758763

764+
if (entry) {
765+
if (currentTime > entry.timestamp + entry.expire * 1000) {
766+
debug?.('entry is expired', serializedCacheKey)
767+
}
768+
769+
if (
770+
workStore.isStaticGeneration &&
771+
currentTime > entry.timestamp + entry.revalidate * 1000
772+
) {
773+
debug?.('static generation, entry is stale', serializedCacheKey)
774+
}
775+
}
776+
759777
const [newStream, pendingCacheEntry] = await generateCacheEntry(
760778
workStore,
761779
workUnitStore,
@@ -942,6 +960,13 @@ async function shouldDiscardCacheEntry(
942960
// If the cache entry was created before any of the implicit tags were
943961
// revalidated last, we also need to discard it.
944962
if (entry.timestamp <= (await implicitTags.expiration)) {
963+
debug?.(
964+
'entry was created at',
965+
entry.timestamp,
966+
'before implicit tags were revalidated at',
967+
implicitTags.expiration
968+
)
969+
945970
return true
946971
}
947972

@@ -962,13 +987,17 @@ function isRecentlyRevalidatedTag(tag: string, workStore: WorkStore): boolean {
962987

963988
// Was the tag previously revalidated (e.g. by a redirecting server action)?
964989
if (previouslyRevalidatedTags.includes(tag)) {
990+
debug?.('tag', tag, 'was previously revalidated')
991+
965992
return true
966993
}
967994

968995
// It could also have been revalidated by the currently running server action.
969996
// In this case the revalidation might not have been propagated to the cache
970997
// handler yet, so we read it from the pending tags in the work store.
971998
if (pendingRevalidatedTags?.includes(tag)) {
999+
debug?.('tag', tag, 'was just revalidated')
1000+
9721001
return true
9731002
}
9741003

0 commit comments

Comments
 (0)