The drain window was only checked BETWEEN batches: one batch (sequential
LLM calls per page) plus lock acquire/release and the backlog count ran
unbounded, so window=120s runs were observed overrunning to 282.5s.
The drain now derives a real-time deadline signal (window timeout + the
Minion job.signal) and threads it through everything the loop awaits:
- extract-atoms-drain: signal-driven loop; hung count/batch/lock-acquire
are cancelled at the deadline; no post-window final count (remaining
reports null instead of overrunning); external worker abort rethrows
after the lock is released.
- extract-atoms phase: abortSignal forwarded to every gateway chat call,
discovery/count/idempotency queries, and putPage writes, plus a
cooperative between-item check (the bound on PGLite, whose query abort
only abandons the waiter — the default engine keeps a working drain).
Billable usage is recorded before the post-chat abort check; receipt +
rollup bookkeeping runs on a fresh 5s grace signal so committed atoms
never lose their cost trail; deadline-truncated runs don't count as
completed rounds (deadline_aborted detail).
- db-lock: tryAcquireDbLock/withRefreshingLock accept a deadline signal;
release routes through the direct session pool with a fresh grace
timeout; deadline-bound callers skip the unbounded same-host takeover
path (honest busy result; TTL stays the backstop).
- engines: putPage accepts an optional signal (Postgres cancels the
in-flight statement; PGLite pre-checks); executeRawDirect short-circuits
an already-fired signal before pool routing and bounds a stalled
direct-pool acquisition.
Takeover of PR #2752: keeps its signal-threading design, but retains the
putPage write path (so the chunker_version default from #2988 applies —
the PR's bulk INSERT bypassed it), drops the PGLite hard refusal in favor
of cooperative abort, and reverts the unsanctioned transcript-suppression
scope change (_transcripts: []).
Fixes#2750
Co-authored-by: panda850819 <panda850819@users.noreply.github.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>