Skip to content

Commit 7e232c5

Browse files
authored
Cache in CacheScopeStore in use cache (#71221)
Implements deduping of "use cache" entries using CacheScopeStore. This is required for dynamic I/O. We also have the option to use this for dynamic runtime deduping too, rather than going to the underlying store and ensuring consistency within the request. There are some negative tradeoffs with this approach though that it might not be worth it for dynamic requests. For now, it's up to the Cache Handler to do deduping if it wants to. I implemented this in the default cache handler. I tested this with a blank cache implementation since the existing cache is microtasky. Since a working cache is required for other tests and should be the default, I reverted the blank cache. So this doesn't have the full test coverage yet in practice. Once we land configurable caches we can use a blank cache in the newly added tests to get better coverage. I had to make the cache scope external since it's now shared with RSC layer.
1 parent d88c60d commit 7e232c5

File tree

16 files changed

+522
-103
lines changed

16 files changed

+522
-103
lines changed

packages/next/src/export/worker.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ import {
4646
type FallbackRouteParams,
4747
} from '../server/request/fallback-params'
4848
import { needsExperimentalReact } from '../lib/needs-experimental-react'
49-
import { runWithCacheScope } from '../server/async-storage/cache-scope'
49+
import { runWithCacheScope } from '../server/async-storage/cache-scope.external'
5050
import type { AppRouteRouteModule } from '../server/route-modules/app-route/module.compiled'
5151

5252
const envConfig = require('../shared/lib/runtime-config.external')
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import type { CacheScopeStore } from './cache-scope.external'
2+
3+
import { createAsyncLocalStorage } from '../app-render/async-local-storage'
4+
5+
export const cacheScopeAsyncLocalStorage =
6+
createAsyncLocalStorage<CacheScopeStore>()

packages/next/src/server/async-storage/cache-scope.ts renamed to packages/next/src/server/async-storage/cache-scope.external.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
import { createAsyncLocalStorage } from '../app-render/async-local-storage'
1+
import { cacheScopeAsyncLocalStorage } from './cache-scope-instance' with { 'turbopack-transition': 'next-shared' }
22

33
export interface CacheScopeStore {
4-
cache?: Map<string, any>
4+
cache: Map<string, any>
55
}
66

7-
export const cacheScopeAsyncLocalStorage =
8-
createAsyncLocalStorage<CacheScopeStore>()
7+
export { cacheScopeAsyncLocalStorage }
98

109
/**
1110
* For dynamic IO handling we want to have a scoped memory

packages/next/src/server/base-server.ts

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -172,10 +172,7 @@ import { FallbackMode, parseFallbackField } from '../lib/fallback'
172172
import { toResponseCacheEntry } from './response-cache/utils'
173173
import { scheduleOnNextTick } from '../lib/scheduler'
174174
import { PrefetchCacheScopes } from './lib/prefetch-cache-scopes'
175-
import {
176-
runWithCacheScope,
177-
type CacheScopeStore,
178-
} from './async-storage/cache-scope'
175+
import { runWithCacheScope } from './async-storage/cache-scope.external'
179176

180177
export type FindComponentsResult = {
181178
components: LoadComponentsReturnType
@@ -3039,10 +3036,8 @@ export default abstract class Server<
30393036
responseGenerator = async (
30403037
...args: Parameters<typeof responseGenerator>
30413038
): ReturnType<typeof responseGenerator> => {
3042-
let cache: CacheScopeStore['cache'] | undefined
3043-
30443039
if (this.renderOpts.dev) {
3045-
cache = this.prefetchCacheScopesDev.get(urlPathname)
3040+
let cache = this.prefetchCacheScopesDev.get(urlPathname)
30463041

30473042
// we need to seed the prefetch cache scope in dev
30483043
// since we did not have a prefetch cache available
@@ -3066,19 +3061,21 @@ export default abstract class Server<
30663061
delete req.headers[RSC_HEADER]
30673062
delete req.headers[NEXT_ROUTER_PREFETCH_HEADER]
30683063
}
3069-
}
30703064

3071-
return runWithCacheScope({ cache }, () =>
3072-
originalResponseGenerator(...args)
3073-
).finally(() => {
3074-
if (this.renderOpts.dev) {
3075-
if (isPrefetchRSCRequest) {
3076-
this.prefetchCacheScopesDev.set(urlPathname, cache)
3077-
} else {
3078-
this.prefetchCacheScopesDev.del(urlPathname)
3079-
}
3065+
if (cache) {
3066+
return runWithCacheScope({ cache }, () =>
3067+
originalResponseGenerator(...args)
3068+
).finally(() => {
3069+
if (isPrefetchRSCRequest) {
3070+
this.prefetchCacheScopesDev.set(urlPathname, cache)
3071+
} else {
3072+
this.prefetchCacheScopesDev.del(urlPathname)
3073+
}
3074+
})
30803075
}
3081-
})
3076+
}
3077+
3078+
return originalResponseGenerator(...args)
30823079
}
30833080
}
30843081

packages/next/src/server/lib/incremental-cache/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
import type { Revalidate } from '../revalidate'
1111
import type { DeepReadonly } from '../../../shared/lib/deep-readonly'
1212

13-
import { cacheScopeAsyncLocalStorage } from '../../async-storage/cache-scope'
13+
import { cacheScopeAsyncLocalStorage } from '../../async-storage/cache-scope.external'
1414
import FetchCache from './fetch-cache'
1515
import FileSystemCache from './file-system-cache'
1616
import { normalizePagePath } from '../../../shared/lib/page-path/normalize-page-path'
@@ -414,7 +414,7 @@ export class IncrementalCache implements IncrementalCacheType {
414414
if (this.hasDynamicIO && ctx.kind === IncrementalCacheKind.FETCH) {
415415
const cacheScope = cacheScopeAsyncLocalStorage.getStore()
416416

417-
if (cacheScope?.cache) {
417+
if (cacheScope) {
418418
const memoryCacheData = cacheScope.cache.get(cacheKey)
419419

420420
if (memoryCacheData?.kind === CachedRouteKind.FETCH) {
@@ -538,7 +538,7 @@ export class IncrementalCache implements IncrementalCacheType {
538538
if (this.hasDynamicIO && data?.kind === CachedRouteKind.FETCH) {
539539
const cacheScope = cacheScopeAsyncLocalStorage.getStore()
540540

541-
if (cacheScope?.cache) {
541+
if (cacheScope) {
542542
cacheScope.cache.set(pathname, data)
543543
}
544544
}

packages/next/src/server/lib/prefetch-cache-scopes.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { CacheScopeStore } from '../async-storage/cache-scope'
1+
import type { CacheScopeStore } from '../async-storage/cache-scope.external'
22

33
export class PrefetchCacheScopes {
44
private cacheScopes = new Map<

0 commit comments

Comments
 (0)