Skip to content

fix(cli): codegraph status opens the index read-only, and says when a rebuild is running - #2921

Closed
danusha2345 wants to merge 1 commit into
colbymchenry:mainfrom
danusha2345:fix/2913-status-read-only
Closed

danusha2345 wants to merge 1 commit into
colbymchenry:mainfrom
danusha2345:fix/2913-status-read-only

Conversation

@danusha2345

Copy link
Copy Markdown
Contributor

Fixes #2913.

What happened

codegraph status opened the database read-write, so DatabaseConnection.open ran the writer's open-time repairs on it: migrations, the FTS-trigger and secondary-index recovery an interrupted bulk load leaves behind, WAL maintenance. While a codegraph index was running, that recovery recreated the five edge indexes the indexer had dropped for its bulk load (beginBulkEdgeLoad). The indexer's next insertEdges transaction had already read its endpoint ids, so the schema change invalidated its snapshot and the write failed with SQLite extended code 517, SQLITE_BUSY_SNAPSHOT: "Failed to index: database is locked". The issue's reproduction, a writer in a bulk edge load that runs the real codegraph status between its endpoint read and its edge insert, shows exactly that; with { readOnly: true } at the same point the writer succeeds.

The change

Crash repair is unchanged: a read-write open, the writer's own, still recreates what an interrupted bulk load dropped.

Tests

__tests__/status-during-rebuild.test.ts (4 tests), against the built CLI as the issue's reproduction:

  • a writer holds a bulk edge load with rebuild.pid and writer.pid naming a live process; codegraph status --json answers with the statistics and rebuildInProgress: true, the dropped indexes are still dropped afterwards, and the writer's edge insert succeeds; endBulkEdgeLoad brings the indexes back;
  • a read-write open on the same state recreates them (the writer-side recovery is unchanged);
  • the text output names the running rebuild, and a lock with a dead pid is reported as no rebuild;
  • a project with no rebuild reports as before.

The first test fails on main at the index check (status recreated the five indexes).

Full suite on main 69af19a with the native kernel (CODEGRAPH_KERNEL_EXPECT=1): 9,072 passed, 39 skipped, 0 failed; tsc --noEmit clean.

🤖 Generated with Claude Code

… a rebuild is running

Status opened the database read-write, so the open ran the writer's repairs:
migrations, the FTS trigger and secondary-index recovery an interrupted bulk
load leaves behind, WAL maintenance. During a `codegraph index` that recovery
recreated the five edge indexes the indexer had dropped for its bulk load, and
the indexer's next edge write, whose transaction had already read its
endpoints, failed with SQLITE_BUSY_SNAPSHOT: "Failed to index: database is
locked" (colbymchenry#2913).

Status reads: it opens with `readOnly: true`, which leaves every repair to the
writer, as the MCP reader connections already do. When another live process
holds `rebuild.pid`, the report says so (`rebuildInProgress` in `--json`, a
warning in the text), since its numbers are a snapshot of a graph being
rebuilt. `isRebuildInProgress` is the check `assertNoRebuild` already made,
exported for a caller that reports rather than refuses.

The test runs the built CLI against an index whose writer holds a bulk edge
load with both locks: status answers, the dropped indexes stay dropped, and
the writer's edge insert succeeds; a read-write open still repairs them.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
colbymchenry pushed a commit that referenced this pull request Oct 27, 2026
… rebuild is running

Status opened the database read-write, so its open ran the writer's
repairs: migrations, the FTS-trigger and secondary-index recovery an
interrupted bulk load leaves behind, and WAL maintenance. During a
`codegraph index`, that recovery recreated the indexes the indexer had
dropped for its bulk load, and the indexer's next write, whose
transaction had already read, failed with SQLITE_BUSY_SNAPSHOT (517):
"Failed to index: database is locked".

Status now opens with `readOnly: true` and leaves every repair to the
writer, as the MCP reader connections already do. When another live
process holds `rebuild.pid`, it says so: a warning in the text output and
`rebuildInProgress` in `--json`, the only new key. `isRebuildInProgress`
is the check `assertNoRebuild` already made.

