Summary
Next.js changed dev-mode cache behavior so that cacheMaxMemorySize: 0 and custom cache handlers no longer cause warm reloads to be treated as cold cache misses under Cache Components. The fix introduces a built-in in-memory front handler that fronts the configured (slow/remote) backing handler in dev only, so warm reads resolve in a microtask.
Before the fix, when Cache Components is enabled, next dev treated a 'use cache' value as a miss whenever the read didn't resolve right away. Two dev configurations triggered this even for cached entries:
cacheMaxMemorySize: 0 replaced the built-in default handler with a no-op stub
- Custom cache handlers with a slow or remote
get didn't return in time
Behavior
- Size-0 case: Dev now uses a real in-memory handler instead of the no-op stub. The
'use cache' wrapper forces a dynamic cache life for it (revalidate: 0, 5-minute expire) — same treatment private caches receive — so every read serves the stale entry and re-warms in the background. Also fixes the dev private handler, which was sized from cacheMaxMemorySize and degraded to the no-op stub when set to 0.
- Custom handler case: A new
TieredCacheHandler puts a fast built-in in-memory front in front of the user-configured backing. Warm reads serve from the front, writes go through to both tiers, and the front reconciles against the backing in the background (evicting front entries when the backing no longer has them, via an already-expired overwrite since the handler interface has no per-key delete).
- Dev-only handlers are gated on
process.env.__NEXT_DEV_SERVER; production is unchanged (cacheMaxMemorySize: 0 still caches nothing, configured handlers used directly).
- Dev-only handlers are kept out of the main registered handler set but merged in where tag operations iterate, so
revalidateTag still reaches them.
vinext implications
vinext's dev cache layer (see isr-cache.ts / cache-handler config) should consider:
- Whether vinext exhibits the same dev cold-miss behavior under the new
'use cache' semantics when cacheMaxMemorySize: 0 is configured or when a slow custom handler is used
- Whether to implement an equivalent dev-only front handler / tiered handler so warm reads in vinext dev don't surface as cold-cache misses
- Tag invalidation must continue to reach the dev front handler if we add one
This is dev-only parity but matters for users who configure cacheMaxMemorySize: 0 (e.g. to emulate deploy envs) or who plug in a remote cache handler.
Upstream
Related: #1936 (dev cache serve-until-expire), #1937 (persist use cache: private in dev)
Summary
Next.js changed dev-mode cache behavior so that
cacheMaxMemorySize: 0and custom cache handlers no longer cause warm reloads to be treated as cold cache misses under Cache Components. The fix introduces a built-in in-memory front handler that fronts the configured (slow/remote) backing handler in dev only, so warm reads resolve in a microtask.Before the fix, when Cache Components is enabled,
next devtreated a'use cache'value as a miss whenever the read didn't resolve right away. Two dev configurations triggered this even for cached entries:cacheMaxMemorySize: 0replaced the built-in default handler with a no-op stubgetdidn't return in timeBehavior
'use cache'wrapper forces a dynamic cache life for it (revalidate: 0, 5-minuteexpire) — same treatment private caches receive — so every read serves the stale entry and re-warms in the background. Also fixes the dev private handler, which was sized fromcacheMaxMemorySizeand degraded to the no-op stub when set to 0.TieredCacheHandlerputs a fast built-in in-memory front in front of the user-configured backing. Warm reads serve from the front, writes go through to both tiers, and the front reconciles against the backing in the background (evicting front entries when the backing no longer has them, via an already-expired overwrite since the handler interface has no per-key delete).process.env.__NEXT_DEV_SERVER; production is unchanged (cacheMaxMemorySize: 0still caches nothing, configured handlers used directly).revalidateTagstill reaches them.vinext implications
vinext's dev cache layer (see
isr-cache.ts/cache-handlerconfig) should consider:'use cache'semantics whencacheMaxMemorySize: 0is configured or when a slow custom handler is usedThis is dev-only parity but matters for users who configure
cacheMaxMemorySize: 0(e.g. to emulate deploy envs) or who plug in a remote cache handler.Upstream
cacheMaxMemorySize: 0and custom cache handlers fast in dev vercel/next.js#94784packages/next/src/server/use-cache/tiered-cache-handler.ts(new),packages/next/src/server/use-cache/handlers.ts,packages/next/src/server/use-cache/use-cache-wrapper.tstest/development/app-dir/use-cache-size-zero/,test/development/app-dir/use-cache-custom-handler-dev/Related: #1936 (dev cache serve-until-expire), #1937 (persist
use cache: privatein dev)