## Summary
Foreground revalidation is necessary when a stale result will be
persisted by another server cache, not only when the request is
prerendering. Otherwise, a cache created during a dynamic request can
consume a stale inner value and extend its lifetime.
- derive foreground revalidation from whether the immediate work-unit
consumer will persist the result in a server cache
- model that capability as willConsumerServerCache rather than
inheriting it through the full outer scope chain
- treat server cache and static prerender work units as server-caching
consumers, while runtime prerenders are not
- add regression coverage for an outer "use cache" scope consuming a
stale unstable_cache entry
<!-- NEXT_JS_LLM -->
We're splitting the test app routes between two root layouts, one for (partially) static pages that does not wrap its children in a Suspense boundary, and one for dynamic pages that adds a Suspense boundary above `body`, which opts those pages into fully dynamic rendering.
Two tests are expected to fail for now, because they revealed bugs related to revalidation, which will be fixed in follow-up PRs:
- https://github.com/vercel/next.js/pull/81611
- https://github.com/vercel/next.js/pull/81621
closes NAR-171
When a route handler calls `revalidateTag` or `revalidatePath`, we need
to make sure that these revalidations are executed before returning with
the redirect response.
Note: When deployed, we currently don't have a strong guarantee that the
revalidations are fully propagated before executing the redirect target
route – as we do for redirecting server actions. This is because, for
route handlers, the redirect occurs client-side, which prevents us from
using the same technique as for server actions, which involves sending a
revalidate token as a request header. This token must not leak to the
client. However, eventually the revalidation will be propagated, and a
refresh should show fresh data.
fixes#74757
This allows us to catch regressions and monitor progress (e.g. for NAR-85).
In addition, we're consolidating all pages in the test app to use a single root layout without a suspense boundary. Before we switched the app from using the `dynamicIO` flag to the `useCache` flag, some pages needed a suspense boundary to not fail the build.
While splitting up the `"use cache"` e2e test pages into two separate root layout groups (one with a suspense boundary, one without), I noticed that there's a bug when retrieving the client reference manifest for a page in a route group.
To fix it, we're now normalizing the `page` that's passed into `setReferenceManifestsSingleton` with the existing `normalizeAppPath` helper. When the manifest is retrieved, we're using the already normalized `workStore.route` instead of `workStore.page`.
The cacheScope needs to contain the full CacheEntry, not just the
stream, so that it can transfer tag/life to the parent scope when it's
used.
I needed to do some refactoring. The tricky bit is that we want to store
a Promise into both the cacheHandler and the cacheScope as early as
possible to ensure they can dedupe and future requests while it's still
pending.
This should really be a global config but we don't have an easy way to
get the config from a module. Might need a loader. So this just plumbs
it through a bunch of contexts.
I'm also generate the enum type based on the config.
Implements deduping of "use cache" entries using CacheScopeStore. This
is required for dynamic I/O.
We also have the option to use this for dynamic runtime deduping too,
rather than going to the underlying store and ensuring consistency
within the request. There are some negative tradeoffs with this approach
though that it might not be worth it for dynamic requests. For now, it's
up to the Cache Handler to do deduping if it wants to. I implemented
this in the default cache handler.
I tested this with a blank cache implementation since the existing cache
is microtasky. Since a working cache is required for other tests and
should be the default, I reverted the blank cache. So this doesn't have
the full test coverage yet in practice. Once we land configurable caches
we can use a blank cache in the newly added tests to get better
coverage.
I had to make the cache scope external since it's now shared with RSC
layer.
When compiling a route handler entry, we are now also generating client reference and server reference manifests. This enables `"use cache"` usage in route handlers.
As a follow-up we should try to get rid of the `SERVER_ACTION_MANIFESTS_SINGLETON` and move the manifests to the `WorkStore`.