Added when landing it:
- A read-only open migrates nothing, so status on an index built by
  1.4.0 or older (schema 7, no `unresolved_refs.status`) failed with "no
  such column: status". The pending-reference count counts every row
  when the column is missing, as the v8 migration marks them pending.
- While a rebuild runs, status skips the advice meant for an index at
  rest: the interrupted-run references, "run sync", "re-index", and the
  oversized-WAL warning, which mid-rebuild all describe the live run.
- Status no longer reclaims a killed session's oversized WAL, so the
  warning names the next `codegraph sync` instead of status itself.
- Tests: the issue's exact interleaving (status inside the writer's edge
  transaction, as in PR #2916's test), a pre-v8 index, and the
  mid-rebuild advice.

Validation (Windows 11, Node 22.22):
- The issue's reproduction with our build of main 69af19a: status inside
  the transaction exits 0, the writer fails with errcode 517 and the five
  dropped edge indexes come back. With this change the insert succeeds
  and the indexes stay dropped.
- Real rebuilds of excalidraw 95307fa1 (716 files) with `codegraph
  status` looping beside them, 3 with default settings and 3 with the
  resolution window forced: main failed 6 of 6, each with "store worker:
  database is locked" and an empty index; this change failed 0 of 6 over
  46 status runs, with identical complete graphs (14,236 nodes, 61,210
  edges).
- status-during-rebuild.test.ts: 7/7 pass. With main's sources 6 of 7
  fail; without `readOnly` 3 fail, one with "database is locked"; with
  the PR as written the pre-v8 and mid-rebuild cases fail.
- Indexes built by v1.6.2 (schema 11) and v1.4.0 (schema 7): the same
  numbers as main, and the schema is left as it is. Interrupted-index
  warnings (indexing, partial, failed) are unchanged.

Original PR #2921 by @danusha2345.
Fixes #2913

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@colbymchenry

Copy link
Copy Markdown
Owner

Thanks @danusha2345! This landed in #2930 with you as the commit author: codegraph status now opens the index read-only and says when a rebuild is running. We added three things on top. An index built by 1.4.0 or older has no per-reference status column, so a read-only status failed there with "no such column: status"; it now reads such an index as it is. While a rebuild runs, status now skips the at-rest advice ("run sync", "re-index", the interrupted-run and WAL warnings) that contradicted your new warning. And the WAL warning now points at codegraph sync, since status no longer reclaims the log. We also added the issue's exact interleaving as a test. It ships in the next release.

danusha2345 pushed a commit to danusha2345/codegraph that referenced this pull request Oct 28, 2026
…mchenry#2972; 10 landed PRs yield to main; EXTRACTION_VERSION 56)

Main landed ten more of our PRs as our own commits with the maintainer's additions
(colbymchenry#2923 ← colbymchenry#1936, colbymchenry#2924 ← colbymchenry#1951, colbymchenry#2925 ← colbymchenry#1939, colbymchenry#2930 ← colbymchenry#2921, colbymchenry#2940 ← colbymchenry#1736,
colbymchenry#2942 ← colbymchenry#1947, colbymchenry#2950 ← colbymchenry#1952, colbymchenry#2961 ← colbymchenry#1954, colbymchenry#2965 ← colbymchenry#1942, colbymchenry#2972 ← colbymchenry#1933) and the
module-path half of colbymchenry#2308 (colbymchenry#2955). Where the integration carried our version, main's
wins: the enclosing-scope member pick and its tests, the Go and Java receiver typers,
the delegating-call self-loop rule, the duplicated-module copy confidence, the C #if
collapse, the module-path resolution of Rust paths, the trail/path hardening in
utils.ts. Kept beside main: colbymchenry#1875's Verilog hooks and status fields, colbymchenry#2839's
implementations command, colbymchenry#1923, colbymchenry#2922, colbymchenry#2258, colbymchenry#1761, colbymchenry#1583, the bare-call half of
colbymchenry#2308 (rust-use-bindings.ts). CHANGELOG is main's plus the open PRs' bullets.

EXTRACTION_VERSION 56: the C #if group handling (colbymchenry#2940) and the kernel change.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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.

codegraph status repairs indexes during a live rebuild, causing SQLITE_BUSY_SNAPSHOT (517)

2 participants