perf(queries): batched native Rust query methods for read path#698
perf(queries): batched native Rust query methods for read path#698carlos-alm merged 5 commits intomainfrom
Conversation
Add 4 batched NativeDatabase methods that run multiple SQLite queries in a single napi call, eliminating JS↔Rust boundary crossings: - getGraphStats: replaces ~11 separate queries in module-map statsData - getDataflowEdges: replaces 6 directional queries per node in dataflow - getHotspots: replaces 4 eagerly-prepared queries in structure-query - batchFanMetrics: replaces N*2 loop queries in branch-compare Add openReadonlyWithNative() connection helper that opens a NativeDatabase alongside better-sqlite3 for incremental adoption. Wire native fast paths with JS fallback in module-map.ts, dataflow.ts, structure-query.ts, and branch-compare.ts.
|
Claude finished @carlos-alm's task —— View job PR Review: Performance Optimization ⭐⭐⭐⭐☆Todo List ✅ Complete
Phase 5: Backlog Compliance ✅Zero-dep: ✓ - No new runtime dependencies. Builds entirely on existing tree-sitter + SQLite + Rust/napi infrastructure. Foundation-aligned: ✓ - Fully compliant with all principles:
Problem-fit: 5/5 - Directly reduces query latency for interactive commands ( Breaking: No - Purely additive. New Tier: Tier 1 - Zero-dep ✓ + Foundation-aligned ✓ Root Cause AnalysisProblem Being Solved: JS↔Rust boundary crossing overhead in read-heavy operations. Interactive queries were making 11+ separate napi calls instead of batching. Code Quality Assessment
Final Recommendation
This PR delivers exactly what it promises: ~70% reduction in query boundary overhead for the most common agent interactions. |
Greptile SummaryThis PR adds four batched Key observations:
Confidence Score: 5/5Safe to merge — native and JS fallback paths are behaviorally equivalent, resource cleanup is correct, and all test-filter logic is consistent. No P0 or P1 findings. All remaining comments are P2 style suggestions. The implementation correctly preserves output parity between native and JS paths, and the graceful degradation pattern works reliably. No files require special attention. crates/codegraph-core/src/read_queries.rs has one P2 suggestion about further optimizing batch_fan_metrics with an IN (...) query. Important Files Changed
Sequence DiagramsequenceDiagram
participant TS as TypeScript caller
participant Conn as openReadonlyWithNative()
participant BSQ as better-sqlite3 (db)
participant NDN as NativeDatabase (nativeDb)
participant SQL as SQLite file
TS->>Conn: openReadonlyWithNative(path)
Conn->>BSQ: openReadonlyOrFail(path)
BSQ->>SQL: open (readonly)
Conn->>NDN: NativeDatabase.openReadonly(path)
NDN->>SQL: open (readonly)
Conn-->>TS: { db, nativeDb, close() }
alt nativeDb available
TS->>NDN: getGraphStats / getDataflowEdges / getHotspots / batchFanMetrics
NDN->>SQL: batched SQLite queries (single napi call)
SQL-->>NDN: results
NDN-->>TS: typed struct
else JS fallback
TS->>BSQ: db.prepare(...).all() x N
BSQ->>SQL: individual queries
SQL-->>BSQ: rows
BSQ-->>TS: raw rows
end
TS->>Conn: close()
Conn->>BSQ: db.close()
Conn->>NDN: nativeDb.close()
Reviews (2): Last reviewed commit: "fix(native): resolve Rust compile errors..." | Re-trigger Greptile |
…ddress review feedback (#698) - Move get_graph_stats, get_dataflow_edges, get_hotspots, batch_fan_metrics into a #[napi]-annotated impl block so napi-rs can generate bindings for methods that take &self - Remove dead total_files computation from get_graph_stats (unused by TS caller) - Use SQL bind parameters for kind in query_outgoing/query_incoming instead of string interpolation - Replace unwrap_or(0) with map_err in batch_fan_metrics to propagate errors instead of silently returning zeros
|
Addressed all three P2 findings from the Greptile review:
Additionally fixed the Rust compile error: the 4 batched Also merged |
|
Claude finished @carlos-alm's task —— View job PR Re-Review: Addressing P2 Findings ✅Todo List
✅ All P2 Fixes Successfully AddressedP2 Fix #1 -
|
- Use fully-qualified rusqlite::Connection in inner function signatures since #[napi] macro generates a module scope that hides the type - Add explicit rusqlite::Row type annotations on closure parameters needed after switching from format!() to params![] bind parameters
Summary
NativeDatabasemethods that run multiple SQLite queries in a single napi call, eliminating JS↔Rust boundary crossings on the read pathopenReadonlyWithNative()connection helper for incremental native adoptionBatched methods
getGraphStats(noTests)module-map.tsstatsData()getDataflowEdges(nodeId)dataflow.tsdataflowData()getHotspots(kind, metric, noTests, limit)structure-query.tshotspotsData()batchFanMetrics(nodeIds)branch-compare.tsloadSymbolsFromDb()Impact
Targets ~70% of the remaining native performance gap on interactive queries (
codegraph stats,codegraph audit,codegraph query --dataflow,codegraph hotspots,codegraph compare).Test plan
cargo checkpasses (Rust compilation)npm run lintcleancodegraph statsproduces same output with native addon available vs WASM fallbackcodegraph query --dataflow <name>produces identical results