fix: watch loop holds one persistent DB connection instead of open/close per batch - #43
Draft
pradeepmouli wants to merge 1 commit into
Draft
Conversation
…ose per batch Root cause of the sittir 54GB graph-bloat incident: infigraph never issues an explicit CHECKPOINT, and lbug's forceCheckpointOnClose=true means every Database close forces a full checkpoint. The watch loop's open_transient opened a fresh connection on every batch flush (~1/sec during active editing) and on every file-removal event, so a multi-hour editing session turned into thousands of forced checkpoint cycles. Empirically confirmed ~3GB of write amplification from a single open/close cycle at 54GB scale. Fix: watch_project_with_periodic now holds one connection open for the whole watch session on non-Windows platforms (watch_db/poison_watch_db), reopening only if an operation on it errors -- e.g. if the underlying database file gets replaced out from under it by a concurrent `infigraph index --full` elsewhere. The original per-batch open/close behavior is kept Windows-only, since Windows mandatory file locking (which prevents a second concurrent connection while another handle is open) was open_transient's sole original rationale. Verified empirically: started a real watch session and confirmed via lsof that the graph file's fd (same inode) stays open across two separate batches rather than closing and reopening between them.
pradeepmouli
requested review from
johnintuit,
murari316 and
sandeep-mewara
as code owners
July 30, 2026 02:10
pradeepmouli
marked this pull request as draft
July 31, 2026 15:01
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Relates to #46
Summary
Root-caused a real-world incident where
infigraph watchgrew a project's.infigraph/graphfile to 54GB over a long editing session (logical content was ~58k symbols; a single open/close cycle on the bloated file showed ~3GB of write amplification, confirmed via an APFS CoW clone before cleanup).Root cause:
infigraphnever issues an explicitCHECKPOINT, and lbug/Kuzu'sSystemConfig::default()hasforceCheckpointOnClose = true— everyDatabaseclose forces a full checkpoint. The watch loop'sopen_transientopened a brand-new DB connection on every batch flush (roughly once per second during active editing) and on every file-removal event, so a multi-hour session turned into thousands of forced checkpoint cycles. The underlying storage engine's page allocator requires one contiguous free range per allocation and can't shrink an interior free region — so under sustained small-write churn at scale, this pattern is a plausible driver of unbounded file growth (independent of actual logical data growth).Fix
watch_project_with_periodicnow holds one DB connection open for the whole watch session on non-Windows platforms (watch_db/poison_watch_db), reopening only if an operation on it errors — e.g. if the on-disk database gets replaced out from under it by a concurrentinfigraph index --fullelsewhere. Windows keeps the original per-batch open/close behavior, since Windows mandatory file locking (which prevents a second concurrent connection while another handle on the same file is open) wasopen_transient's sole original rationale — that comment is preserved and updated in the diff.Also hardened
groups_watch_perf.rs'sgroup_indexassertion: it previously assumed the explicitgroup_indexcall always performs the (re)indexing work, but auto-watch (triggered by earlier calls in the same test) can legitimately have already indexed both repos in the background by the time it runs, in which caseindex_groupcorrectly reports 0 repos needing work rather than re-listing them by name. Both outcomes mean the data is indexed (already confirmed viagroup_queryearlier in the test) — the assertion now accepts either. This is a genuine, if narrow, pre-existing race between the test's assumptions and background auto-watch timing; this PR's connection-reuse change appears to make the background indexing complete measurably faster/more reliably, which increases how often it wins the race (observed empirically: before this test fix, this specific branch failed with the "already indexed" signature in 3 of 6groups_watch_perfruns, vs. 0 of 4 on an unmodifiedmainbaseline with a matched build).Verification
infigraph-core,infigraph-cli, andinfigraph-mcptest suites green (the only unrelated failure,compression_eval's search-savings assertions, is a pre-existing environment quirk when tests run from a directory nested under an actively-watched parent repo — unrelated to this diff, not reproducible in a normal checkout).fmt/clippyclean.infigraph watchsession and confirmed vialsofthat the graph file's file descriptor (same inode) stays open across two separate batches rather than closing and reopening between them.Test plan
cargo test -p infigraph-core— all passcargo test -p infigraph-cli— all passcargo test -p infigraph-mcp(excluding the pre-existing unrelatedcompression_evalenvironment quirk noted above) — all passcargo fmt --all -- --check/cargo clippy --all-targets -- -D warnings— cleanlsof