Repository navigation
fix: run tool handlers off the event loop to unblock vector tools - #54
Merged
Merged
Conversation
…44) All ToolHandler.run_tool() implementations are synchronous. Before this fix, server.py called run_tool() directly in the async event loop, blocking it for the duration of every tool call — including LanceDB queries that carry their own heavy I/O. Fix: wrap the call with `await asyncio.to_thread(...)` so the event loop remains free. Also tighten VectorDB.get_stats(): replace the `.search().to_list()` full-scan (which builds a Python dict per row) with `.search().to_arrow()` + PyArrow compute, reducing per-row overhead when the table is large. Adds regression tests in tests/unit/vector/test_asyncio_regression.py that use a real (tiny) LanceDB table and a stub embedder to prove VectorSearchToolHandler and VectorDBStatusToolHandler complete when dispatched via asyncio.to_thread(). Note: the Windows-specific to_list() hang reported in issue #44 could not be reproduced or verified on macOS.
Adds logger.debug instrumentation so a future Windows hang report can
identify the stalled stage from ~/.cache/mcp-logseq/mcp_logseq.log:
vector/db.py
- search(): logs mode/top_k/filters before dispatch
- _vector_search/_keyword_search/_hybrid_search: log "to_list start"
and "to_list done in Xms, Y rows" around each q.to_list() call
- get_stats(): logs count_rows timing and to_arrow+count_distinct timing
vector/index.py (VectorSearchToolHandler)
- "embedding start (embedder=...)" before create_embedder+embed
- "embedding done in Xms" after embed
- "opening table at ..." before VectorDB.open_readonly
- "table opened in Xms" after open
- "query start (mode=...)" before db.search
- "query done in Xms, N results" after db.search
vector/index.py (VectorDBStatusToolHandler)
- "opening table at ..." / "table opened in Xms"
- "get_stats done in Xms" around db.get_stats()
No behavior change; no config switch required.
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.
Fixes #44.
Root cause was exactly as diagnosed in the issue: every
ToolHandler.run_tool()is synchronous and was called directly inside the asynccall_toolhandler, blocking the event loop. LanceDB's internal tokio runtime then deadlocked against it, so vector tools hung forever via MCP while working fine from plain Python.Changes:
server.pywraps the handler call inawait asyncio.to_thread(...)so the event loop stays free. This benefits all tools, not just the vector ones.VectorDB.get_stats()no longer materializes every row to count unique pages; it projects the page column to Arrow and usescount_distinct.to_list()call and handler stage, so if the hang ever reappears the last line inmcp_logseq.logidentifies the stalled stage.Note: the deeper Windows hang reported in the issue comments could not be reproduced on macOS. The
to_threadfix removes the runtime contention my analysis points to, but Windows verification is pending; the new debug logs are there to pinpoint it quickly if it persists.