Skip to content

fix(indexing): make reindex survivable and batch graph writes - #42

Merged
ManSio merged 4 commits into
mainfrom
pr/indexing-graph-batch
Sep 26, 2026
Merged

ManSio merged 4 commits into
mainfrom
pr/indexing-graph-batch

Conversation

@ManSio

@ManSio ManSio commented Sep 26, 2026

Copy link
Copy Markdown
Owner

What

Makes a full reindex survive concurrent/duplicate runs and removes the graph-write bottleneck.

  • fix(indexing): make reindex survivable, single-flight and recorded — split run-lock vs write-lock (removed the bounded-write deadlock), single-flight via begin_run(), durable reindex_ledger, terminal status + stall watchdog, cross-process embedder_lease.
  • perf(indexing): batch per-file graph writes (138x) — PropertyGraph.batch().
  • fix(indexing): make PropertyGraph.batch thread-owned — the 4 parse workers shared one batch transaction (cannot start a transaction within a transaction at finalize); the batch is now owned per thread.
  • docs(known-issues): archive auto-synced tail under 300-line limit.

Evidence

  • Live full reindex: completed in 603s, 10106/10106 rows; path-duplication 0.
  • Graph write throughput: 194 -> 26 826 entities/s (x138); negative control counts equal.
  • verify_clean_state.sh --no-clone on this branch: 1852 passed, 0 failed.

Tests

  • tests/test_graph_batch.py, incl. test_concurrent_batches_do_not_share_a_transaction.

MSCodeBase Agent added 4 commits September 26, 2026 09:34
Resolve the reindex crash-loop found on 2026-09-25 (py-spy + ledger evidence):

- deadlock: run() held the write RLock for the whole run while _bounded_link
  ran bulk_write on a new thread that re-acquired it. Split the locks: run()
  now holds begin_run() (a separate, non-reentrant Lock); _table_write_lock is
  acquired per DB operation.
- concurrency: auto-index and a manual trigger could run two indexers in
  parallel. begin_run() is now single-flight.
- embedder: idle-unload during a long parse let another workspace steal the
  fixed port. Added a cross-process embedder lease; the idle watchdog skips
  unload while a lease is fresh.
- observability: failures were swallowed ("Exception suppressed") and a job
  could stay "running" with no worker. Added a durable reindex ledger
  (reindex_ledger.jsonl), a terminal-status guarantee in finally, a stall
  watchdog, and full tracebacks.
- path contract: normalise rel paths to POSIX at every index-feeding point
  (full reindex wrote "\", incremental "/" -> ~2x index bloat).
- graph: thread-safe local RLock before the cross-process mutex.

Live-verified: full reindex completed (858s); index 19653 -> 10103 rows,
path-duplication 668 -> 0.
PropertyGraph.add_node/add_edge/delete_node each took a named mutex plus a
BEGIN/commit per row (~194 entities/s measured, E18). Add PropertyGraph.batch():
one cross-process lock and one transaction for a whole block, reusing the
connection; nested batches reuse the same transaction and errors roll back the
block atomically. indexer wraps the per-file symbol-graph update (remove +
add_*) in it.

Measured with exp_graph_write_throughput: A (per-call) 194 ent/s vs
D (batch, the real API) 26 826 ent/s -> 138x; negative control counts equal.

Guard: tests/test_graph_batch.py (counts match non-batch, atomic rollback on
error, nested batch = one tx, commit persists).
check_known_issues (E4.8 R4) failed at 475 lines. Moved the
auto-synced E13/E14/E16 entries appended since the last archive into
docs/archive/KNOWN_ISSUES_2026_09.md; the live file is back to 296.
The 4 parse workers shared one batch transaction, so the second
BEGIN IMMEDIATE raised "cannot start a transaction within a
transaction" during finalize (GraphSymbolResolver.resolve_all).

batch() now records the owning thread id; a foreign thread blocks on
the locks and opens its own batch, and the batched write paths verify
ownership before reusing the shared connection. Guard:
test_concurrent_batches_do_not_share_a_transaction (Barrier, 2
threads, no OperationalError, exact counts).
@coderabbitai

coderabbitai Bot commented Sep 26, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 86d7c665-cedb-4486-82b5-b73a49f3c6d4


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@ManSio
ManSio merged commit f44c46e into main Sep 26, 2026
12 of 13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant