Close queue.wait at dequeue so it measures a wait #120
No reviewers
Labels
No labels
autonomy
async-consult
autonomy
epic
autonomy
headless
autonomy
live-collab
coherence-core
priority
P0
priority
P1
priority
P2
priority
P3
priority
P4
qa-fixture
role/ai
role/creator
role/design
role/director
role/engineer
role/exec
role/human
role/ops
role/qa
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
coilyco-flight-deck/agent-proxy!120
Loading…
Reference in a new issue
No description provided.
Delete branch "queue-wait-measures-the-wait"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Bug
Fixes #105.
The issue opened on a 16.6s gap between
queue.wait's p50 and theresilience.attemptp50 inside it, then settled it by measuring 29 traces individually:queue.waitrangequeue.waitis a sibling ofresilience.attempt, not its parent, and it stayed open for the whole request - trackingrequest.chatto within a millisecond at every point. The 16.6s gap was a percentile artifact (resilience.attemptemits one span per attempt, so retries skew its population).That cost the investigation a wrong turn and a retracted claim in two other issues. It also makes the span unusable for the question its name invites: a saturated proxy and a slow model look identical when the span tracks total request duration either way.
Fix
Jobcarries adequeuedfuture the worker resolves on pickup.submitwaits on that alongside the result future, so a job that fails before any worker takes it cannot leave the wait hanging.request.chat - queue.wait - resilience.attemptnow decomposes.resilience.attemptandrequest.chat. In their place:agentproxy.queue.admitted.asyncio.waitdoes not pass cancellation through to the futures it watches, sosubmitnow cancels the job future by hand. Without that a cancelled waiting job kept its queue slot, whichtest_cancelled_waiting_job_releases_queue_slotalready pins.How to verify
ward exec test-test_queue_wait_ends_at_dequeue_not_at_completionholds a job in dispatch and asserts the span has already closed withadmitted: True, and that it does not reopen or duplicate when the response arrives.test_cancelled_waiting_job_releases_queue_slotstill passes, which is what caught theasyncio.waitcancellation gap.Test plan
ward exec format-check,lint,typecheck,pre-commitall cleanRisk
Low, but it is an observability contract change. Any dashboard or query reading
gen_ai.usage.*orresponse.finish_reasonsoffqueue.waitwill stop finding them; those attributes live onrequest.chatandresilience.attempt. Anything readingqueue.waitduration as request duration will see it collapse to milliseconds - which is the fix, not a regression.One existing assertion changed: a
queue.waitspan for a cancelled request no longer carriesoutcome=cancelled, because by then it has been closed for the whole dispatch. The cancellation is still recorded on the three spans that were open when it happened.Note on #107
This does not answer the queue-versus-shed question in #107. It makes the burst test's key measurement readable: admission delay is now the span's own duration rather than a subtraction.
Note
Branched off
cancel-abandoned-work(#119). Review #118 and #119 first; this branch contains their commits.