Skip to content

Views: call-graph reads answer within seconds instead of waiting for a full rebuild - #1338

Merged
ualtinok merged 20 commits into
mainfrom
land/callgraph-read-bound
Nov 3, 2026
Merged

ualtinok merged 20 commits into
mainfrom
land/callgraph-read-bound

Conversation

@aft-alfonso

@aft-alfonso aft-alfonso Bot commented Nov 2, 2026 •

Copy link
Copy Markdown

With views on, a call-graph read (aft_callgraph, aft_zoom with call-graph details, aft_inspect dead code) could block for a whole re-materialization of the checkout's call-graph view after files changed. One zoom waited 77 s, past the client's 60 s deadline.

  • A checkout's call-graph acquisition is now root-owned, cancellable, and runs as a single executor job per view. A read waits at most about 3 s, then answers with the existing "building" state. The next read uses the new graph once acquisition completes.
  • A publication fence stops an acquisition that finishes after its root was quiesced from publishing into the released root.
  • Native call-graph reads no longer wait on build permits.
  • Cancellation reaches every unfinished acquisition, including a completion tail that overlaps a new acquisition. A submitting worker never drops the executor's last owner on itself, so a teardown can't join a worker that's waiting on it.
  • Only call-graph acquisition takes the special maintenance slot. Other heavy maintenance work keeps its existing concurrency.

Tests: the unbounded re-materialization and the late publication after quiesce were reproduced first, and so were the three review findings: last-owner drop during submit, maintenance overlap, and cancellation of an overlapping tail. On changed-checkout fixtures, reads answered in 3.0 s and the next reads completed in 18–22 ms, for both standalone and daemon callers and zoom.

Gate: 1,372 executor, views, configure and context library tests, 140 call-graph, views and daemon-search integration tests, -D warnings, formatting and the Windows check. No agent-visible text, schema or tool-argument changes.


Autofix with [code]smith
Open in [code]smith · Need help on this PR? Tag @codesmith-bot with what you need.


Summary by cubic

With views on, a call-graph read no longer waits for the whole call graph to rebuild after files change. It returns the existing "building" state within about 3 seconds; the next read uses the new graph once acquisition completes. One zoom previously waited 77 s, past the client's 60 s deadline.

  • Acquisition is root-owned, cancellable, and runs off the request thread as one executor maintenance job; a timed-out read keeps the worker running rather than restarting it.
  • A publication fence blocks a late-finishing acquisition from publishing into a quiesced root.
  • Cancellation reaches all unfinished acquisitions, including completion tails overlapping a new acquisition, and a submitting worker never drops the executor's last owner on itself.
  • Only call-graph acquisition takes the special maintenance slot; other heavy maintenance keeps its existing concurrency.
  • Typed busy/building view errors are classified as planned and skip failure accounting; real errors still count.
  • Test fixtures await acquisition via a shared completion-driven waiter with a 120-second deadline; production reads keep the three-second budget, and the worker preserves the caller's test-only attribute policy.
  • Tests cover the unbounded re-materialization, late publication after quiesce, and the three review findings (last-owner drop, maintenance overlap, overlapping-tail cancellation). On changed-checkout fixtures, reads answered in 3.0 s and subsequent reads in 18–22 ms. No agent-visible text, schema, or tool-argument changes.

Written for commit 5607b0c. Summary will update on new commits.

View guided diff Turn on auto-fix

ualtinok and others added 20 commits November 2, 2026 23:25
Co-authored-by: alfonso-aft <289616620+alfonso-aft@users.noreply.github.com>
Run one checkout-view acquisition off the request thread. Share the existing CALLGRAPH_QUERY_WAIT across query-lock contention and receiving its result, retain the worker after timeout, and reuse existing Building replies for callgraph and zoom. Reverify queued results unless the checkout still reports their steady revision.

The controlled 64-file Git fixture blocked callers for 4214 ms and zoom for 4206 ms before the fix, returning only after the test released materialization. It now checks concurrent bounded reads and full answers after publication; existing retry/backoff assertions keep their original contracts.

Co-authored-by: alfonso-aft <289616620+alfonso-aft@users.noreply.github.com>
The new three-second request deadline can expire before SQLite's five-second WAL checkpoint wait. Keep the existing checkpoint-contention assertions intact: if the first request reports Building, wait for its background acquisition to fail and require the next reader to carry the same detailed busy diagnostic. The blocker stays installed throughout; no underlying contention claim is removed.

Co-authored-by: alfonso-aft <289616620+alfonso-aft@users.noreply.github.com>
Co-authored-by: alfonso-aft <289616620+alfonso-aft@users.noreply.github.com>
Co-authored-by: alfonso-aft <289616620+alfonso-aft@users.noreply.github.com>
Daemon acquisitions are Maintenance HeavyInit jobs using a weak root executor. Keep the heavy permit and maintenance limits, but exclude one running maintenance-heavy job from the actor's interactive capacity so same-root reads remain runnable.

Use the driver's PublishFence to serialize CAS with root lifecycle/generation changes. Cancel on quiesce, runtime clearing, and shutdown; check cancellation at materialization/member boundaries. Standalone views retain and join their managed worker handles after cancellation, and standalone process cleanup explicitly closes acquisition before exit. Reuse the existing Building replies and three-second request budget on both paths.

