Repository navigation
perf(query): retire excess workers after idle bursts - #2862
Closed
doomsday1906 wants to merge 1 commit into
Closed
doomsday1906 wants to merge 1 commit into
doomsday1906 wants to merge 1 commit into
Conversation
doomsday1906
marked this pull request as ready for review
October 23, 2026 21:04
colbymchenry
pushed a commit
that referenced
this pull request
Oct 31, 2026
…en back A session grows its query pool for concurrent reads, and every worker kept its graph caches, SQLite pages and heap until shutdown, so a server that had served one burst held that memory for the rest of its life. After five minutes with no work the pool now retires every worker that has been used and starts one fresh one, which is ready for the next call. A retired worker is asked to close: it closes its projects, runs a full collection and exits by itself, the path worker-teardown.ts measured as safe on Windows. Only one that hasn't exited after 5 seconds is terminated, through the pool's own start-aware path. Calls that arrive while the fresh worker starts are served in process, the same way as before the pool's first worker is up. Changed on top of the PR: - The idle delay is five minutes, not the PR's 60 seconds, so main doesn't get slower than 1.6.2. Agents routinely pause over a minute to edit or build, and the first explore on a fresh worker takes about 200 ms more. The memory still comes back when a session really goes quiet. - The recycle also arms when a burst's last worker reports ready after the last result. Before, that pool stayed at full size until the next call. - A warm worker that has served nothing is never recycled, so that readiness arming can't replace a clean worker over and over. - destroy() waits for retirements underway, so a worker is not left to end with the process. PR #2862 had this; the PR here did not. PR #2862 by @doomsday1906 made the same idle-worker change and is closed in favor of this one. On its own fork it measured idle RSS going from about 673 MiB to 426 MiB. Validation: - Red on main: six pool tests fail, including a real worker that retires with exit code 0 and is never terminated. Each added case was also proven red by undoing its line. - Windows 11 x64, on a busy machine: 480 child processes, each with four real workers on an excalidraw index and retiring right after a burst, all exited 0 (1,920 worker exits). The AGENTS.md crash did not reproduce here even with an owner terminate in place of the close (0 of 480), so this shows the path works, not that the crash can't happen. - excalidraw, four workers after a burst of explores: 611 MB RSS idle on main, 113 MB here. The first explore after a recycle took 299 ms against 93 ms on a warm worker, and the second was back to 92 ms. Original PR #1583 by @danusha2345. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
colbymchenry
pushed a commit
that referenced
this pull request
Oct 31, 2026
…en back A session grows its query pool for concurrent reads, and every worker kept its graph caches, SQLite pages and heap until shutdown, so a server that had served one burst held that memory for the rest of its life. After five minutes with no work the pool now retires every worker that has been used and starts one fresh one, which is ready for the next call. A retired worker is asked to close: it closes its projects, runs a full collection and exits by itself, the path worker-teardown.ts measured as safe on Windows. Only one that hasn't exited after 5 seconds is terminated, through the pool's own start-aware path. Calls that arrive while the fresh worker starts are served in process, the same way as before the pool's first worker is up. Changed on top of the PR: - The idle delay is five minutes, not the PR's 60 seconds, so main doesn't get slower than 1.6.2. Agents routinely pause over a minute to edit or build, and the first explore on a fresh worker takes about 200 ms more. The memory still comes back when a session really goes quiet. - The recycle also arms when a burst's last worker reports ready after the last result. Before, that pool stayed at full size until the next call. - A warm worker that has served nothing is never recycled, so that readiness arming can't replace a clean worker over and over. - destroy() waits for retirements underway, so a worker is not left to end with the process. PR #2862 had this; the PR here did not. PR #2862 by @doomsday1906 made the same idle-worker change and is closed in favor of this one. On its own fork it measured idle RSS going from about 673 MiB to 426 MiB. Validation: - Red on main: six pool tests fail, including a real worker that retires with exit code 0 and is never terminated. Each added case was also proven red by undoing its line. - Windows 11 x64, on a busy machine: 480 child processes, each with four real workers on an excalidraw index and retiring right after a burst, all exited 0 (1,920 worker exits). The AGENTS.md crash did not reproduce here even with an owner terminate in place of the close (0 of 480), so this shows the path works, not that the crash can't happen. - excalidraw, four workers after a burst of explores: 611 MB RSS idle on main, 113 MB here. The first explore after a recycle took 299 ms against 93 ms on a warm worker, and the second was back to 92 ms. Original PR #1583 by @danusha2345. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Owner
|
Thanks for retiring excess query workers after idle bursts, @doomsday1906! The same idle-worker change has landed as #3126, from the worker commit in #1583, which came first. Your measured gain, idle RSS down from about 673 MiB to 426 MiB, is cited in that commit, |
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.
A session grows its query pool for concurrent reads but retains every worker's heap and SQLite connection until shutdown. After a burst, an otherwise idle MCP server therefore keeps paying for the peak worker count.
Retire excess workers after 60 seconds idle, retain one warm worker, and regrow through the existing dispatch path. Active/starting workers are preserved, intentional retirement does not consume the crash budget, and shutdown awaits retirements already underway. No new public setting or dependency.
Validation on Linux/WSL, based on main 44adef8: all 28 query-pool tests passed, including real compiled workers, multi-project dispatch, failed-open recovery and teardown. The three added regressions cover repeated shrink/regrow, busy/starting workers, and shutdown during retirement. An installed MCP comparison on the maintained fork previously dropped idle RSS from about 673 MiB to 426 MiB while preserving source queries.
Windows x64 and macOS arm64 native builds and focused regressions also passed (39 tests on each platform, including the native scaffold). Cross-platform validation checks the exact PR head commit. This PR excludes the maintained-fork installer, runtime identity and workstation configuration.