Conversation
… writes settle (cloudflare#3321) An inner `"use cache"` invocation now publishes its value, tags, lifetime, and root-param dependencies to the enclosing request as soon as collection completes. Its handler write is handed to the request lifecycle (`waitUntil` plus the pending cache-work queue that action and route-handler boundaries drain) instead of being awaited on the value path, and a repeat lookup for the same key is served from a request-scoped retained record until that write settles. Two outer cache scopes that share an inner cache therefore inherit the inner metadata while persistence is still in flight, instead of re-executing the inner function and keying themselves without the root params it read. The retained record is dropped once the write settles, and cross-request reads are unchanged: a later request still reads the handler and follows the root-param redirect marker to rebuild a root-param-specific key, so an entry produced for one root-param value is never reused for another. Upstream: vercel/next.js#98808.
@vinext/cloudflare
create-vinext-app
@vinext/types
vinext
@cloudflare/workers-response-store
commit: |
Contributor
Performance benchmarksCompared 1 improved · 0 regressed · 5 within ±1.5%
View detailed results and traces 🟢 improvement · 🔴 regression · ⚫ change below 1.5% · paired base/head |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #3321
Summary
"use cache"invocation's value, tags, lifetime, and root-param dependencies to the enclosing request as soon as collection completes; persistence is handed to the request lifecycle instead of being awaited on the value path.Cause
Two outer cache scopes that share an inner
"use cache"call must inherit the inner scope's root-param dependencies while the inner cache-handler write is still pending. Vinext awaited that write inside the value path, so an outer scope either stalled behind a slow handler or re-executed the inner function and keyed its own entry without the root params the inner cache read — letting a later request with a different root-param value reuse the entry produced for another value.Upstream fixed the same problem in vercel/next.js#98808 (
Fix metadata propagation for deduplicated nested caches) by replacing the pending-metadata promise consumers awaited with a shared cache entry that forks at collection time. This change applies the same ordering to vinext: metadata leaves the inner scope at collection, and the write is deferred throughwaitUntilon Workers or the pending cache-work queue that action and route-handler boundaries drain. Where no lifecycle owns the work, the write is still awaited, so "a resolved value implies its write settled" continues to hold there. The retained record is dropped once the write settles.Red/green proof
vp test run --project unit tests/use-cache-root-params.test.ts— with the fix removed the new test times out, because the render never finishes while the inner write is held; with the fix, 3 passed.The test is ported from upstream
test/e2e/app-dir/app-root-params-getters/use-cache.test.ts(theuse-cache-dedupfixture): a delaying handler holds the English inner write until both outer entries reachset(). It asserts the render completes, the inner function executes once, both outer entries are stored under root-param-specific keys, and a later French request does not reuse the English entry.Validation
vp run build,vp run check,vp run knip,bash -n scripts/*.sh,node scripts/ci-integration-shard.mjs --check --shard-total=10— all pass.vp test run(unit + integration): 12,249 passed. The remaining failures (action-runtime-security,dev-route-discovery×2,pages-router) reproduce on the clean baseline in this environment (macOS/varvs/private/varpath handling); parallel-run-only failures pass in isolation.app-router-prod/use-cache.spec.ts), app-router-client-cache 22, cloudflare-dev 19, cloudflare-workers 86, pages-router 144, pages-router-prod 77, remaining projects green.@cloudflare/workers-response-store#test51,@vinext/cloudflare#test31.vinext build+ expected outputs), create-vinext-app (Cloudflare build).