Repository navigation
progress: detach and bound the build progress read from its observer - #199
Conversation
A poll cancelled mid-read closed the reserved verdict session and left the build's failure verdict indeterminate; observers also queued without limit behind a stalled poll.
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
🤖 Review 1/2 of I read the PR's own change against its merge base ( The fix is correct for the failure it names. The build's progress read now runs under Non-blocking1. A caller whose context ended before it called
|
| Mutant | Caught by |
|---|---|
Build read under the observer's ctx instead of the detached one |
TestProgressRunsTheBuildReadDetachedFromTheObserver and the integration test |
WithTimeout(ctx, …) (bounded but still cancellable) |
the same two |
| Detached read with no deadline | TestProgressRunsTheBuildReadDetachedFromTheObserver |
Observer waits for the gate unconditionally (takePollGate) |
TestProgressGivesUpWaitingForThePollGateWhenTheObserverEnds (the second poller reaches the session and the fake panics) |
| Give-up path returns a nil error | TestProgressGivesUpWaitingForThePollGateWhenTheObserverEnds |
| Give-up path returns a zero snapshot instead of the last-known state | TestProgressGivesUpWaitingForThePollGateWhenTheObserverEnds |
progressReadTimeout halved |
survives (unit and integration): finding 2 |
progressReadTimeout set to one hour |
survives (unit only): finding 2 |
WorkSource read detached from the observer |
survives: finding 3 |
- Both new unit tests and the integration test are real tests of the behaviour: the integration test holds the read executing on the server (
wait_event = 'PgSleep') before it cancels the observer, and asserts the backend PID is the same afterwards. A detached read that did not hold the session would fail it. - The handoff fences wait only on a bounded poll now: build reads end at
progressReadTimeoutat the latest, source reads under their contract. I found no path where the executor's verdict waits on an observer. - The docs agree with the code on the three points the PR changes: gate wait bounded by the observer, build read detached and bounded, source read under the observer. I found no remaining "polling lifetime is the caller's context" wording in
docs/orREADME.md. - Probes and mutations were run in a scratch worktree and restored; nothing was left behind.
This review was generated by Claude Code (claude-sonnet-5-5).
|
🤖 Review 2/2 of Through the two lenses this is a good change, and its documentation is better than most: Non-blocking1. Two different "the deadline passed" outcomes from
|
aparajon
left a comment
There was a problem hiding this comment.
🤖 Stamping with comments: 0 blocking, 6 non-blocking (see the two review comments above).
This stamp was left by Claude Code (claude-sonnet-5-5).
… its caller Address the adversarial review of #199: - Progress checks ctx after taking the poll gate, as CancelBuild does, so a caller whose context already ended reads nothing. - Both give-up paths return ErrProgressNotRead wrapped with the context error, so a caller tells "nothing read" from a detached read that hit its own deadline. - The godoc and progress-report.md state that Progress can outlast ctx by up to a minute once the read is issued, and the progressReadTimeout margin's pool caveat. - Tests pin an ended context reading nothing, the read deadline above dbconn.DefaultStatementTimeout, and the WorkSource read under the observer's context. - LK-2's progress-read sentence moves to its own paragraph with an Enforced pointer; low-level-design.md points at the bound.
|
🤖 Adversarial review response — created by Kiran's code review agent (Amp, Claude Opus 4.6) — pull/199, follow-up commit 6 non-blocking findings across two reviews of
Source: 6230643805, 6230644295, 6230491121 (automated run, no review) at head |
A progress poll whose caller gives up mid-read no longer destroys the session the index build's failure verdict runs on, and a poller no longer waits forever behind a stalled poll.
Why
Tracker.Progressread a concurrent index build's progress on the executor's reserved verdict session under the observer's own context. pgx closes a connection whose query's context ends mid-read, so an orchestrator's poll request timing out killed that session; a build that then failed got an indeterminateInvalidIndexErrorinstead of a classified one, which blocks automated recovery. Every observer also waited on the poll gate without limit, so one stalled poll hung every other poller (LK-2).CancelBuildalready avoids both hazards;Progressdid not.What
progressReadTimeout(twicedbconn.DefaultStatementTimeout, one minute), so on apkg/dbconnpool with the default or a lowerstatement_timeoutthe server ends a slow read with a query cancellation and the session survives; the client deadline only backstops a network that has stopped answering. A pool configured above that bound, or one with no sessionstatement_timeout, reaches the client deadline first. An observer whose context ends mid-read waits for that read to return, soProgresscan outlast its context by up to a minute; the godoc and progress report say so.progress.ErrProgressNotReadwrapped with its context's error, having read nothing; a deadline error without the sentinel came from the read itself (the reserved session stopped answering). The handoff fences still wait unconditionally, so the executor's verdict never depends on an observer.WorkSourceis still read under the observer's context, as its contract says.pkg/progressand the tests),SAFETY.md, the progress report's polling semantics, and the low-level design's polling pointer updated (upholds LK-2 and the reserved-session rationale).dbconn.DefaultStatementTimeout, and theWorkSourceread under the observer's context.Exported API changes
progress.ErrProgressNotRead— new sentinel, wrapped together with the caller's context error whenProgressissued no read.Before / after
An orchestrator polls progress with a 1-second request timeout while
CREATE UNIQUE INDEX CONCURRENTLY idx_email ON users (email)runs, and the build later fails on duplicate emails. The request times out while its progress read is in flight. Only what happens to the reserved session differs.A second poller arriving while the first poll is stalled: before, it waited forever for the poll gate; after, it returns its own context's error at its deadline, with the last-known snapshot.