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"]]`