Fix inconsistent cache life/tags propagation for cache handler hits#91454
Fix inconsistent cache life/tags propagation for cache handler hits#91454unstubbable wants to merge 1 commit intohl/root-params-in-use-cachefrom
Conversation
|
Warning This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
This stack of pull requests is managed by Graphite. Learn more about stacking. |
Failing test suitesCommit: bd20bd2 | About building and testing Next.js
Expand output● Graceful Shutdown › production (standalone mode) › should not accept new requests during shutdown cleanup › should stop accepting new requests when shutting down |
Stats from current PR🔴 1 regression
📊 All Metrics📖 Metrics GlossaryDev Server Metrics:
Build Metrics:
Change Thresholds:
⚡ Dev Server
📦 Dev Server (Webpack) (Legacy)📦 Dev Server (Webpack)
⚡ Production Builds
📦 Production Builds (Webpack) (Legacy)📦 Production Builds (Webpack)
📦 Bundle SizesBundle Sizes⚡ TurbopackClient Main Bundles
Server Middleware
Build DetailsBuild Manifests
📦 WebpackClient Main Bundles
Polyfills
Pages
Server Edge SSR
Middleware
Build DetailsBuild Manifests
Build Cache
🔄 Shared (bundler-independent)Runtimes
📝 Changed Files (16 files)Files with changes:
View diffsapp-page-exp..ntime.dev.jsfailed to diffapp-page-exp..time.prod.jsDiff too large to display app-page-tur..ntime.dev.jsfailed to diffapp-page-tur..time.prod.jsfailed to diffapp-page-tur..ntime.dev.jsfailed to diffapp-page-tur..time.prod.jsDiff too large to display app-page.runtime.dev.jsfailed to diffapp-page.runtime.prod.jsDiff too large to display app-route-ex..ntime.dev.jsDiff too large to display app-route-tu..ntime.dev.jsDiff too large to display app-route-tu..ntime.dev.jsDiff too large to display app-route.runtime.dev.jsDiff too large to display pages-api-tu..ntime.dev.jsDiff too large to display pages-api.runtime.dev.jsDiff too large to display pages-turbo...ntime.dev.jsDiff too large to display pages.runtime.dev.jsDiff too large to display 📎 Tarball URL |
a0d54b5 to
f459f4e
Compare
7c5bb1c to
6cdbd8c
Compare
f459f4e to
af0bc89
Compare
6cdbd8c to
a861e7c
Compare
f50f505 to
1034a3f
Compare
a861e7c to
f63764d
Compare
1034a3f to
0bf6951
Compare
6b94301 to
1d7a484
Compare
b560a6e to
68df0ff
Compare
ae318fc to
b36c41f
Compare
68df0ff to
b93c42c
Compare
b36c41f to
940a612
Compare
8a7b9c8 to
daa3b2b
Compare
940a612 to
47d46de
Compare
When a `"use cache"` entry is newly generated during a prerender (`prerender` or `prerender-runtime`), `collectResult` defers propagation of cache life and tags to the outer context. This is because the entry might later be omitted from the final prerender due to short expire or stale times, and omitted entries should not affect the prerender's cache life. However, when a cache handler returns a hit for an existing entry, `propagateCacheEntryMetadata` was called unconditionally, without the same deferral logic. This meant that short-lived cache entries retrieved from the cache handler could propagate their cache life to the prerender store, even though they would later be omitted from the final render. This inconsistency is currently not observable because runtime prefetches use a prospective and final two-store architecture (see `prospectiveRuntimeServerPrerender` and `finalRuntimeServerPrerender` in `app-render.tsx`). The cache handler hit propagation corrupts the prospective store, but the response is produced from the final store, which reads from the resume data cache with correct stale and expire checks. Static prerenders have a similar two-phase architecture that masks the issue. Because of this, there is no test case that can observe the incorrect behavior, but the fix avoids confusion and prevents the inconsistency from becoming a real bug if the architecture changes. This change extracts a `maybePropagateCacheEntryMetadata` function that encapsulates the conditional propagation logic and is now called from both the generation path (inside `collectResult`) and the cache handler hit path. The resume data cache read path continues to call `propagateCacheEntryMetadata` unconditionally, since it runs in the final render phase after short-lived entries have already been filtered out.
47d46de to
bd20bd2
Compare
daa3b2b to
6282a5a
Compare

When a
"use cache"entry is newly generated during a prerender (prerenderorprerender-runtime),collectResultdefers propagation of cache life and tags to the outer context. This is because the entry might later be omitted from the final prerender due to short expire or stale times, and omitted entries should not affect the prerender's cache life.However, when a cache handler returns a hit for an existing entry,
propagateCacheEntryMetadatawas called unconditionally, without the same deferral logic. This meant that short-lived cache entries retrieved from the cache handler could propagate their cache life to the prerender store, even though they would later be omitted from the final render.This inconsistency is currently not observable because runtime prefetches use a prospective and final two-store architecture (see
prospectiveRuntimeServerPrerenderandfinalRuntimeServerPrerenderinapp-render.tsx). The cache handler hit propagation corrupts the prospective store, but the response is produced from the final store, which reads from the resume data cache with correct stale and expire checks. Static prerenders have a similar two-phase architecture that masks the issue. Because of this, there is no test case that can observe the incorrect behavior, but the fix avoids confusion and prevents the inconsistency from becoming a real bug if the architecture changes.This change extracts a
maybePropagateCacheEntryMetadatafunction that encapsulates the conditional propagation logic and is now called from both the generation path (insidecollectResult) and the cache handler hit path. The resume data cache read path continues to callpropagateCacheEntryMetadataunconditionally, since it runs in the final render phase after short-lived entries have already been filtered out.