Skip to content

fix: avoid double KV release on disaggregated prefill grammar errors - #30937

Merged
ShangmingCai merged 6 commits into
sgl-project:mainfrom
kflansburg:kflansburg/fix-disagg-grammar-double-release
Jul 14, 2026
Merged

ShangmingCai merged 6 commits into
sgl-project:mainfrom
kflansburg:kflansburg/fix-disagg-grammar-double-release

Conversation

@kflansburg

@kflansburg kflansburg commented Jul 12, 2026 •

Copy link
Copy Markdown
Contributor

Motivation

When grammar validation fails after a disaggregated prefill KV transfer has started, the request remains in the inflight queue until the transfer reaches a terminal state. Releasing its KV cache immediately can release the same allocation again during terminal cleanup. Terminal handling can also replace the original grammar error with a transfer result.

Modifications

  • Defer KV cache release after grammar rejection until the inflight transfer reaches a terminal state.
  • Preserve an existing grammar abort reason during successful and failed transfer cleanup.
  • Add CPU unit coverage for transferring, successful, and failed poll states, plus deferred external abort behavior.

Accuracy Tests

Not applicable. This change only affects cleanup after grammar validation rejects a token.

Speed Tests and Profiling

Not applicable. The normal inference path is unchanged.

Checklist

  • Format code with pre-commit.
  • Add unit tests.
  • Documentation is not required because there are no user-facing API changes.
  • Accuracy and speed benchmarks are not applicable to this error-path cleanup.
  • Follow the SGLang code style guidance.

Review and Merge Process

  1. Get approvals from CODEOWNERS and other reviewers.
  2. Trigger the required CI tests.
  3. Merge after CI and required approvals are complete.

CI States

Latest PR Test (Base): ❌ Run #29323853939
Latest PR Test (Extra): ❌ Run #29323853715

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request modifies the disaggregation prefill queue handling to prevent overwriting an existing FINISH_ABORT status on requests. It also adds a comprehensive unit test suite to verify prefill queue cleanup under grammar failures and deferred aborts. The reviewer feedback highlights a critical issue where external aborts (req.to_finish) are silently discarded because they are not promoted to req.finished_reason on the prefill server. Suggestions are provided to promote req.to_finish to req.finished_reason in both the success and failure paths, and to update the new unit tests accordingly.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +798 to +799
if not isinstance(req.finished_reason, FINISH_ABORT):
req.finished_reason = FINISH_LENGTH(length=0)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

If a request is aborted externally, req.to_finish is set to FINISH_ABORT. Since update_finish_state is not called on the prefill server, to_finish is never promoted to finished_reason. Overwriting finished_reason with FINISH_LENGTH here will silently discard the external abort, causing the request to be treated as successful and potentially wasting downstream decode resources. We should promote req.to_finish to req.finished_reason if it is set.

if req.to_finish is not None:
                    req.finished_reason = req.to_finish
                elif not isinstance(req.finished_reason, FINISH_ABORT):
                    req.finished_reason = FINISH_LENGTH(length=0)

Comment on lines +820 to +823
if not isinstance(req.finished_reason, FINISH_ABORT):
prepare_abort(
req, error_message, status_code=HTTPStatus.INTERNAL_SERVER_ERROR
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

Similarly, if the transfer fails but the request was already aborted externally, we should promote req.to_finish to req.finished_reason to preserve the original external abort reason instead of overwriting it with a transfer failure abort.

if req.to_finish is not None:
                    req.finished_reason = req.to_finish
                elif not isinstance(req.finished_reason, FINISH_ABORT):
                    prepare_abort(
                        req, error_message, status_code=HTTPStatus.INTERNAL_SERVER_ERROR
                    )

)

self.assertEqual(done, [req])
self.assertIsInstance(req.finished_reason, FINISH_LENGTH)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

With the fix to promote req.to_finish to req.finished_reason, the request should finish with FINISH_ABORT rather than FINISH_LENGTH when a deferred abort is present.

Suggested change
self.assertIsInstance(req.finished_reason, FINISH_LENGTH)
self.assertIsInstance(req.finished_reason, FINISH_ABORT)

