fix: cache fs-routes enumeration across dev requests - #180
Merged
Conversation
Every dev request iterated getEntries(), re-running the whole site enumeration — including every generateStaticParams() — and registering a fresh set of chunks under new random IDs. On large sites this made every request pay the full-site cost, and the flood of registrations pushed older chunk IDs out of the dev defer registry, so long-idle tabs lost their soft-navigation chunks and fell back to hard navigation. The enumeration (route tree, generateStaticParams(), chunk registration) now runs once per entries-module instance and is cached; editing a routed file invalidates the module in dev and re-enumerates, and a build iterates getEntries() only once so the cache is inert there. Chunk IDs stay stable across requests, and any chunk the dev registry evicts is re-registered under its original ID on the next request, keeping payloads held by open tabs soft-navigable. A failed enumeration is not cached, so a transient generateStaticParams() error does not stick for the session. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016D8ULaGdQjQ493LBy3ow1S
…stry The restore pass runs only when getEntries() is iterated (HTML requests and HMR re-fetches), but a chunk fetch on soft navigation goes through serveRSC's module path, which never iterates the entries. With chunk IDs now stable across requests, a settled chunk idle past the eviction TTL could be dropped — by evictStale at the start of the very request fetching it — and 404, falling back to hard navigation. On a registry miss, iterate the entries once (cheap: the enumeration is cached) to re-register the current chunk set under its original IDs, then retry the lookup before returning 404. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016D8ULaGdQjQ493LBy3ow1S
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
Every dev-server request iterates
getEntries(), and the fs-routes implementation did all of its work per invocation. So on every request (HTML and HMR RSC re-fetches alike):generateStaticParams()re-ran — user code that often fetches data, making large sites pay the full-site enumeration cost per request;The registration flood interacts badly with the registry's eviction policy (
maxPending: 1000, oldest first): a site with C chunks evicts a tab's chunks after ~1000/C further requests. Payloads already served bake the old chunk IDs into their slots, so when a long-idle tab soft-navigates, the chunk fetch 404s andFsRouteChunkBoundaryfalls back to a hard navigation.Fix
createFsRoutesEntriesWithHostnow enumerates the site once per entries-module instance and caches the result (route tree, pages, node metas, registered chunks);getEntries()iterations reuse it:generateStaticParams()or re-register chunks; chunk IDs stay stable across requests, so eviction pressure from fs-routes disappears entirely.maxPendingpressure fromdefer()calls) are re-registered under their original IDs on the next request, via two new host hooks (hasChunk/restoreChunk), so pages held by open tabs stay soft-navigable.import.meta.glob), so a fresh module instance re-enumerates.getEntries()once.generateStaticParams()error (e.g. a CMS hiccup) doesn't stick for the rest of the dev session.Behavior note:
generateStaticParams()results derived from external data are now cached in dev until a routed file is edited or the server restarts. Documented in the file-system routing docs.Testing
pnpm typecheck,pnpm lint,pnpm format:check,pnpm test:runall pass.This addresses the dev-request cost of the existing enumeration model; a follow-up issue covers the larger improvement of not enumerating the whole site to serve one request (also for the non-fs
entries.tsxmode).🤖 Generated with Claude Code
https://claude.ai/code/session_016D8ULaGdQjQ493LBy3ow1S
Generated by Claude Code