perf(callgraph): match the cold-build path's write mechanics on incremental refresh - #246
Conversation
|
CI is red on two Windows jobs. Both fail on 28 of 30 checks green, including all three Linux integration shards, all three macOS shards, Windows shard 2/3 and 3/3, Windows deny-warnings, MSRV, and every E2E job. Job 1 —
|
|
Both halves of your CI analysis verified and acted on — this is exactly the evidential split I'd want (byte-identical-on-base vs same-job-different-test, argued separately):
On the change itself: reviewed at full diff. The Please rebase onto current main (≥ |
bfaa0d9 to
967bedf
Compare
|
Update on your job-2 analysis — the "rotating flake" story decomposes further, and mostly into our own bugs:
Please rebase once more onto ≥ |
|
Rebase is on The one remaining red is shard 1/3 (Windows), and I need to correct what I told you about it: it isn't a flake. I read rotating test names as timing and stopped there. The log on this run: Deterministic path-separator mismatch. The test builds its expectation as Two distinct failures live in that job and fail-fast surfaces whichever trips first, which is what produced the rotation I mistook for flakiness: The process-exit timeout is genuinely timing-dependent and still open; the astro one is a real bug that was never going to pass. Fix sent as #248, off With #248 in, shard 1/3 should be down to just the process-exit timeout. Not asking you to reorder anything — merge #248 first and I'll rebase #246 onto it for a clean run, or take #246 as-is since the remaining red is provably unrelated to it. Your call. |
…mental refresh The incremental refresh path did per-edit work the cold build already avoids: it held the write transaction across file reads and tree-sitter parsing, re-prepared every insert, scanned dispatch_hints for file-column filters, and issued two statements per ref id when deleting. Reported as cortexkit#214 (findings 1-4). The fifth finding there — the full-graph re-read via ProjectIndex::from_db_and_callers — is deliberately not addressed here; it is an architectural change and wants its own design. No behaviour change: same rows, same results, same ordering.
967bedf to
85f6728
Compare
|
Rebased onto Closed #248 as superseded. We landed the same diagnosis about two minutes apart — genuinely concurrent, not duplicated work. Your normalize-at-assertion approach and my build-the-expectation-like-production approach differ only if the product ever emits a mixed spelling; noted it on that PR and left it there rather than making it an argument. No apology needed on the churn — the collisions were the useful part. Both Windows failures were real bugs sitting in shard 1/3, and neither had produced a signal until something forced a run against them. If the shard is chronically red, nobody reads it, and the next real regression there is invisible too; two fixes in one evening is the shard becoming a working detector again. One thing worth keeping from it, since it cost us both a round: I called the shard a flake from the shape of the evidence — rotating test names, moving cancel point — without opening the log to see whether the underlying failures were the same. Rotation is a fail-fast artifact, not a diagnosis; it looks identical whether the failures are timing or deterministic. The log was one click away and would have named the path mismatch immediately. Worth a habit on chronically-red jobs: read the actual assertion before classifying, because the cheap read of the pattern is the one that lets a deterministic bug hide behind the word "flake." That leaves the |
|
Merged. Thanks for the persistence through three rebases — the PR itself was mergeable on day one; everything in between was our own Windows test debt that your CI analysis kept surfacing (and your #248 close was graceful — same diagnosis, two minutes apart). Ledger for #214: this lands findings 2–5 (transaction scoped to DB work, prepared-statement reuse, |
Four of the five findings from #214. One file, no new dependencies.
Finding 1 from that issue — the full-graph re-read via
ProjectIndex::from_db_and_callers— is deliberately NOT here. It's architectural and you may well have a design in mind; mixing it in would make this unmergeable as a unit. This is the mechanical subset only.What changed
Write transaction no longer spans file I/O and parsing.
build_file_extract(read + tree-sitter parse) now runs before the transaction opens, collecting intochanged_extracts/caller_extracts; the transaction then does only DB work. Previously the write lock was held across every read and parse in the batch.Incremental inserts reuse the cold-build prepared statements.
ColdBuildInsertStatementsis constructed once per refresh transaction instead of the path issuing per-rowtx.execute. Statement preparations per refresh drop fromF+N+D+H+B+R+Eto 7.dispatch_hintsgets an index onfile. Two queries filter on that column; the only index was onmethod_name. Added at both schema sites —initialize_schema(fresh/open-timeCREATE IF NOT EXISTS) andcreate_cold_build_secondary_indexes(cold-build recreation), plus the matching drop list, so a freshly-created DB and a rebuilt one don't diverge.delete_ref_idsreuses two prepared statements instead of re-preparing two per id: 2R → 2 per call.No public signature changed.
aft_callgraphimpact:load_file_rowhas one refresh caller,ref_ids_depending_onthree plus a test.One behaviour change, corrected from the original description
I originally wrote "no behaviour change." That was wrong, and you caught it on review — thanks.
clear_backend_state_for_fileis now called on the refresh paths (2 call sites removed from the old inline path, 3 added in the restructured one; net +1). It is required by the mechanics switch: the prepared cold-build inserts writebackend_file_staterows with a plainINSERT, so the row must be cleared first. It is also a correctness improvement in its own right — backend rows now refresh with the file instead of going stale.Everything else is mechanics-only: same rows, same results, same ordering.
Verification
Rebased onto
10d6d20c(picks up thegh_shimXDG fixture fix).Identical failure names to unmodified base in the same checkout (
post_edit_wait_does_not_start_a_server_on_a_cold_root,native_sandbox_pty_denies_outside_write_and_renders_screen— both environmental here).cargo fmt --checkandbun run lintclean.Worth flagging for your CI's sake: this was first gated in a linked worktree, where 19
callgraph_test::*tests fail as environment artifacts — a secondary checkout is a borrow-only artifact root, so the persisted callgraph store is unavailable and those tests fail identically on base. That means the tests covering the code this PR changes do not run in a worktree at all. I re-ran them in the artifact-owner checkout to get a real signal:If your CI runs the suite in a linked worktree anywhere, this class of change is currently ungated there.
Performance evidence
Mechanical rather than benchmarked — there's no callgraph refresh benchmark in-tree and building one is a bigger piece of work than the fix:
F+N+D+H+B+R+Edelete_ref_idspreparationsdispatch_hintsfile-filtered queriesHappy to split this into four commits if you'd rather review them separately, or to drop any individual piece.
Greptile Summary
The PR reduces incremental callgraph refresh transaction duration and statement-preparation overhead while adding the missing file index for dispatch hints.
dispatch_hints.file.Confidence Score: 5/5
The PR appears safe to merge.
No blocking failure remains.
Important Files Changed
Flowchart
%%{init: {'theme': 'neutral'}}%% flowchart LR F[Changed files] --> R[Read freshness and dependencies] R --> P[Parse changed files and callers] P --> T[Open SQLite transaction] T --> D[Delete superseded rows and backend state] D --> I[Insert extracts through prepared statements] I --> E[Resolve references and dispatch edges] E --> V[Bump projection revision] V --> C[Commit under lifecycle fence]Reviews (3): Last reviewed commit: "perf(callgraph): match the cold-build pa..." | Re-trigger Greptile
Context used: