Fix duplicate background revalidation for `'use cache'` (#98446)
An early `cacheHandler.set()` call previously made subsequent same-key
`get()` calls wait for generation and persistence. This prevented
another background revalidation, but those requests waited instead of
receiving the existing stale data.
The work in #94694 added private-cache persistence in dev. Its helper
refactor made cross-request invocations wait for cache writes before
retrying root-param-specific lookups. It also moved the coarse-key
`set()` call after entry collection. That unintentionally allowed
subsequent requests to receive stale data and start another background
revalidation while one was already running.
This change tracks background work separately for each lookup key within
a runtime instance. Subsequent requests can receive the existing stale
entry without waiting for generation or starting duplicate work. The
task remains registered until collection and its cache writes settle.
Each lookup records existing work before calling `get()`, so a slow
lookup does not start another revalidation after the task it observed
has completed.
The handler still receives `set()` after collection, including
asynchronous rendering of the cached result. Subsequent `get()` calls
can still block while the handler persists the entry. We accept that
remaining wait to preserve the cache-handler contract without bypassing
handler reads.
**Alternatives Considered**
- Restore early `set()` calls and rely on the handler's pending-set
promises. This would prevent duplicate regeneration, but would also make
subsequent requests wait for generation again instead of receiving stale
data.
- Extend the existing cross-request sharing mechanism through background
revalidation. We prototyped retaining stale and regenerated results, but
that required additional stream ownership, invalidation checks, and
root-param matching. The separate background-task map avoids those
changes and leaves cold and foreground generation unchanged.
Fixes #98326