mirror of
https://github.com/vercel/next.js.git
synced 2026-09-20 02:25:18 +08:00
codex/ledgers-react-sync
4 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
e0966df114 |
test: migrate caching deploy exclusions to force gates (#98154)
## Summary - migrate Cache Components, PPR, prefetching, prerendering, revalidation, and SSG deployment exclusions to `@force-gate` - remove the corresponding `skipDeployment` options and `skipped` control flow while preserving each suite's rationale This keeps caching-related exclusions together so their deployed semantics can be reviewed by the same owners. ## Verification - verified on the combined top-of-stack tree with `pnpm typescript` - compared ordinary-mode collection before and after: all 4,670 existing test names matched - collected all 510 affected files in deploy mode: 4,578 skipped tests, zero failures <!-- NEXT_JS_LLM --> |
||
|
|
e3790aa911 |
More granular cache keys for use-cache entries (#95233)
Caveats: - I had to skip `react[-dom][/*]` and `private-next-rsc-server-reference` and `private-next-rsc-cache-wrapper` imports in the code hash and env-var tracking. Because those all end up pulling in app-page-turbo.runtime.prod.js which reads many env vars and would cause constant deopting. But this should still be correct. The code of these imports is included via the Next.js version, and no env vars should change the semantics of any of those imports. - Static env var reads are collected, but too dynamic accesses are silently ignored and don't lead to deopts (same goes for env var reads in native NAPI addons). This means that this cache reuse is not guaranteed to be 100% guaranteed to never lead to stale caches. - Code hashing and env var collection happens on a per-module basis. So if you put all use-cache functions into a single file and/or together with react components, then you will see extraneous invalidations. This will be fixed by either generally enabling module splitting for all of Turbopack, or by adding a special transform that does it for use-cache functions. Followups: - There are some env var static analysis gaps that will be immediate followups before broader testing. These are the various TODOs added in https://github.com/vercel/next.js/pull/95310 - Client components invalidation is very coarse grained right now (statically imports any client component anywhere -> deopt completely). Followup for the future. But the current setup is correct. This would just improve effectiveness further - Do this in dev as well. Currently there is no NFT at all in dev (for performance reasons) - Module splitting for more granular tracking - Include entropy when serializing server reference arguments for cache key Todo: - [x] Use implementation code hash (includes inlined env vars): from #94234 - [x] Include non-inlined runtime env vars: from #95310 - [x] Include client reference manifest (very coarse for now) - [x] Include Next.js version (for wire format, etc) - [x] This is now done for all use-cache entries now. Not just for `use cache: remote`. Is that the intended behavior? Yes - [ ] ~~if `NEXT_DEPLOYMENT_ID` is in the env vars. just deopt and don't care about stringifing and hashing the env vars~~ - [x] Is cache key size a problem? Currently you can get this: (values are always hashed) `CustomCacheHandler::get ["80e6f6560092f0078775e7e787c1c10ecf6dea0bc4",[],["d984fbaa996274737f3b59345a300a20","16.4.0-canary.5","__NEXT_NO_MIDDLEWARE_URL_NORMALIZE=undefined","NEXT_OTEL_PERFORMANCE_PREFIX=undefined","__NEXT_PRIVATE_ORIGIN=a04f4b9d6a8f42724740a480e9a2bc67c053dc04377be831c0e2c407a1422004","NEXT_PRIVATE_RESPONSE_CACHE_TTL=undefined","NEXT_PRIVATE_RESPONSE_CACHE_MAX_SIZE=undefined","__NEXT_CACHE_COMPONENTS=b5bea41b6c623f7c09f1bf24dcae58ebab3c0cdd90ad966bc43a45b44867e12b","__NEXT_ROUTER_BASEPATH=undefined","__NEXT_MANUAL_CLIENT_BASE_PATH=undefined","__NEXT_INSTRUMENTATION_CLIENT_ROUTER_TRANSITION_EVENTS=undefined","__NEXT_APP_NAV_FAIL_HANDLING=undefined","__NEXT_GESTURE_TRANSITION=undefined","__NEXT_USE_OFFLINE=undefined","NEXT_DEBUG_BUILD=undefined","__NEXT_VERBOSE_LOGGING=undefined...]] [["_N_T_/layout","_N_T_/page","_N_T_/","_N_T_/index"]]` |
||
|
|
286862e35b |
Fix stale dev 'use cache' for cookieless requests and route handlers (#96022)
In development, editing a file does not evict existing `'use cache'` entries; an entry is invalidated only by re-keying, and the key includes an HMR refresh hash. That hash was delivered through the `__next_hmr_refresh_hash__` cookie, which the browser HMR client set from each server-components change and sent back on later requests. Two cases were therefore broken. First, any request that does not carry the cookie (a `curl`, a plain `fetch`, a fresh browser profile, a second device) served stale cached content after an edit, for both pages and route handlers. Second, on Turbopack a route handler served stale content even when fetched from a page whose HMR client had the cookie, because a route-handler edit never advanced the hash there and so never updated the cookie; on webpack that case happens to work, leaving only cookieless route handlers broken. The hash is server-authored to begin with (webpack's `stats.hash`, Turbopack's `hmrHash` counter), so this change sources it directly on the server and removes the cookie entirely. The hot-reloader exposes it, and `base-server` attaches it to each request with `addRequestMeta`, next to `serverComponentsHmrCache`, so it reaches every render, including the internal Cache Components validation and warmup renders. It is threaded onto the request store the same way `serverComponentsHmrCache` is, so `'use cache'` is invalidated for every client regardless of whether it ran the HMR client. Route handlers, whose `NextRequest` does not carry request meta, receive it through the app-route template's `renderOpts`. Editing a route handler also has to advance the hash in the first place. On webpack it already did, but on Turbopack the `app-route` entry wired no change subscription, so a route edit recompiled the module without advancing `hmrHash`; it now subscribes to the route endpoint so an edit advances the hash without broadcasting a page refresh. That subscription is also made the only thing that advances `hmrHash`: the config-invalidation and server Fast Refresh reset paths previously bumped it even without a content change, for example on a route's first load, and with the hash now read synchronously instead of bounced back through a cookie, such a bump would land between a route's cold write and warm read and evict a still-valid entry. The getter returns the counter unconditionally so the key is present and stable for every request, matching webpack's always-present `stats.hash`. With the cookie gone, the `hash` field on the `SERVER_COMPONENT_CHANGES` HMR message has no remaining consumer, so it is dropped from the message type and both bundlers. This is a short-term stopgap until the granular implementation hash (`codeHash` plus `runtimeEnvVars` plus the Next.js version) is used in `next dev`, which invalidates server-authoritatively by construction and will let this mechanism be removed. > [!TIP] > Best reviewed with hidden whitespace changes. closes NAR-894 --- <sub>Stack created with <a href="https://github.com/github/gh-stack">GitHub Stacks CLI</a> • <a href="https://gh.io/stacks-feedback">Give Feedback 💬</a></sub> |
||
|
|
34dbafb46d |
Skip saving expire: 0 values in the default cache handler in prod (#95363)
A `'use cache'` value with an explicit `cacheLife({ expire: 0 })` is
expired the moment it is produced, so in production the `'use cache'`
wrapper regenerates it on every read rather than serving the stored
copy. The entry written to the cache handler is never served back, so
persisting it is wasted work, and for a remote handler a needless
round-trip and stored payload.
Whether to skip that write is left to each cache handler. This change
implements it in the built-in default handler; a custom handler can opt
into the same behavior. Beyond the saved write, this makes `cacheLife({
expire: 0 })` a reliable way to keep a value out of the server-side
cache handler, which covers two cases. In a conditional opt-out, a
function with an otherwise normal cache life sets `expire: 0` for a
result that is not worth persisting, for example when an upstream fetch
errored. In client-only caching, a value is `expire: 0` by design so
that it lives only in the client cache, via Cached Navigations or
Runtime Prefetches when the route opts in, and never on the server.
The skip is gated on `!process.env.__NEXT_DEV_SERVER`. In production the
value is regenerated on every read regardless, so not storing it changes
nothing that is served. Development keeps storing it, because the
default handler's minimum retention serves the previously cached value
across reloads while the entry re-warms in the background.
A new `use-cache-default-handler-expire-zero` end-to-end test enables
the default handler's debug logging (`NEXT_PRIVATE_DEBUG_CACHE`) and
asserts on its `set()` decisions. Because the debug output is only
available from the CLI, the test skips deployment.
|