Skip to content

[PD] Preserve abort ACKs until in-flight KV transfers drain - #40645

Merged
ShangmingCai merged 1 commit into
mainfrom
cctry/pd-preserve-abort-ack
Sep 22, 2026
Merged

ShangmingCai merged 1 commit into
mainfrom
cctry/pd-preserve-abort-ack

Conversation

@cctry

@cctry cctry commented Sep 21, 2026 •

Copy link
Copy Markdown
Collaborator

Motivation

The prefill scheduler can clear a failed sender while its KV transfer is still writing. Clearing the pending abort-ACK destination loses the drain notification, leaving decode to release device KV on timeout. An abort arriving after room cleanup can also miss registration while writes remain outstanding.

Modifications

Preserve the ACK destination until outstanding writes drain, acknowledge immediately when already drained, and register late Mooncake aborts for rooms with outstanding transfers. Add a regression test covering cleanup before and after drain and exactly-once acknowledgment.

Validation

  • 64 PD wire and deferred-release tests passed at 83406b6f45.
  • Formatting and other pre-commit checks passed. The repository-wide test-registry check fails on main's existing test/registered/kernel/quantization/test_mxfp8_kv_reserved_slot.py path; only that hook was skipped for the commit.
  • Accuracy and speed benchmarks are not applicable to this abort cleanup change.

CI States

Latest PR Test (Base): 🚫 Run #35667160829
Latest PR Test (Extra): ❌ Run #35667160700
Latest PR Test (AMD ROCm 10): ⏳ Run #35667160749

@ShangmingCai

Copy link
Copy Markdown
Collaborator

/tag-and-rerun-ci

@github-actions github-actions Bot added the run-ci CI: run the baseline test suite on this PR label Sep 22, 2026
if (
room_active
or self._staging_outstanding.get(room_to_be_aborted, 0) > 0
):

@ShangmingCai ShangmingCai Sep 22, 2026 •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: with this new condition, the elif at L2438 is only reached when the room is inactive and outstanding <= 0, so its == 0 re-check can never usefully be false. The one state where it differs is a hypothetically negative counter, where the elif silently drops the abort (no ack, no registration → decode waits out the release timeout), while a plain else would correctly ack — a negative counter still means nothing is in flight.

-                        elif self._staging_outstanding.get(room_to_be_aborted, 0) == 0:
-                            # Concluded/unknown AND quiescent: ack now. A cleared
-                            # room is not automatically quiescent -- clear() can
-                            # drop a room whose chunk is still transferring.
+                        else:
+                            # Concluded/unknown AND quiescent (the branch above
+                            # already took every case with writes outstanding):
+                            # ack now so decode releases without the timeout.

Purely cosmetic/robustness — fine to take as a follow-up; the current code is behaviorally identical in every reachable state.

@ShangmingCai

Copy link
Copy Markdown
Collaborator

/rerun-group disaggregation

@github-actions

github-actions Bot commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor

Results for /rerun-group disaggregation:

🚀 4-gpu-gb300 (1 test): ✅ View workflow run

cd test/ && python3 registered/disaggregation/test_disaggregation_aarch64.py

🚀 2-gpu-h100 (6 tests): ❌ View workflow run

cd test/ && python3 registered/disaggregation/test_disaggregation_basic.py
cd test/ && python3 registered/disaggregation/test_disaggregation_chunked_prefill_abort.py
cd test/ && python3 registered/disaggregation/test_disaggregation_decode_offload.py
cd test/ && python3 registered/disaggregation/test_disaggregation_optimistic_prefill.py
cd test/ && python3 registered/disaggregation/test_disaggregation_rust_server.py
cd test/ && python3 registered/disaggregation/test_disaggregation_unified_memory.py

🚀 8-gpu-h20 (5 tests): ✅ View workflow run

cd test/ && python3 registered/disaggregation/test_disaggregation_decode_radix_cache.py
cd test/ && python3 registered/disaggregation/test_disaggregation_different_tp.py
cd test/ && python3 registered/disaggregation/test_disaggregation_dp_attention.py
cd test/ && python3 registered/disaggregation/test_disaggregation_nixl.py
cd test/ && python3 registered/disaggregation/test_disaggregation_pp.py

🚀 8-gpu-h200 (4 tests): ✅ View workflow run

cd test/ && python3 registered/disaggregation/test_disaggregation_decode_radix_cache_swa.py
cd test/ && python3 registered/disaggregation/test_disaggregation_dsv4.py
cd test/ && python3 registered/disaggregation/test_disaggregation_hisparse.py
cd test/ && python3 registered/disaggregation/test_disaggregation_hybrid_attention.py

🚀 4-gpu-b200 (2 tests): ✅ View workflow run

cd test/ && python3 registered/disaggregation/test_disaggregation_dwdp_gpt_oss.py
cd test/ && python3 registered/disaggregation/test_disaggregation_inkling_mxfp8.py

🚀 4-gpu-h100 (2 tests): ✅ View workflow run

cd test/ && python3 registered/disaggregation/test_disaggregation_kimi_linear.py
cd test/ && python3 registered/disaggregation/test_epd_disaggregation.py

🚀 8-gpu-b200 (1 test): ✅ View workflow run

cd test/ && python3 registered/disaggregation/test_kimi_linear_pd_dcp4.py

⛔ registered/disaggregation/test_disaggregation_xpu.py: test/registered/disaggregation/test_disaggregation_xpu.py is registered for XPU (suite stage-b-test-1-gpu-xpu), not for CUDA or CPU; rerun-test.yml has no XPU job. Rerun it with /rerun-failed-ci, or dispatch the XPU workflow manually.

@ShangmingCai
ShangmingCai merged commit 8ef6d31 into main Sep 22, 2026
239 of 295 checks passed
@ShangmingCai
ShangmingCai deleted the cctry/pd-preserve-abort-ack branch September 22, 2026 10:46
livingshade added a commit to livingshade/sglang that referenced this pull request Sep 23, 2026
Pick up sgl-project#40645, sgl-project#40711, the shared prefill->decode status plumbing
(sgl-project#36612) and runtime role switching (sgl-project#28403).

Conflict resolution:
- common: keep upstream's update_status structure and still reset the
  deferred-ACK state when a room lifecycle starts. CommonKVSender.clear()
  follows sgl-project#40645: ACK now when nothing is in flight, else keep the target.
- mooncake: the PR's unconditional register-then-try-ACK already covers
  sgl-project#40645 and sgl-project#40711.
- nixl: keep upstream's settle-then-conclude exception path and still
  poison the ACK target, since that chunk stays counted.
- mori: re-apply the drain-aware ACK path on upstream's conclude_transfer
  / conclude_failure API and list-returning _submit_kv_transfer; register
  the drain threads with teardown() and stop them with a sentinel.
- tests: update stubs for the new NIXL exception path and for the
  deferred-ACK fields CommonKVManager now always owns.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

run-ci CI: run the baseline test suite on this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants