perf: repo_map edge cache is globally invalidated by any file reindex #20
Labels
No labels
code-review
correctness
dos
performance
security
severity/high
severity/low
severity/medium
tech-debt
Kind/Breaking
Kind/Bug
Kind/Documentation
Kind/Enhancement
Kind/Feature
Kind/Security
Kind/Testing
Priority
Critical
Priority
High
Priority
Low
Priority
Medium
Reviewed
Confirmed
Reviewed
Duplicate
Reviewed
Invalid
Reviewed
Won't Fix
Status
Abandoned
Status
Blocked
Status
Need More Info
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
h-dv/code-index#20
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
The
repo_mapedge cache key includesMAX(files.indexed_at), so saving one file busts the entire cache. The nextrepo_mapcall then recomputes the whole enclosing-symbol graph from scratch — one correlated subquery per resolved ref — even though only one file's edges changed. On an actively-watched repo the cache effectively never hits during development.Where
crates/daemon/src/local_index.rs:302(compute_edges, O(resolved_refs) correlated subqueries)crates/daemon/src/local_index.rs:333-355(edges()cache key =(MAX(indexed_at), resolved_count, target_sum); full-key equality → global miss)crates/indexer/src/writer.rs:294writesindexed_at = nowon every reindexed file, so any real edit advances the key.Severity
Medium. The recompute is lazy (only on the next
repo_mapafter an edit),repo_mapis a user-invoked orientation tool (not on the write/watch hot path), and the per-ref probe usesidx_symbols_file_span(m0009). But the rebuild holds the connection checkout and the cache is defeated for anyrepo_mapduring active editing.Fix
Make edge computation incremental: key/store edges per
file_idand recompute only changed files' edge rows on reindex (the watcher already knows which files changed), or persist an enclosing-symbol edge table maintained by the indexer sorepo_mapreads it instead of recomputing. Likely needs a new migration (m0012+) for the edge table (m0001–m0011 frozen).Surfaced by the v0.5.8 ultradeep review (performance dimension, CONFIRMED).
Acceptance
repo_map; only the changed file's edges recompute.repo_mapranking output unchanged;repo_map_tests(incl.edge_cache_invalidates_on_resolution_change) stay green.Implemented on
fix/ultradeep-review-findings(commit0d79e7c), via fix option 2 (persistent edge table).The in-memory edge cache keyed on
MAX(files.indexed_at)— so any file save busted it and the nextrepo_maprecomputed the whole enclosing-symbol graph — is gone. A persistentsymbol_edgestable (migration m0013) is maintained by the indexer inrebuild_symbol_edges, called in the same transaction as resolution (alongside the #19 ref_count refresh, gated behind resolution actually running).repo_mapreads the table directly: no per-call recompute, no cache to invalidate, always current.On the incremental option (1): I deliberately chose full-rebuild-on-resolve over per-file incremental. An edge's
from_id(enclosing symbol) changes when any file targeting it is reindexed and re-resolved to a new symbol id, so per-file invalidation would miss cross-file edges. The rebuild is one indexed pass overrefs, comparable to resolution's own cost and only paid when refs changed — while makingrepo_mapreads unconditionally cheap.The edge SQL lives once in
index::SYMBOL_EDGES_SELECT(shared by the runtime rebuild; mirrored by the m0013 backfill so a freshly-upgraded DB has a graph immediately).CURRENT_VERSION → 13.Acceptance: editing a file no longer forces a read-time graph rebuild;
repo_mapranking output unchanged (repo_map_testsgreen, now populatingsymbol_edgesvia a test mirror of the rebuild). Tests:m0013_backfills_symbol_edges_from_resolved_refs(self-edge dropped),repo_map_reflects_rebuilt_edges, existinge2e_repo_map_rpcproves end-to-end maintenance. Closing.