Skip to content

fix(code-index): delete a reindex's stale rows in one pass per FTS table - #26

Merged
wongk merged 2 commits into
mainfrom
fix/incremental-reindex-batched-deletes
Sep 19, 2026
Merged

wongk merged 2 commits into
mainfrom
fix/incremental-reindex-batched-deletes

Conversation

@wongk

@wongk wongk commented Sep 19, 2026

Copy link
Copy Markdown

Problem

An incremental reindex of symphony-alpha ran for more than 10 minutes and never finished.

The FTS5 tables (file_line_fts with 12.4M rows, symbol_fts, symbol_trigram, file_path_trigram) are keyed by rowid only; file_path and symbol_id are UNINDEXED columns. So _delete_file_index, run once per changed file, did a full scan of each table for every file: about 2.2 s per file. After this morning's pulls, 556 files were stale, which is about 20 minutes of scanning.

The autosync subprocess is killed at 600 s. Every run died before committing, the next run started over, and the live index had not committed in hours.

Fix

  • _delete_files_index removes a whole batch's rows with one statement per table (… IN (SELECT value FROM json_each(?))). That is one scan per FTS table per run. The regular tables are indexed on these columns, so batching costs them nothing. All three call sites use it.
  • _excluded no longer resolves each path when there are no exclude patterns (2.3 s per run).
  • The incremental loop resolves each directory once, not every component of every file (_dir_cached_relpath). Its output is identical to _safe_relpath on all 19,749 files of two real repos, and a symlink case is tested separately.

Results

symphony-alpha, measured on a copy-on-write clone of the live index. The machine was under heavy load, so ranges are given.

files changed before after
0 ~5 s 5–8 s
5 ~16 s 9–12 s
50 ~2 min 13–15 s
~830 (after pulls) never finished (killed at 600 s) 72–91 s

Tests

  • A structural test traces the SQL of an incremental run with 7 stale files and asserts one delete per FTS table. On main it fails with "7 stale files cost 28 FTS scans".
  • A correctness test checks that no table keeps rows for a changed or removed file, and that an untouched file is unchanged.
  • The core and code-intel suites pass. The one failure, test_index_pool_does_not_fork_live_parent_state, fails on main too (macOS uses spawn).

Related

One of three independent fixes for code search failing on symphony-alpha: fix/code-engine-autosync-leak, fix/incremental-reindex-batched-deletes, fix/search-reads-during-reindex. Each is based on main, and all three merge cleanly in any order (checked with git merge-tree, and their new tests pass together on an octopus merge).

wongk and others added 2 commits September 18, 2026 16:06
The FTS5 tables are rowid-keyed, so deleting a file's rows by file_path or
symbol_id scans the whole table. Done per file, a symphony-alpha reindex
cost ~2 s per changed file; the backlog after a pull outran the autosync
subprocess's 600 s timeout and never committed. Also stop resolving every
path component per file on each run.

Co-Authored-By: lemoncrow <302591943+lemoncrow-agent[bot]@users.noreply.github.com>
@wongk
wongk merged commit f005c54 into main Sep 19, 2026
15 of 16 checks passed
@wongk
wongk deleted the fix/incremental-reindex-batched-deletes branch September 19, 2026 13:53
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