Regression fixtures hold a real v3 reservation, then quiesce the root, shut down standalone, or drop the executor. They preserve the old pointer and require released pins/reservations, prompt same-root reads, and no fallback after the daemon executor stops. Before the fix both lifecycle cases published a new generation after quiesce.

Co-authored-by: alfonso-aft <289616620+alfonso-aft@users.noreply.github.com>
Co-authored-by: alfonso-aft <289616620+alfonso-aft@users.noreply.github.com>
Views-on graph tool reads use PureRead admission now that their acquisition is a separate Maintenance HeavyInit job. Otherwise a one-permit executor parks later callgraph reads behind the builder instead of letting them return Building within the read budget. Views-off calls retain their old HeavyInit lane.

Exercise both daemon bound regressions through actual tool-call admission with one heavy permit, and retire a previously warmed standalone view when a root executor adopts its context. Retry the nonblocking scheduler census in the lifecycle fixture rather than treating a momentarily busy census mutex as a missing job.

Co-authored-by: alfonso-aft <289616620+alfonso-aft@users.noreply.github.com>
Co-authored-by: alfonso-aft <289616620+alfonso-aft@users.noreply.github.com>
Co-authored-by: alfonso-aft <289616620+alfonso-aft@users.noreply.github.com>
Wrap the temporary executor owner used for root submission in a guard that releases it off-worker, including unwind paths. The existing self-thread join skip does not prevent an indirect join cycle with a dependent worker.

Apply the separate in-flight slot only to the internal callgraph acquisition job; generic Maintenance/HeavyInit jobs retain their previous actor-cap concurrency. Track all unfinished acquisition tokens through completion tails and cancel them all, pruning completed records with a drop guard. Recheck standalone shutdown while holding its handle-registration lock.

All three focused regressions fail on the preceding implementation: worker-side last-owner submission waits for its dependent worker, unrelated heavy maintenance is serialized, and the earlier post-send tail loses cancellation.

Co-authored-by: alfonso-aft <289616620+alfonso-aft@users.noreply.github.com>
Port the incoming ready-reader helper to parking_lot and retain its total retry deadline when a background acquisition has no retry backoff. Arm the existing thread-local Git config timeout on the acquisition thread using a test-only source wrapper; preserve the WouldBlock, DatabaseBusy, unchanged-evidence and eventual-ready assertions.

Apply the landing comment clarification for the acquisition result waiting across an unreported temporary c.ts edit. No repository c.ts file or compatibility shim is introduced.

Co-authored-by: alfonso-aft <289616620+alfonso-aft@users.noreply.github.com>
Co-authored-by: alfonso-aft <289616620+alfonso-aft@users.noreply.github.com>
…scanner reads .take( as a cut)

Co-authored-by: alfonso-aft <289616620+alfonso-aft@users.noreply.github.com>
Use one completion-driven fixture reader with a 120-second deadline, acquisition-result waiting, import completion notification, retry-deadline events, and stateful timeout diagnostics. Production view reads retain the three-second deadline. Expose a doc-hidden AppContext fixture adapter so unit and integration requests share that waiter and pin the completed reader.

Migrate successful setup/final reads in views, copies, cap, semantic-fill, configure, and worktree fixtures. Leave timed reads, Building/refusal assertions and backoff checks on normal APIs. Include the landing scanner fix: assign executor = None instead of taking its Option.

Co-authored-by: alfonso-aft <289616620+alfonso-aft@users.noreply.github.com>
Route the remaining positive recovery, routed-root, idle/reaper, diagnostics, import zoom, and worktree reads through the shared fixture waiter. Keep their held-lock, Building, backoff, error, and timeout observations on normal APIs; no assertions are removed.

Co-authored-by: alfonso-aft <289616620+alfonso-aft@users.noreply.github.com>
Co-authored-by: alfonso-aft <289616620+alfonso-aft@users.noreply.github.com>
Skip all typed busy/building view errors before failure accounting, retaining acquisition timing and error diagnostics. Reuse the existing reserve/failure recorder while avoiding a second string classification for typed real failures; lexical/string-only callers retain their existing classifier path.

The regression uses opaque reasons for busy-only and building-only states, then a planned-looking reason with both flags false to prove real errors still count. Existing Building response assertions and agent-visible text remain unchanged.

Co-authored-by: alfonso-aft <289616620+alfonso-aft@users.noreply.github.com>
The broader native Windows run exposed four copy/base assertions after readiness was fixed: their known-attribute fixture is thread-local, so the asynchronous acquisition had lost its policy override. Capture and restore that test-only scope on the acquisition worker; production policy queries and read budgets are unchanged.

Add a controlled source regression that verifies the worker sees the caller's fixture policy. Keep the copy-cap, base reuse and eviction assertions intact.

Co-authored-by: alfonso-aft <289616620+alfonso-aft@users.noreply.github.com>
Co-authored-by: alfonso-aft <289616620+alfonso-aft@users.noreply.github.com>
@ualtinok
ualtinok added this pull request to the merge queue Nov 3, 2026
Merged via the queue into main with commit f0473c2 Nov 3, 2026
92 checks passed
@ualtinok
ualtinok deleted the land/callgraph-read-bound branch November 3, 2026 02:41
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