Repository navigation
perf(void): reuse unchanged source analysis during dev rescans - #543
Conversation
fa91f95 to
b4015bf
Compare
8a132a7 to
ccd7809
Compare
## Problem Every file event in a source convention directory reran the whole application analysis. Binding inference and the page scanner each read every source file synchronously, and the page scanner parsed every `*.server.ts` with Oxc again. In a 5,000-page app (the many-pages benchmark), one rescan made 20,006 `readFileSync` calls, two per page file, and took about 800 ms on an idle machine. After the rescan, the routing plugin regenerated `virtual:void-routes` even when the save changed nothing the scan reports. That module lists every page, so Vite re-transformed and re-ran it on every save. In a CPU profile of `vp dev` while editing one page of that app, the rescan took 1.0-1.6 s of each 2.0-2.7 s HMR update, and re-transforming and re-running the route table took most of the rest. The benchmark's HMR time grew from 209 ms at 100 pages to 2.21 s at 5,000. The React adapter's pages and islands plugins also scan pages on their own when files are added or removed and at build start, so the same files were read and parsed several times. ## Fix - `deriveFromSource` (`shared/source-analysis-cache.ts`) caches values derived from a file's contents, keyed by path and analysis kind, and reuses them while the file's inode, size, mtime and ctime are unchanged. Binding inference, page server metadata, component `layout` exports and Markdown frontmatter go through it. The cache is module-level, so the core plugin and the framework adapters share it. - Coarse file system timestamps and inode reuse can leave a rewritten file's stat unchanged. As Git does for racily clean index entries, a file that changed within the last 3 s is always read again, and the dev watcher drops a file's entries on every event. - On a `change` event, the routing plugin compares the scan before and after the rescan. When nothing changed, it skips regenerating route types and entries and leaves `virtual:void-routes` alone; Vite still invalidates the saved module. Adds, deletes, auth config, env files and SSR entries keep the previous behavior. ## Impact Measured on the 5,000-page benchmark app: - A warm rescan reads 2 files instead of 20,006 and takes about 100 ms instead of 530-640 ms. - HMR after editing a page (median of five edits, two runs each): 1,166 and 1,216 ms before, 299 and 248 ms after. The benchmark measured 209 ms at 100 pages, so HMR now barely grows with the page count. - Loader and route handler edits also reach the next SSR response sooner: about 1.6 s to 0.5 s for a loader, and 0.8-1.2 s to 0.1 s for a route handler. ## Verification - New `source-analysis-cache.test.ts`. `pages-scan.test.ts` covers metadata edits between cached scans and fails if the cache ignores the file signature. `plugin-routing.test.ts` covers saves that do and do not change scanned facts. - `vp run check` (33/33, after generating playground codegen in a fresh worktree). - `vp run test --base origin/main`: 511 affected files pass, including the Pages HMR integration suites. - On `vp dev` with the 5,000-page app, edits to a page component, its loader and a route handler all reach the next SSR response. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Remove redundant cache guidance from AGENTS.md while keeping the implementation unchanged.
Keep the generated routing checkpoint pending until writes and module invalidation succeed, including forced updates with unchanged scan facts. Cover write failures, forced updates, invalidation failures, and recovery to the body-only save fast path.
Stop an unresponsive runtime version probe after ten seconds so test discovery can skip unavailable optional runtimes instead of hanging before Vitest timeouts apply.
ccd7809 to
18a4733
Compare
# Conflicts: # packages/void/src/plugin-routing.ts # packages/void/test/unit/plugin-routing.test.ts
…alysis on watch rebuilds - The fast path compared only that the generated files existed. Another void process in the same root (`void build`, `void db generate`, or the previous server during a restart) can rewrite them, and the dev watcher ignores `.void/`, so they stayed stale until the scan changed. Record their size and mtime at each checkpoint and regenerate when they differ. - `vite build --watch` reruns buildStart without the dev watcher, so the `watchChange` hook now forgets the analysis of each changed file too. - Tests: an environment-file save with an unchanged scan, a forced update whose invalidation failed, a reverted edit after a failed update, a generated file rewritten by another process, and a stronger queue check that fails for a queue that only defers by a microtask. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Thanks for this PR. I merged Each new test fails when its fix is removed. Checked and left alone: the cache key (ino, size, mtime, ctime, plus the 3 s window) is sound on local disks, and callers don't mutate cached values. |
|
@codex review |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
Codex Review: Didn't find any major issues. Chef's kiss. Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
|
Thanks :)
Glad no major issues found, I had Claude and Codex do many rounds of
reviews before publishing for review.
…On Thu, 22 Oct 2026, 11:13 pm chatgpt-codex-connector[bot], < ***@***.***> wrote:
*chatgpt-codex-connector[bot]* left a comment (voidzero-dev/void#543)
<#543 (comment)>
Codex Review: Didn't find any major issues. Chef's kiss.
*Reviewed commit:* 1aec808
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo
<https://chatgpt.com/codex/cloud/settings/general>. Reviews are triggered
when you
- Open a pull request for review
- Mark a draft as ready
- Comment ***@***.*** <https://github.com/codex> review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting ***@***.***
<https://github.com/codex> address that feedback".
—
Reply to this email directly, view it on GitHub
<#543?email_source=notifications&email_token=AAKBCHLS6PZPDWQY4KLQDZT5VIPY7A5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTMMZUGQYTSNZZGE42M4TFMFZW63VGMF2XI2DPOKSWK5TFNZ2KYZTPN52GK4S7MNWGSY3L#issuecomment-6344197919>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAKBCHJN6VVTBX3CY43HWQD5VIPY7AVCNFSNUABGKJSXA33TNF2G64TZHMYTCNRUGQZTOMBYGA5US43TOVSTWNJZG4YTIMRYGA3TJILWAI>
.
You are receiving this because you authored the thread.Message ID:
***@***.***>
|
Issue
Saving a page currently re-reads and analyzes unchanged source files, then regenerates routing output even when only the page body changed. This adds unnecessary work as the app grows.
Fix
This change caches file-derived analysis, invalidates edited files through the watcher, and skips route-entry regeneration and route-module invalidation when routing inputs remain unchanged. Recent writes are conservatively re-analyzed to preserve freshness. Layout inventories are deterministic, and returned Markdown metadata cannot mutate the cached analysis. Startup and file changes serialize regeneration so the generated files and successful-input checkpoint stay consistent. Watcher failures are logged and retried on a later save. Auth provider/dialect settings participate in change detection, and missing generated routing files are repaired on the next source save.
The implementation is shared across React, Vue, Svelte, and Solid Pages. The benchmarks below measure React only.
Results
Measured from file write to the updated heading appearing in Chrome, using Cloudflare dev with local workerd on an Apple M5:
Each fixture includes two additional fixed pages. Results average session medians across ten paired sessions per size, with 360 timed saves overall and seeded, sequential variant ordering after warmup.
Both 1,000-page improvements are statistically significant after Holm correction across six comparisons (
p = 0.0117). The 95% savings intervals are 235–268 ms for HMR and 123–197 ms for reloads. Smaller fixtures show no statistically clear improvement or regression.Validation:
36a3697f): core build, 465 routing/cache/navigation and email unit/integration tests, and all 33 workspace typecheck tasks passed. The targeted run skipped 13 tests.cd480dde: affected suite passed 10,936 tests; full suite passed 14,314 tests, with 31 skipped.The performance figures above are from the original benchmark run; this follow-up was verified for correctness.