Skip to content

Pre-warm the MCP query pool + slim worker connections - #560

Merged
bompus merged 1 commit into
fork/consolidatedfrom
perf/query-pool-prewarm
Oct 27, 2026
Merged

bompus merged 1 commit into
fork/consolidatedfrom
perf/query-pool-prewarm

Conversation

@bompus

@bompus bompus commented Oct 27, 2026

Copy link
Copy Markdown
Owner

Summary

Three measured fixes to the MCP read path:

  • Warm floor — the pool grew one worker at a time on demand, so an 8-query burst serialized through the cold-start ladder. A bounded floor (CODEGRAPH_QUERY_POOL_WARM, default 4, capped by pool size) keeps a few workers hot; drain() trickles growth on progress events and the post-warm spawn cap rises 2 → 4. Pools without a default project stay lazy; idle retirement still drains to one warm worker.
  • awaitReady grace — the first tools/call could land inside the ~40 ms window before the eager worker posts ready, take the in-process fallback, and pay a multi-second lazy module load on the transport thread — reproducibly ~6 s on Bun. Calls now wait out a bounded 4 s grace for a pending start before falling back.
  • Compact read-only worker connections — pooled workers opened full writer connections (64 MiB cache, 256 MiB mmap, migration/heal/WAL probes at every spawn). They now openSync readOnly + compact (16 MiB cache, no mmap): pure readers skip writer-side setup, and summed RSS stops multiplying per worker.

Measured (bench harness, gin + n8n, mirrored arms)

Cell Before After Upstream
gin 8-query wave, Node 134 ms ~90 ms ~114 ms
gin busy RSS, Bun — ~480 MB ~800 MB
n8n busy RSS, Node 4.54 GiB 3.10 GiB 3.85 GiB (was +18% → now −20%)
n8n busy RSS, Bun 3.50 GiB 1.24 GiB ~3.5 GiB
n8n 8-query wave, Node / Bun 1.91 / 1.63 s 2.04 / 1.75 s 2.39 / 1.96 s
gin cold first answer ~6 s cliff (race) 164–330 ms 190–380 ms

Test plan

  • __tests__/query-pool.test.ts: 30 tests — warm-floor growth, bounded spawn cap, awaitReady race coverage, crash recovery, retirement, project-less pools
  • Full build + typecheck clean
  • README rows checked: MCP table + loss-list cells move with this change; doc refresh lands here once final numbers settle
  • Worker crash recovery, soft timeout, backstop, direct single-client DIRECT_QUERY_POOL_MAX = 2 cap all preserved

Stacked on fork/consolidated at 543a4f64; independent of #558/#559.

Three fixes to the MCP read path, all measured on gin + n8n:

- Warm floor: the pool lazily grew one worker at a time, so an 8-query
  burst serialized through the spawn ladder. A bounded warm floor
  (CODEGRAPH_QUERY_POOL_WARM, default 4, capped by pool size) keeps a
  few workers hot; drain() trickles growth on progress events and the
  post-warm spawn cap rises 2 -> 4 so a real burst still fills the
  pool. Pools without a default project stay lazy.

- awaitReady: the first tools/call could land inside the ~40 ms window
  before the eager worker posts 'ready', take the in-process fallback,
  and pay a multi-second lazy module load on the transport thread
  (reproducibly ~6 s on Bun). Calls now wait out a bounded 4 s grace
  for a pending start before falling back.

- Compact read-only worker connections: pooled workers opened full
  writer connections (64 MiB cache, 256 MiB mmap, migration/heal/WAL
  probes at every spawn). They now openSync readOnly + compact
  (16 MiB cache, no mmap) — pure readers skip writer-side setup, and
  the pool's summed RSS stops multiplying per worker.

Measured: gin 8-query wave 134 -> ~90 ms (upstream-Node ~114); n8n
busy process-tree RSS 4.54 -> 3.10 GiB on Node (upstream 3.85) and
1.24 GiB on Bun (upstream ~3.5); n8n wave 1.91 -> 2.04 s Node /
1.63 -> 1.75 s Bun stays ahead of upstream (2.39 / 1.96); gin cold
first answer no longer falls off the ~6 s cliff.
@coderabbitai

coderabbitai Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 59 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

Run configuration
Commits

Reviewing files that changed from the base of the PR and between 543a4f6 and 926cf02.

Files selected for processing: 7
  • __tests__/query-pool.test.ts
  • site/src/content/docs/reference/mcp-server.md
  • src/codegraph.ts
  • src/db/index.ts
  • src/mcp/query-pool.ts
  • src/mcp/query-worker.ts
  • src/mcp/tools.ts
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@bompus

bompus commented Oct 27, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Oct 27, 2026 •

Copy link
Copy Markdown
⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@bompus
bompus merged commit c01c4a7 into fork/consolidated Oct 27, 2026
4 checks passed
github-actions Bot pushed a commit that referenced this pull request Oct 28, 2026
…s rate-limited API (colbymchenry#2939)

install.ps1 resolved "latest" with one Invoke-RestMethod to the GitHub
API, with no fallback, under ErrorActionPreference Stop. The API allows
60 unauthenticated requests an hour per IP and then answers 403, so
`irm .../install.ps1 | iex` died with a raw "(403) Forbidden" on shared
networks and in CI. A pinned CODEGRAPH_VERSION=1.6.2 was used as-is, so
the download URL lacked the v that release tags carry and 404ed.
install.sh (PR #336) and `codegraph upgrade` (resolveLatestVersion)
already handle both; install.ps1 was never ported.

Fix: step 2 is now a function between Resolve-CodeGraphVersion markers.
It sends a HEAD to github.com/<repo>/releases/latest with
System.Net.HttpWebRequest and AllowAutoRedirect off, reads the tag from
the Location header with /releases/tag/([^/?#]+), falls back to the API
inside try/catch, adds the v to a bare version, and keeps the friendly
throw when both fail. Install-CodeGraphFiles is untouched (still equal to
WINDOWS_SWAP_FUNCTION).

Validation (Windows PowerShell 5.1.26100; pwsh 7 is not installed here):
- Each arm's unmodified step 2, with Invoke-RestMethod shadowed so the
  API answers a local 403: main stops with "(403) Forbidden"; the fix
  resolves v1.6.2 from the real redirect with no API call.
  CODEGRAPH_VERSION=1.6.2: main builds .../download/1.6.2/... (HEAD 404),
  the fix .../download/v1.6.2/... (HEAD 302). With the API available,
  main makes one API call and the fix none.
- New Windows-gated test runs the shipped resolver block in PowerShell
  against a local stand-in for github.com and the API: redirect read with
  one HEAD and no follow, API fallback for a 500 or a missing Location,
  the friendly error (no raw 403) when both fail, the v added to a pinned
  version with no request. 4 fail on main's install.ps1, 4 pass with the
  fix; upgrade.test.ts (the swap function pin) passes.
- Both scripts parse with no errors in PowerShell 5.1. The install steps
  were not run: they always add the install to the user PATH.

Reported in PR #560 by @hiyochi; this reimplements the fix
independently.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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