[Bugfix] Cancel inference on client disconnect + fix non-stream request leak#1441
Open
yhl-amd wants to merge 2 commits into
Open
[Bugfix] Cancel inference on client disconnect + fix non-stream request leak#1441yhl-amd wants to merge 2 commits into
yhl-amd wants to merge 2 commits into
Conversation
Contributor
Author
This was referenced Jul 2, 2026
…st leak The server never reacted to a client hanging up mid-request, and the non-streaming path never released finished requests. Under retrying clients (opencode / Claude Code) this piled up "pending requests" without bound and wasted GPU on output nobody reads. Two independent bugs, both verified with controlled curl tests: 1. No disconnect cancellation. Neither the streaming generators nor the non-stream handlers aborted the engine sequence when the client went away, so an abandoned request ran to max_tokens. Add an engine abort path (Sequence.aborted -> Scheduler reuses the normal finish/deallocate stop path; EngineUtilityHandler.abort_request; CoreManager.abort_request) and wire it into the API layer. Streaming generators (stream_chat_response / _fanout) now run their body under try/finally so cleanup_streaming_request (which aborts the seq) fires on GeneratorExit. For non-stream requests Starlette does NOT cancel the handler on disconnect, so add `_run_nonstream_with_disconnect()` which races the generation against `raw_request.is_disconnected()` and cancels on disconnect. `chat_completions` and `completions` gain a `raw_request` parameter for this. 2. Non-stream request leak. generate_async / _multimodal / _fanout only removed the request from io_processor.requests on a setup exception, never on normal completion, so every completed non-stream request leaked a Sequence (pending requests grew forever). Wrap their token loops in try/finally that always pops the request (and aborts on early exit). Streaming already pops via cleanup_streaming_request. Also add `"choices": []` to the streaming usage chunk so AI-SDK clients (opencode) stop rejecting the final usage frame. Verified (DeepSeek-V4, TP8): 6 sequential non-stream completions keep pending flat (was 1->8); 3 mid-generation disconnects -> abort found, GPU drops immediately, pending flat (was +3 leak, GPU pinned to max_tokens); normal streaming + non-stream answers unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: yihonglie <hyi@amd.com>
yhl-amd
force-pushed
the
fix/client-disconnect-abort
branch
from
July 12, 2026 15:47
5d00023 to
40c6c03
Compare
Address review: fold the parallel `Sequence.aborted` boolean into the existing `SequenceStatus` enum so a sequence's terminal intent has a single source of truth. - sequence: add SequenceStatus.ABORTED (kept distinct from FINISHED so an aborted running seq still rides one cleanup pass; is_finished() stays False until then); drop Sequence.aborted. - engine_utility: _handle_abort_request sets seq.status = ABORTED. - scheduler (running): finish check reads seq.status == ABORTED. - scheduler (waiting): intercept ABORTED seqs when popped from `waiting`, BEFORE the waiting->running promotion overwrites status with RUNNING and loses the abort intent. Such seqs hold no KV and need no forward pass, so finish them outright via `_rejected` (mirrors the unschedulable exit). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: yihonglie <hyi@amd.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two independent bugs in the OpenAI server, both surfaced by retrying clients (opencode / Claude Code) which pile up unbounded
pending requestsand waste GPU on output nobody reads.1. No disconnect cancellation. Neither the streaming generators nor the non-stream handlers aborted the engine sequence when the client hung up, so an abandoned request ran to
max_tokens.Sequence.aborted→ Scheduler reuses the normal finish/deallocate stop path;EngineUtilityHandler.abort_request;CoreManager.abort_request(broadcast to all DP ranks).stream_chat_response/_fanout) now run under try/finally socleanup_streaming_request(which aborts the seq) fires onGeneratorExit._run_nonstream_with_disconnect()which races generation againstraw_request.is_disconnected()and cancels on disconnect.chat_completions/completionsgain araw_requestparam.2. Non-stream request leak.
generate_async/_multimodal/_fanoutonly removed the request fromio_processor.requestson a setup exception, never on normal completion → every completed non-stream request leaked aSequence(pending grew forever). Wrap the token loops in try/finally that always pops (and aborts on early exit). Streaming already pops viacleanup_streaming_request.Also add
"choices": []to the streaming usage chunk so AI-SDK clients (opencode) stop rejecting the final usage frame.Why
Under an agentic client that retries / cancels mid-generation, the pre-fix server leaks a Sequence per completed non-stream request and keeps abandoned requests running to
max_tokens.pending requestsgrows without bound and GPU stays pinned on discarded work.Test plan
Verified on DeepSeek-V4, TP8:
pending requestsstays flat (was 1→8).abort_request ... found=True, GPU utilization drops immediately, pending flat (was +3 leak with GPU pinned tomax_tokens).Reproduce:
Split out of #1428 (which now carries only the DeepSeek-V4 DSML tool-call changes) so this fix can be reviewed on its own.
🤖 Generated with Claude Code