Skip to content

Commit e09d5b0

Browse files
authored
Track timestamp and expire time on CacheEntry (#71227)
Compute whether it's expired or stale based on those and the current time.
1 parent 605ba61 commit e09d5b0

File tree

1 file changed

+31
-7
lines changed

1 file changed

+31
-7
lines changed

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

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,16 @@ const isEdgeRuntime = process.env.NEXT_RUNTIME === 'edge'
3636

3737
type CacheEntry = {
3838
value: ReadableStream
39+
timestamp: number
3940
// In-memory caches are fragile and should not use stale-while-revalidate
4041
// semantics on the caches because it's not worth warming up an entry that's
4142
// likely going to get evicted before we get to use it anyway. However,
4243
// we also don't want to reuse a stale entry for too long so stale entries
4344
// should be considered expired/missing in such CacheHandlers.
44-
stale: boolean
45-
tags: string[]
4645
revalidate: number
46+
expire: number
47+
stale: number
48+
tags: string[]
4749
}
4850

4951
interface CacheHandler {
@@ -60,12 +62,22 @@ cacheHandlerMap.set('default', {
6062
// TODO: Implement proper caching.
6163
const entry = await defaultCacheStorage.get(cacheKey)
6264
if (entry !== undefined) {
65+
if (
66+
performance.timeOrigin + performance.now() >
67+
entry.timestamp + entry.revalidate * 1000
68+
) {
69+
// In memory caches should expire after revalidate time because it is unlikely that
70+
// a new entry will be able to be used before it is dropped from the cache.
71+
return undefined
72+
}
6373
const [returnStream, newSaved] = entry.value.tee()
6474
entry.value = newSaved
6575
return {
6676
value: returnStream,
67-
stale: false,
77+
timestamp: entry.timestamp,
6878
revalidate: entry.revalidate,
79+
expire: entry.revalidate,
80+
stale: entry.stale,
6981
tags: entry.tags,
7082
}
7183
}
@@ -217,6 +229,7 @@ async function collectResult(
217229
savedStream: ReadableStream,
218230
outerWorkUnitStore: WorkUnitStore | undefined,
219231
innerCacheStore: UseCacheStore,
232+
startTime: number,
220233
errors: Array<unknown> // This is a live array that gets pushed into.
221234
): Promise<CacheEntry> {
222235
// We create a buffered stream that collects all chunks until the end to
@@ -263,9 +276,11 @@ async function collectResult(
263276

264277
const entry = {
265278
value: bufferStream,
266-
stale: false, // TODO: rm
267-
tags: collectedTags === null ? [] : collectedTags,
279+
timestamp: startTime,
268280
revalidate: collectedRevalidate,
281+
expire: Infinity,
282+
stale: 0,
283+
tags: collectedTags === null ? [] : collectedTags,
269284
}
270285
// Propagate tags/revalidate to the parent context.
271286
propagateCacheLifeAndTags(outerWorkUnitStore, entry)
@@ -301,6 +316,8 @@ async function generateCacheEntryImpl(
301316
}
302317
)
303318

319+
// Track the timestamp when we started copmuting the result.
320+
const startTime = performance.timeOrigin + performance.now()
304321
// Invoke the inner function to load a new result.
305322
const result = fn.apply(null, args)
306323

@@ -326,6 +343,7 @@ async function generateCacheEntryImpl(
326343
savedStream,
327344
outerWorkUnitStore,
328345
innerCacheStore,
346+
startTime,
329347
errors
330348
)
331349

@@ -372,7 +390,13 @@ async function loadCacheEntry(
372390
implicitTags
373391
)
374392

375-
if (entry === undefined || (entry.stale && workStore.isStaticGeneration)) {
393+
const currentTime = performance.timeOrigin + performance.now()
394+
if (
395+
entry === undefined ||
396+
currentTime > entry.timestamp + entry.expire * 1000 ||
397+
(workStore.isStaticGeneration &&
398+
currentTime > entry.timestamp + entry.revalidate * 1000)
399+
) {
376400
// Miss. Generate a new result.
377401

378402
// If the cache entry is stale and we're prerendering, we don't want to use the
@@ -398,7 +422,7 @@ async function loadCacheEntry(
398422
} else {
399423
propagateCacheLifeAndTags(workUnitStore, entry)
400424

401-
if (entry.stale) {
425+
if (currentTime > entry.timestamp + entry.revalidate) {
402426
// If this is stale, and we're not in a prerender (i.e. this is dynamic render),
403427
// then we should warm up the cache with a fresh revalidated entry.
404428
const ignoredStream = await generateCacheEntry(

0 commit comments

Comments
 (0)