Skip to content

Run libnpmdiff in a worker with a time budget and cache timeouts - #1077

Draft
oBusk wants to merge 1 commit into
mainfrom
worktree-diff-timeout-cache
Draft

oBusk wants to merge 1 commit into
mainfrom
worktree-diff-timeout-cache

Conversation

@oBusk

@oBusk oBusk commented Oct 22, 2026

Copy link
Copy Markdown
Owner

Why

Fluid Active CPU spiked Sep 30 – Oct 9 with only a few hundred invocations per day (~17s CPU/invocation). Measured locally (prod build):

Diff CPU time in libnpmdiff
lodash 4.17.20→4.17.21 0.5s 0.2s
react-dom 18.3.1→19.0.0 41s 37s
typescript 5.8.3→5.9.2 71s 69s

libnpmdiff calls jsdiff's createTwoFilesPatch synchronously per file, so huge bundled files block the event loop. Diffs over maxDuration (60s) are killed before cacheLife runs, so they're recomputed on every request, and they starve concurrent requests (Bundlephobia/Packagephobia/source lookups time out).

What

  • libnpmdiff runs in a worker_threads Worker (new Worker(new URL("./libnpmdiff.worker.ts", import.meta.url))), terminated after 40s.
  • Timeout is returned as { kind: "timeout" } and cached (cacheLife("days"), not max, in case the timeout was a slow registry download).
  • Page renders "This diff is too large!" with the local npm diff command; API returns 504.

Verified locally (next build + next start)

  • Turbopack emits a separate [worker thread]-src_lib_npmDiff_libnpmdiff_worker_ts_*.js chunk.
  • typescript diff: 40s → "too large"; repeat 0.1s (cached). API: 504, repeat 4ms.
  • Bundlephobia/Packagephobia now complete (26ms/47ms) during a large diff instead of timing out.
  • lint, typecheck, test-ci (312/312), build pass.

Not verified

  • Behaviour on Vercel (worker chunk tracing into the function bundle, worker startup cost on Fluid). Check on the preview deployment, e.g. /typescript@5.8.3...typescript@5.9.2 should show "too large" after ~40s and instantly on reload.
  • Worker startup adds a small fixed cost per uncached diff.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Xk9y1TZXZcrUoUrCUaLGKp

Large diffs (react-dom, typescript) spend 40-70s of CPU in libnpmdiff's
synchronous jsdiff calls. They exceeded maxDuration, were killed before
cacheLife ran, and so were recomputed on every request, while also
blocking the event loop for concurrent requests.

libnpmdiff now runs in a worker thread that is terminated after 40s.
The timeout is returned as a "timeout" result and cached, rendered as
"diff too large" on the page and as a 504 from the API.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xk9y1TZXZcrUoUrCUaLGKp

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant