Repository navigation
fix(symbols): run symbol work in a spawned worker process - #54
Merged
Merged
Conversation
Symbol builds and queries are CPU-bound Python. In the shared daemon they held the GIL, so other tool calls waited behind a build: search_text took 17.0 s during a 17.1 s symbol build against 0.47 s alone. All symbol work now runs in one persistent worker process started with spawn: index checks, refreshes, bootstraps, explicit index jobs, symbol queries, outlines, list_repos, invalidate_cache, and prune_symbols. The daemon routes calls and receives small results. The worker points stdout at stderr and forwards its log events to the daemon's sinks, caps malloc arenas, and receives the shutdown stop event at start. A worker that outlives the shutdown grace is killed. A crashed worker is replaced and its in-flight calls return a retryable symbol_worker_restarted error. The native memory hooks move to lgrep.memory so the worker can use them without importing the server package.
A replacement worker counted snapshot tokens from 1 again, so a stale token from a lost worker could name another repository's snapshot. Tokens are now random. A failure reply is summarized unless it survives a pickle round trip, so an exception that cannot unpickle no longer makes the daemon discard a healthy worker. The symbol lane now carries quick lookups beside index checks, so it grows to eight threads and the worker to sixteen. Only FastMCP's outside-a-request error selects the in-process path. The operations doc names which tools report a lost worker as symbol_worker_restarted.
Contributor
Author
|
Live overlap timing on head 762f494, after the review fixes. Method: worktree server over stdio MCP, fresh symbol store,
The same harness on |
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
Symbol builds and queries are CPU-bound Python. In the shared daemon they hold the GIL, so every other tool call waits behind a build. After #53,
search_textagainst Vision still took 17.0 s during a 17.1 s symbol build, against 0.47 s alone.Change
All symbol work runs in one persistent worker process started with
spawn(src/lgrep/symbol_worker.py):index_symbols_folder/index_symbols_repojobs;search_symbols,get_symbol,get_symbols,search_references,get_file_outline,get_repo_outline;list_repos,invalidate_cache, andprune_symbols, which parse index bodies or evict the worker's index cache.The daemon routes calls on the existing symbol lane and receives small picklable results. The worker:
worker_pidfield;A crashed worker is replaced on the next call. Its in-flight calls return
code="symbol_worker_restarted",retryable=true. Unchanged: single-flight refresh per path, a cancelled query does not stop its build, explicit-job status, counters and credential redaction, and the in-process CLI/Python path (InProcessSymbolWorker). No dependency or storage-format change.The native memory hooks move from
lgrep.server.memorytolgrep.memory, so the worker can use them without importing the server package.Verification
tests/test_symbol_worker.py(11 tests) covers search during a real spawned-worker build, an empty stdout, crash replacement, the stop event, the shutdown kill, and retryable tool errors. The responsiveness test fails when the build runs in the daemon: its one overlapping search took 2.45 s, the length of the whole build.search_textagainst Vision during a fullindex_symbols_folderbuild (164 files, 88,218 occurrences), fresh symbol store:main(4a51c8d)Cost
The worker is a second process: an idle worker holds about 161 MB RSS. Importing
lgrep.storageloads LanceDB through the package__init__, which adds up to about 2 s to a cold worker start and part of that RSS. A follow-up covers it.