Skip to content

fix(indexing): prune ignored directories and bound LanceDB index growth - #14706

Open
maphew wants to merge 20 commits into
Kilo-Org:mainfrom
maphew:fix/indexing-memory-lancedb-cleanup
Open

maphew wants to merge 20 commits into
Kilo-Org:mainfrom
maphew:fix/indexing-memory-lancedb-cleanup

Conversation

@maphew

@maphew maphew commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

What & why

Two defects behind codebase-indexing trouble on large repos.

  1. Scanner walked ignored directories (memory / OOM). scanner.ts passed FileIgnore.PATTERNS straight to glob's ignore option. Most entries are bare directory names (node_modules, dist, build, target, vendor, …), and a bare name matches nothing at depth, so ignored directories were never pruned — every file under them was enumerated and retained before the TS-level filter dropped them. On a repo with a large node_modules this is a large, avoidable memory/time cost. In the reported incident the indexing process was the one the WSL OOM killer SIGKILLed (exit 137; dmesg: Out of memory: Killed process … (kilo) anon-rss:6844612kB).

  2. Zero-retention LanceDB cleanup. optimizeTable() used cleanupOlderThan: new Date(), i.e. a 0 ms window. Lance treats that as an auto-cleanup policy that prunes every non-latest version; with concurrent writers it races, spams lance::io::commit … auto_cleanup_hook … manifest not found, and can leave the dataset unusable. This implements requested fix (2) in Code index LanceDB store corrupted by concurrent initialize()+optimize() across sessions/workers (no per-workspace lock) #14544 — use a real retention window instead of a zero/default policy.