@Jiminator
Jiminator marked this pull request as ready for review July 13, 2026 21:40
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@ShangmingCai ShangmingCai left a comment

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.

Looks good.

@ShangmingCai

Copy link
Copy Markdown
Collaborator

/rerun-test test/registered/unit/disaggregation/test_prefill_queue_cleanup.py

@ShangmingCai

Copy link
Copy Markdown
Collaborator

/rerun-group disaggregation

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Rebase Required Before Re-run

A major update has landed on main. Your PR is diverged relative to required base commit cbcbef6.

Re-run was not dispatched. What to do:

  • Rebase your branch onto the latest main and push again
  • Follow issue #21065 for context
  • CI-fix PRs may request the bypass-maintenance label to skip this check

1 similar comment
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Rebase Required Before Re-run

A major update has landed on main. Your PR is diverged relative to required base commit cbcbef6.

Re-run was not dispatched. What to do:

  • Rebase your branch onto the latest main and push again
  • Follow issue #21065 for context
  • CI-fix PRs may request the bypass-maintenance label to skip this check

@ShangmingCai

Copy link
Copy Markdown
Collaborator

/rerun-test test/registered/unit/disaggregation/test_prefill_queue_cleanup.py

@ShangmingCai

Copy link
Copy Markdown
Collaborator

/rerun-group disaggregation

@github-actions

github-actions Bot commented Jul 14, 2026 •

Copy link
Copy Markdown
Contributor

Results for /rerun-test test/registered/unit/disaggregation/test_prefill_queue_cleanup.py:

🚀 ubuntu-latest (1 test): ❌ View workflow run

cd test/ && python3 registered/unit/disaggregation/test_prefill_queue_cleanup.py

@github-actions

github-actions Bot commented Jul 14, 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 (3 tests): ✅ View workflow run

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

🚀 8-gpu-h20 (4 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_pp.py

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

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

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

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

🚀 1-gpu-5090 (2 tests): ✅ View workflow run

cd test/ && python3 registered/disaggregation/test_disaggregation_xpu.py
cd test/ && python3 registered/disaggregation/test_specv2_kvcache_offloading.py

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

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

@ShangmingCai

Copy link
Copy Markdown
Collaborator

/rerun-test test/registered/unit/disaggregation/test_prefill_queue_cleanup.py

@github-actions

github-actions Bot commented Jul 14, 2026 •

Copy link
Copy Markdown
Contributor

Results for /rerun-test test/registered/unit/disaggregation/test_prefill_queue_cleanup.py:

🚀 ubuntu-latest (1 test): ❌ View workflow run

cd test/ && python3 registered/unit/disaggregation/test_prefill_queue_cleanup.py

@ShangmingCai

Copy link
Copy Markdown
Collaborator

/rerun-group disaggregation

@ShangmingCai

Copy link
Copy Markdown
Collaborator

/rerun-test test/registered/unit/disaggregation/test_prefill_queue_cleanup.py

@github-actions

github-actions Bot commented Jul 14, 2026 •

Copy link
Copy Markdown
Contributor

Results for /rerun-test test/registered/unit/disaggregation/test_prefill_queue_cleanup.py:

🚀 ubuntu-latest (1 test): ❌ View workflow run

cd test/ && python3 registered/unit/disaggregation/test_prefill_queue_cleanup.py

@github-actions

github-actions Bot commented Jul 14, 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 (3 tests): ✅ View workflow run

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

🚀 8-gpu-h20 (4 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_pp.py

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

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

🚀 8-gpu-h200 (1 test): ❌ View workflow run

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

🚀 1-gpu-5090 (2 tests): ✅ View workflow run

cd test/ && python3 registered/disaggregation/test_disaggregation_xpu.py
cd test/ && python3 registered/disaggregation/test_specv2_kvcache_offloading.py

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

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

@ShangmingCai
ShangmingCai merged commit bbb5702 into sgl-project:main Jul 14, 2026
97 of 113 checks passed
Chronostasys pushed a commit to MindLab-Research/sglang that referenced this pull request Aug 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants