Repository navigation
[Bugfix][Duplex] Close session when session.created never reaches the client - #7997
Conversation
|
This PR appears to belong to: docs/design/module/profiling.md, docs/design/module/entrypoints.md, docs/design/module/observability.md. Module owners: @alex-jw-brooks @linyueqian @NickCao Routing: @alex-jw-brooks via module of the changed files, module named in the PR description, semantic router, CODEOWNERS; @linyueqian via module of the changed files, module named in the PR description, semantic router, CODEOWNERS; @NickCao via module of the changed files, module named in the PR description, semantic router, CODEOWNERS @NumberWan, please review your own changes and leave a short self-review comment describing what you checked. PRs without author self-review may not be assigned a reviewer. Please take a look when you have a chance. If you would like an automated review, mention @vllm-omni-review-bot in a comment. |
… client Detach-for-resume after a pump send failure leaves an admission slot occupied until idle expiry, but nobody can resume without the session id and token. Close instead when the failed event is session.created (vllm-project#7636 issue 9). Signed-off-by: NumberWan <wantszkin2003@gmail.com>
fc6c1e8 to
21e872b
Compare
Omni ReviewBot triage noteResolved as of |
Serving still closes on undeliverable session.created. Document that the unit test only covers the close-vs-detach branch; production slots free after handle.close waits for session.closed. Signed-off-by: NumberWan <wantszkin2003@gmail.com>
b15d94e to
7361282
Compare
|
Ready for full review when |
The serving unit test only covers close vs detach when session.created never reaches the client. Fake admission capacity is not manager resource_exhausted; document production slot release in the docstring. Signed-off-by: NumberWan <wantszkin2003@gmail.com>
Self-review: Added a function "_close_undeliverable_session" in serving.py. Before this PR, when a session pump a event failed. It will treat it as a " _detach_current_attachment"(offline and maybe resume) even the event having fully created. After this PR, when the failed event is "SessionCreated", will call the _close_undeliverable_session → handle.close(reason="session_created_undelivered"). Otherwise will use the old method " _detach_current_attachment" |
Omni ReviewBot: no human activity for 7 days@NumberWan this pull request has had no human commit, comment or review since 2026-09-23. Please confirm the current plan and next step. The author or a maintainer decides whether to change the PR state. To keep it moving, any one of these is enough: push an update, reply to the open blocker, or post the current plan and timeline. |
|
This PR will be scheduled to merge after 5/10/2026 |
|
LGTM |
Omni ReviewBot routing recordAssigned Strict on zcode (GLM-5.3-Flash) under experiment |
These two CI failures are not caused by this PR. This PR only changes the duplex websocket send-failure path, and only when the event that failed to send is session.created. No diffusion, sleep-mode, model-parallel, Qwen2.5-Omni, or XPU code is touched. The new branch runs only inside the realtime websocket pump after send_json raises.
A failure caused by this PR would show up as test_session_created_send_failure_closes_instead_of_detaching (or another tests/entrypoints/duplex/ case). Neither log does. |
amy-why-3459
left a comment
There was a problem hiding this comment.
Reviewed 9ea9a6c430feebc92d389935615f3a80641857d6, which is still the current head. Closing on a failed SessionCreated send is the right direction, but another pre-handshake disconnect path still retains an unresumable session.
[P2] Also close when the connection detaches before SessionCreated is sent
At vllm_omni/entrypoints/duplex/serving.py:405-414, the new close path requires send_event() to raise. Consider this ordering:
- The engine session is opened, but the pump has not sent
session.createdyet. - The read loop receives a disconnect and
_on_disconnect()detaches the resumable attachment. - The pump then processes
SessionCreated. DuplexSessionAttachmentRegistry.send_event()findsstate.attachment is Noneand returns without sending or raising._close_undeliverable_session()is never called. The client has no session ID/resume credentials, but the session retains its admission slot until expiry.
An isolated harness executing the relevant methods extracted from this head, with stubbed surrounding objects, produced transport_sends=0, close_calls=0, detach_calls=1 for that sequence. This validates the control-flow gap, not a full engine/end-to-end reproduction.
Please track successful initial-handshake transport delivery and close a session that disconnects before that handshake completes. Distinguish journal acceptance from successful transport delivery, and handle the in-flight-send/disconnect race. Add a regression that delays the first event pump, disconnects the reader, then lets the pump process SessionCreated.
Please verify actual admission-slot release
The new test verifies that the fake handle is closed rather than detached. It does not verify that the production manager releases capacity. Please add a manager/integration regression with max_sessions=1 showing that a second session can open after the failed initial handshake is cleaned up. Cover both a raised send failure and detach-before-send, and retain the mid-session send-failure test demonstrating that a successfully established session remains resumable.
The PR description also still claims a FakeOmni.max_sessions capacity test and a successful second open; the current commit removed those assertions. Please update the description to match the actual coverage.
Validation status
The local command python -m pytest -q -rs tests/entrypoints/duplex/test_duplex_serving.py failed during import because the installed vLLM lacks vllm.v1.outputs.RoutedExpertsLists. No local pytest pass is claimed. At review time CUDA/NPU CI statuses were successful and AMD/Intel statuses were failing.
Please provide complete repository unit-test evidence tied to this head or the updated head after fixes, using the supported CI unit-test shards and applicable backend jobs, including the entrypoint/engine coverage defined in .buildkite/cuda/test-ready.yml. Attach exact commands, environment/dependency versions, passed/failed/skipped/deselected/collection-error counts where available, and CI or complete-log links. Existing complete evidence on the same SHA can be linked without a redundant rerun. Explain skipped or unavailable jobs, and provide a same-environment base/head comparison for failures believed to be pre-existing. If coverage cannot be completed, obtain an explicit maintainer disposition for the remaining gap; mixed CI statuses and the local import failure do not establish merge readiness.
|
Very clean fix. Detaching on an initial Just double-checking the cleanup boundary in async def _close_undeliverable_session(self, session_id: str) -> None:
handle = self._omni.get_session(session_id)
if handle is None or handle.closed:
return
try:
await handle.close(reason="handshake_undeliverable")
except Exception:
logger.warning("Failed to close undeliverable duplex session %s", session_id, exc_info=True)If The added test |
Thanks for the review. Head
Admission: Local: |
A reader that detaches before the pump sends session.created makes send_event return without raising, so the admission slot stayed held. Treat only a successful transport send as handshake delivery. Signed-off-by: NumberWan <wantszkin2003@gmail.com>
Signed-off-by: NumberWan <wantszkin2003@gmail.com> # Conflicts: # tests/entrypoints/duplex/test_duplex_serving.py # vllm_omni/entrypoints/duplex/serving.py
5b82645 to
baf33fd
Compare
natureofnature
left a comment
There was a problem hiding this comment.
LGTM. The updated handling covers both a failed initial session.created send and disconnect-before-send, while preserving resumability after a successful handshake. The manager regression also verifies that admission capacity is reclaimed.
Please resolve the outstanding CI checks before merging.
… client (vllm-project#7997) Signed-off-by: NumberWan <wantszkin2003@gmail.com> Co-authored-by: NATURE <wzliu@connect.hku.hk>
Summary
Follow-up correctness fix from the Unified Full-duplex Framework review backlog in #7636. This PR covers Issue 9 (P2).
This is a small Serving transport correctness bug, not AURA-specific. No scheduler or plugin changes.
Issue 9 — Session kept alive for an impossible resume when
session.createdis not deliveredWhere:
vllm_omni/entrypoints/duplex/serving.py(_send_event)Bug: Any pump send failure was treated as a disconnect of the current attachment:
_detach_current_attachmentkeeps the engine session alive for resume (journal + disconnect grace). That is correct for mid-session failures (e.g. an audio delta after handshake), where the client already hassession_id/resume_token.When
session.createditself never reaches the client, nobody can callsession.resume. Two ways that happens:send_jsonraises while sendingsession.created.send_eventthen finds no attachment and, becausesession.createdis not journaled, returns without raising. The close path never ran, so the admission slot stayed held until expiry.Fix:
_close_undeliverable_session(handle.close(reason="session_created_undelivered")) unless the handshake was actually written to the socket.on_acceptedcallback after a successful transport send. A journal record, or a detached no-op return, is not delivery. An in-flight send that then raises still closes; one that completes leaves the session resumable.finallystill drops_pumpsand the attachment registry entry.Test plan
pytest tests/entrypoints/duplex/test_duplex_serving.py -o addopts='--strict-markers --strict-config'(20 passed)test_session_created_send_failure_closes_instead_of_detachingtest_disconnect_before_session_created_closes_instead_of_staying_detachedtest_transport_send_failure_detaches_the_session_instead_of_closing_ittest_undeliverable_handshake_close_releases_admission_after_detach(max_sessions=1; detach still rejects the next open;close(reason="session_created_undelivered")admits it). Both serving triggers call this close.FakeOmnidoes not model admission.Files
vllm_omni/entrypoints/duplex/serving.pysession.createdis not transport-deliveredtests/entrypoints/duplex/test_duplex_serving.pytests/engine/duplex/test_session_manager.pymax_sessions=1slot release after that close