Changes

  • src/file/ignore.ts: add FileIgnore.globs() — expands bare folder names into anchored prune globs (x/**, **/x, **/x/**).
  • src/indexing/processors/scanner.ts, src/indexing/processors/file-watcher.ts: use FileIgnore.globs() so ignored dirs are pruned, not walked.
  • src/indexing/shared/load-ignore.ts: discovery() delegates to FileIgnore.globs() (single source of truth; no behavior change for ignore-file discovery).
  • src/indexing/vector-store/lancedb-vector-store.ts:
    • prune with cleanupOlderThan: new Date(Date.now() - 10 min) instead of new Date();
    • compact in the background every ~40 modification operations (Lance recommends ~20; each upsert is delete+add), coalesced and non-overlapping, so it never blocks or races the write path;
    • serialize writes and compaction through the existing native() queue;
    • close() awaits any pending compaction.
  • .changeset/quiet-lions-index.md (patch, @kilocode/kilo-indexing).

Testing

  • bun run typecheck (kilo-indexing): clean. Full workspace bun typecheck incl. @kilocode/kilo-jetbrains: passing.
  • bun test --timeout 30000 (kilo-indexing): 515 pass / 0 fail, including new FileIgnore.globs() tests and updated compaction tests (background scheduling, non-blocking, coalescing, retention window).
  • oxlint on changed files: 0 errors.
  • Real-LanceDB smoke run: 120 upserts + background compaction + clean close, no deadlock; compaction merges fragments.

Not included

This does not add the cross-process, per-workspace index lock requested as fix (1) in #14544. Separate OS processes targeting the same workspace can still race; this PR only removes the unsafe zero-retention prune and makes the in-process path safe (native-queue serialization). #14544 should stay open for the lock.

Relates to #14544
Fixes #14705

kilo-auto/efficient on behalf of maphew


private async closeConnect(): Promise<void> {
// Let any background compaction finish before releasing the connection.
await this.pending

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: await this.pending only awaits the compaction promise that is current when closeConnect() runs. queueCompact()'s finally (line 611) can chain another compaction into this.pending when a write crosses the threshold while the first compaction is still running. closeConnect() resumes as soon as the promise it captured settles, so the chained compaction is never awaited and can execute after this.table = null / db.close(), i.e. a use-after-close (or it can reopen the connection through getDb()). That defeats the guarantee stated in the comment above this line. Consider awaiting a stable drain (e.g. loop while a per-instance in-flight flag is set, or track all chained runs) instead of a single promise snapshot.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

throw new Error(`Invalid point id format: ${bad}`)
// Serialize the delete+add commit against other stores' native work so
// concurrent batches cannot race each other or a compaction.
await native(async () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: native() is backed by the module-level nativeQueue (line 18), shared by every LanceDBVectorStore instance in the process. Routing each upsert's delete+add through it (and the background compaction at line 625) serializes DB work across all workspaces, not just within one store. If cross-workspace serialization isn't intended, consider a per-instance queue so one workspace's write/compaction backlog can't stall another's indexing.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

@kilo-code-bot

kilo-code-bot Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Incremental review of 4ec776f ("test(indexing): run case-sensitive prune regression on macOS"), the only PR-authored commit since the previously reviewed caeb36c. The commit only rewrites packages/kilo-indexing/test/kilocode/indexing/processors/scanner.test.ts; the remaining incremental delta (7.8.16 → 7.8.17 version bumps) comes from the upstream main merge / release commit and is not part of this PR's authored change.

The reworked regression test now creates only capitalized source dirs (src/Build, src/Target, Desktop) alongside lowercase ignored dirs at the root (build) and nested (pkg/build), so it no longer needs the runtime case-sensitivity probe or the early win32 return. It still fails if the scanner glob loses nocase: false, because src/Build/Main.ts, src/Target/Main.ts, and Desktop/App.ts would then be pruned while processed stays at 3; the added pkg/build assertion also keeps the nested anchored-pattern case covered. Verified against scanner.ts:151-162 (nocase: false, ignore: FileIgnore.globs()) and FileIgnore.globs() (packages/kilo-indexing/src/file/ignore.ts:65-77).

Targeted re-checks of the unchanged PR files found no carried-forward defects: closeConnect() still drains chained compactions in a loop (lancedb-vector-store.ts:585-597), and native() is now keyed per database path via nativeQueues (line 22). The previous scanner thread on scanner.ts:156 remains resolved by nocase: false at line 160.

Files Reviewed (1 PR file changed since the last review)
  • packages/kilo-indexing/test/kilocode/indexing/processors/scanner.test.ts - no issues
Previous Review Summaries (8 snapshots, latest commit caeb36c)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit caeb36c)

Status: No Issues Found | Recommendation: Merge

Incremental review of caeb36c (merge of upstream/main into the PR branch). The PR's own diff is unchanged since the previously reviewed commit 7cb5843: git diff 7cb5843..HEAD -- packages/kilo-indexing/src packages/kilo-indexing/test .changeset/quiet-lions-index.md is empty, and the only PR-authored delta since then remains the trailing newline in .changeset/quiet-lions-index.md. Version bumps and changeset deletions in the commit range come from the upstream main merge and are not part of this PR's diff. No new issues on changed lines.

Previously raised findings were re-verified against current code and remain resolved: scanner.ts:160 keeps nocase: false alongside FileIgnore.globs(), so capitalized source folders are still indexed; closeConnect() loops until no compaction is outstanding (lancedb-vector-store.ts:588-596), draining a compaction chained by queueCompact()'s finally; and native() is keyed per database path (nativeQueues, line 22), so stores no longer serialize against each other. The two old LanceDB bot comments are outdated (subject_type: line with line: null). The human comment on scanner.ts:156 describes the case-sensitivity regression that nocase: false fixed.

Files Reviewed (0 PR files changed since the last review)
  • No PR-authored source or test files changed since 7cb5843; upstream merge only.

Previous review (commit 7cb5843)

Status: No Issues Found | Recommendation: Merge

Incremental review of 7cb5843cd ("docs(changeset): add trailing newline"). The only PR change since the previous review is a trailing newline appended to .changeset/quiet-lions-index.md; the packages/kilo-indexing/package.json version bump in the commit range comes from the upstream main merge and is not part of this PR's diff. No new issues on the changed line.

Previously raised findings were re-verified against current code and remain resolved: scanner.ts:160 keeps nocase: false alongside FileIgnore.globs(), so capitalized source folders are still indexed; closeConnect() now loops until no compaction is outstanding (lancedb-vector-store.ts:589-592), draining a compaction chained by queueCompact()'s finally; and native() is keyed per database path (nativeQueues, line 22), so stores no longer serialize against each other. The two old LanceDB bot comments are outdated (subject_type: line with line: null). The human comment on scanner.ts:156 describes the case-sensitivity regression that nocase: false fixed.

Files Reviewed (1 file changed since the last review)
  • .changeset/quiet-lions-index.md - 0 issues

Previous review (commit d812f62)

Status: No Issues Found | Recommendation: Merge

Incremental review of d812f629f9 ("fix(indexing): skip case-coexistence scanner test on case-insensitive volumes"). The change replaces the macOS comment with a runtime probe: it creates Build and build under a temp dir and skips the test when readdirSync reports fewer than two entries, which correctly detects case-insensitive volumes (Windows and default APFS) rather than relying on platform alone. The probe cleans up via finally, the win32 fast-path early return is retained, and the test's own root/cacheDir cleanup is unchanged. The kilo-jetbrains/package.json version bump appearing in the commit range is from the upstream main merge, not part of this PR's diff. No new issues on the changed lines, and no unresolved findings carry forward (the scanner nocase: false regression is fixed; the two LanceDB comments are outdated with line: null).

Files Reviewed (1 file changed since the last review)
  • packages/kilo-indexing/test/kilocode/indexing/processors/scanner.test.ts - 0 issues

Previous review (commit 857393d)

Status: No Issues Found | Recommendation: Merge

Incremental review of 857393d9b99fe61bc472128f1a0b838ccb4362dc ("fix(indexing): exempt case-coexistence test on Windows"). The only change since the previous review is an early return on win32 in the case-coexistence scanner test, with a comment explaining that a case-insensitive Windows filesystem cannot hold both Build and build. The rationale is correct and the change is a sane platform guard for a test that cannot represent its fixture on Windows. The previously addressed scanner regression (nocase: false) is unchanged, and the remaining LanceDB comments are outdated/legacy (subject_type: line with line: null), so no findings carry forward. No new issues on the changed lines.

Files Reviewed (1 file changed since the last review)
  • packages/kilo-indexing/test/kilocode/indexing/processors/scanner.test.ts - 0 issues

Previous review (commit bd2899e)

Status: No Issues Found | Recommendation: Merge

Incremental review of bd2899eb736d8762ee21da21616c184eeb2464cf ("keep glob ignore matching case-sensitive on all platforms"). Adding nocase: false to the scanner glob restores the case-sensitive pruning that FileIgnore.match() uses, resolving the regression raised on scanner.ts:156, and the new scanner test exercises capitalized source folders alongside lowercase ignored dirs. No other PR file changed since the previous review, and both earlier LanceDB findings remain resolved (per-database native() queue; closeConnect() drains chained compactions). No new issues on the changed lines.

Files Reviewed (2 files changed since the last review)
  • packages/kilo-indexing/src/indexing/processors/scanner.ts - 0 issues
  • packages/kilo-indexing/test/kilocode/indexing/processors/scanner.test.ts - 0 issues

Previous review (commit 1ff7204)

Status: No Issues Found | Recommendation: Merge

Files Reviewed (9 files)
  • .changeset/quiet-lions-index.md
  • packages/kilo-indexing/src/file/ignore.ts
  • packages/kilo-indexing/src/indexing/constants/index.ts
  • packages/kilo-indexing/src/indexing/processors/file-watcher.ts
  • packages/kilo-indexing/src/indexing/processors/scanner.ts
  • packages/kilo-indexing/src/indexing/shared/load-ignore.ts
  • packages/kilo-indexing/src/indexing/vector-store/lancedb-vector-store.ts
  • packages/kilo-indexing/test/kilocode/indexing/file/ignore.test.ts
  • packages/kilo-indexing/test/kilocode/indexing/vector-store/lancedb-vector-store.test.ts

Previous review (commit 1fd4268)

Status: No Issues Found | Recommendation: Merge

Incremental review of 1fd42688464b3ccfb73e454b7d95209ec5031aff ("drain chained compactions on close, scope native queue per database"). Both previously reported findings are resolved: closeConnect() now loops until no compaction is outstanding, draining a compaction chained by queueCompact()'s finally, and native() is keyed per database path so one workspace no longer serializes against another. Re-verified against current HEAD; no new issues on the changed lines.

Files Reviewed (9 files)
  • .changeset/quiet-lions-index.md
  • packages/kilo-indexing/src/file/ignore.ts
  • packages/kilo-indexing/src/indexing/constants/index.ts
  • packages/kilo-indexing/src/indexing/processors/file-watcher.ts
  • packages/kilo-indexing/src/indexing/processors/scanner.ts
  • packages/kilo-indexing/src/indexing/shared/load-ignore.ts
  • packages/kilo-indexing/src/indexing/vector-store/lancedb-vector-store.ts
  • packages/kilo-indexing/test/kilocode/indexing/file/ignore.test.ts
  • packages/kilo-indexing/test/kilocode/indexing/vector-store/lancedb-vector-store.test.ts

Previous review (commit 01a6200)

Status: 2 Issues Found | Recommendation: Address before merge

The ignore-glob expansion (FileIgnore.globs()) and the retention-window change look correct; comments below focus on the new background-compaction lifecycle.

Fix these issues in Kilo Cloud

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 1
Issue Details (click to expand)

WARNING

File Line Issue
packages/kilo-indexing/src/indexing/vector-store/lancedb-vector-store.ts 578 closeConnect() awaits only the single this.pending snapshot; a compaction chained by queueCompact()'s finally (line 611) is not awaited and can run after the table/connection are released.

SUGGESTION

File Line Issue
packages/kilo-indexing/src/indexing/vector-store/lancedb-vector-store.ts 390 native() uses a process-global queue, so the newly serialized upserts/compaction serialize DB work across all workspaces, not just one store.
Files Reviewed (9 files)
  • .changeset/quiet-lions-index.md - 0 issues
  • packages/kilo-indexing/src/file/ignore.ts - 0 issues
  • packages/kilo-indexing/src/indexing/constants/index.ts - 0 issues
  • packages/kilo-indexing/src/indexing/processors/file-watcher.ts - 0 issues
  • packages/kilo-indexing/src/indexing/processors/scanner.ts - 0 issues
  • packages/kilo-indexing/src/indexing/shared/load-ignore.ts - 0 issues
  • packages/kilo-indexing/src/indexing/vector-store/lancedb-vector-store.ts - 2 issues
  • packages/kilo-indexing/test/kilocode/indexing/file/ignore.test.ts - 0 issues
  • packages/kilo-indexing/test/kilocode/indexing/vector-store/lancedb-vector-store.test.ts - 0 issues

Reviewed by deepseek-flash-latest · Input: 37.7K · Output: 6K · Cached: 347.9K

Review guidance: REVIEW.md from base branch main

maphew and others added 2 commits October 13, 2026 13:15
The scanner passed bare ignore folder names (node_modules, dist, ...) straight
to glob's `ignore` option, where they match nothing at depth, so every ignored
directory was walked and its paths materialized before the TypeScript filter
dropped them. Expand bare names into anchored prune globs in one shared helper
and use it from the scanner, watcher, and ignore-file discovery.

LanceDB's optimizeTable pruned with a zero-length retention window
(cleanupOlderThan: new Date()), which installs an auto-cleanup that races
concurrent writers, logs "manifest not found", and can corrupt the store.
Prune with a 10-minute retention instead, compact in the background every ~40
modification operations (Lance recommends ~20) without overlapping itself or
the write path, and serialize writes/compaction through the native queue.

Fixes Kilo-Org#14705
@maphew
maphew force-pushed the fix/indexing-memory-lancedb-cleanup branch from 1fd4268 to 1ff7204 Compare October 13, 2026 20:16
nodir: true,
dot: false,
ignore: FileIgnore.PATTERNS,
ignore: FileIgnore.globs(),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for addressing these indexing issues. One regression needs fixing before merge: on macOS, glob defaults to case-insensitive matching, while FileIgnore.match() checks folder names case-sensitively. The new prune patterns therefore exclude valid sources such as src/Build/Main.ts, src/Target/Main.ts, and Desktop/App.ts, which were indexed before this change. I reproduced an incremental scan removing four such existing files from a real LanceDB index (five rows became one).

Please preserve the existing case-sensitive semantics, for example with nocase: false in the scanner glob, and add a scanner regression test covering capitalized source folders alongside lowercase ignored folders. The current suite passes but does not cover this case.

@maphew maphew Oct 23, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed. The scanner glob now passes nocase: false (packages/kilo-indexing/src/indexing/processors/scanner.ts:160) so the anchored FileIgnore.globs() patterns keep the same case-sensitive semantics as FileIgnore.match(). Added the scanner regression test keeps capitalized source folders that match ignored dirs case-insensitively, covering src/Build, src/Target, and Desktop alongside lowercase build directories. It no longer skips case-insensitive volumes, so it exercises the macOS path directly; I confirmed glob on platform: "darwin" prunes src/Build/Main.ts by default and keeps it with nocase: false. Branch is synced with main; kilo-indexing typecheck and tests pass.

@github-actions github-actions Bot added awaiting-contributor Waiting for the contributor to reply priority:p1 High: fixes and regressions. Set from the title. Change by hand. and removed needs-triage Waiting for a maintainer to look labels Oct 16, 2026
maphew

This comment was marked as outdated.

@github-actions github-actions Bot added needs-triage Waiting for a maintainer to look and removed awaiting-contributor Waiting for the contributor to reply labels Oct 19, 2026
@maphew maphew closed this Oct 22, 2026
@maphew maphew reopened this Oct 22, 2026
@maphew

maphew commented Oct 23, 2026

Copy link
Copy Markdown
Contributor Author

Sweep note: branch is current with main (0 behind) and the review findings are resolved, but the latest CI run has a failure unrelated to this diff — AgentManagerPanelTest > test blank pr title keeps the row name in packages/kilo-jetbrains/frontend threw NoSuchElementException (run 39966714684), which then failed test (linux). Main's equivalent run passed. I do not have permission to rerun failed jobs; a maintainer can rerun it.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

community Opened by someone outside the Kilo team needs-triage Waiting for a maintainer to look priority:p1 High: fixes and regressions. Set from the title. Change by hand.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug(indexing): scanner walks ignored directories (node_modules) instead of pruning them, causing large scan memory spikes / OOM

2 participants