fix: scope dev defer registrations per HTML render to bound registry growth#160
Closed
uhyo wants to merge 1 commit into
Closed
fix: scope dev defer registrations per HTML render to bound registry growth#160uhyo wants to merge 1 commit into
uhyo wants to merge 1 commit into
Conversation
…growth In dev, deferRegistry is a module-level singleton and every HTML render registered fresh defer() entries (with random UUIDs) that were never evicted, so memory grew unboundedly over a dev session (#144). Track registrations per top-level HTML render: the dev server wraps each HTML render and HMR RSC re-fetch in DeferRegistry#startRender, which uses AsyncLocalStorage so that every defer() call made during that render — including nested defer() calls made when a deferred element is itself rendered later — is attributed to the same render, forming a rendered-by tree rooted at the top-level render. When a new render for the same entry path begins, the previous render's whole tree is invalidated and evicted after a 30s grace period, so in-flight client fetches for the previous page load still resolve. Build-time rendering never scopes renders, so build output is unaffected. Closes #144 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012sAWXBbDQBMWmdZpgkssXH
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.
Problem
In dev,
deferRegistryis a module-level singleton in the RSC environment. Every HTML render registered freshdefer()entries under random UUIDs and nothing ever evicted them, so memory grew unboundedly over a long dev session (many page loads / HMR re-renders).Closes #144
Approach
Implements the "track registrations per HTML render" strategy suggested in the issue, extended to a rendered-by tree so that invalidating a top-level render invalidates all of its nested
defer()registrations too:DeferRegistry#startRender(key, fn): the dev server wraps each top-level HTML render and each HMR RSC re-fetch in anAsyncLocalStoragecontext keyed by entry path. Everydefer()registration made during that render is attributed to aRenderHandlefor it.defer()calls attach to the same top-level render.startRender, so unattributed entries are kept until the process exits, as before.DeferRegistryis extracted fromdefer.tsxintodeferRegistry.ts(with re-exports preserving existing imports) so the lifecycle is unit-testable without thevirtual:funstack/config/#rsc-clientmodules.Verification
fixture-ssr-deferdev server: after re-rendering the page, the previous generation's parent and nested payloads kept serving 200 during the grace period, then both returned 404 after 30s, while the current generation stayed at 200 — confirmingAsyncLocalStorageattribution holds through real React flight rendering.🤖 Generated with Claude Code
https://claude.ai/code/session_012sAWXBbDQBMWmdZpgkssXH
Generated by Claude Code