Repository navigation
fix(ppr): collect shell tags at the putShell write barrier — async-recorded tags evict the shell (#676) - #680
Merged
Merged
Conversation
…d tags evict the shell (#676)
commit: |
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.
Closes #676.
Problem statement
A
cacheTag()recorded after anawaitin an async server component that renders into a PPR shell was silently dropped from the shell entry —revalidateTag()with that tag would not evict the shell. The same window applied to tags propagated by asynccache()/"use cache"reads during capture. Landed as a documented known edge with #678; consumers following the render-callablecacheTag()pattern in async components got un-evictable shells with no signal.Root cause and fix
The shell's tag snapshot ran at RSC-stream construction time in
attemptCapture— before the capture had rendered or quiesced. Synchronous tags survived only because React renders the sync tree during construction. The fix moves the snapshot (and the union with staticppr.tags) to theputShellwrite barrier insidecaptureAndStoreShell— after the capture quiesces and the deferred cache writes are awaited — so anything recorded at any point before the write is collected. Moved, not duplicated:shell-capture.tshas exactly one tag snapshot site. The hole invariant is unchanged (masked loaders never execute during capture, so nothing under a renderableloading()boundary can tag the shell); the document cache never shared the edge (it buffers before collecting).Before vs. after
awaitin async shell contentrevalidateTagcannot evict the shellrevalidateTagdrops the shell"use cache"read at captureppr.tagsunion + dedupeloading()holeCode-change summary
One production file:
src/rsc/shell-capture.ts(+44/−29 incl. comments) — the collect/union block deleted fromattemptCapture, recreated at theputShellsite incaptureAndStoreShell(whosereqCtxis the derived capture context),ShellCaptureDescriptor.tagsdocblock updated. Everything else is tests, fixture, and the design-doc contract update.Test plan
"use cache"propagation simulated via the exactrecordRequestTags(entry.tags, requestCtx)callcache-runtime.ts:384makes on a read-HIT (substitution over standing up a cached fn — noted in the test).captureAndStoreShellstub tests gained the required_requestTags: new Set()field their mock omitted (production always seeds it); their existingtags: ["t1"]assertions passing unchanged is itself the data-preservation proof for the move.ppr-tag-evictiongains the async case (async-tagged shell → HIT →revalidateTag→ recapture), probe-gated with a unique per-run token like the sync case; existing cases untouched. 4+4 green.ppr-shellsuite 19+19 green in dev and production.test:unit:all(708 in the affected suites), lint, format,check:e2e-bucketing, zero.gen.tschurn.Notes
docs/design/ppr-shell-resume.mdis replaced by the write-barrier contract.putShellpath should reuse the same barrier-collect.