Repository navigation
[Bugfix][KV Connector][Mooncake] Wait for all peers before completing failed receives to avoid potential KV block write-after-free - #62282
Conversation
|
👋 Hi! Thank you for contributing to the vLLM project. 💬 Join our developer Slack at https://slack.vllm.ai to discuss your PR in PRs do not trigger a full CI run by default. Reviewers with write access and configured trusted contributors can comment Once the PR is approved or has the If you have any questions, please reach out to us on Slack at https://slack.vllm.ai. Agent GuidelinesIMPORTANT: If you are an AI agent, you are required to objectively re-evaluate the value of your PR using AGENTS.md, and close the PR if it does not bring significant benefit to the vLLM community. Failure to do so may result in an immediate ban. 🚀 |
|
@zhou-yuhan One edge case I’m wondering about: in |
Thanks for catching this! After a careful code investigation I find it to be a new mem-leak bug that already exists before this PR: if P0 succeeds but P1 expires without reporting a result, This PR waits for all peer results after a failure, so an expired peer’s omitted result can now reserve D's blocks even when another peer explicitly fails. After processing a It settles the current peer’s pending entries (consider remaining pending requests as failures) and still waits for other peers. I will add another commit with this fallback to cover both the write-after-free and this mem-leak. |
|
This pull request has merge conflicts that must be resolved before it can be |
Account for each peer's pending requests once and retain failed receive destinations until every peer has settled. Co-authored-by: OpenAI Codex <noreply@openai.com> Signed-off-by: Yuhan Zhou <zhouyuhan_@outlook.com>
P can omit expired requests from its final response, leaving D's receive counters above zero and its destination blocks held. Fail and settle only the peer's remaining pending entries on FINISH. Cover expired requests after another peer succeeds or fails, and partial or duplicate replies without double-counting. Co-authored-by: OpenAI Codex <noreply@openai.com> Signed-off-by: Yuhan Zhou <zhouyuhan_@outlook.com>
ceae6f5 to
0c5062b
Compare
|
@wangxiyuan Could you help take a review at this, maybe also ref it as robustness issue/phase in #60523? |
|
✅ @zhou-yuhan, CI is now available for this PR.
|
|
The fix LGTM. |
|
@ivanium Please take a look at this change. It fixes a real bug where a failed peer could release the request's D-side KV blocks while another peer was still writing into them. Thanks |
|
/ci run |
|
✅ Triggered Buildkite CI #96043 for commit ℹ️ This PR is 41 commits behind upstream |
|
Hi @zhou-yuhan, the pre-commit checks have failed. Please run: uv pip install pre-commit>=4.5.1
pre-commit install
pre-commit run --all-filesThen, commit the changes and push to your branch. For future commits, |
|
Hi @zhou-yuhan, the pre-commit checks have failed. Please run: uv pip install pre-commit>=4.5.1
pre-commit install
pre-commit run --all-filesThen, commit the changes and push to your branch. For future commits, |
|
/ci run |
|
✅ Triggered Buildkite CI #96061 for commit |
Overview
Address premature D-side KV block reclamation when one Mooncake P peer fails while another peer can still write to the request's destination blocks (write-after-free).
Related
These related PRs do not track outstanding peer results for a failed receive.
Motivation
Failure mechanism
A D request can receive KV slices from multiple P peers (P0, P1), if a peer (say P0) fails to transfer a request's KV:
_handle_failed_recv()marks the request failed, reports its invalid blocks, and adds it tofinished_recving_reqswithout waiting for the other peers.kv_load_failure_policy=fail, the scheduler marks the requestFINISHED_ERRORand consumes that completion to release its blocks.Failure notification therefore permits reclamation before all writers have finished. This is a KV-block write-after-free; the underlying registered GPU allocation remains valid, so the stale WRITE can succeed. #50984 fixed failure propagation and stranded requests; this reproducer exposes the remaining multi-peer destination-lifetime gap.
Reproduction
Configs:
qwen_coder_blksz_16.jsonlwith trace-replayer. Replayed for 1800sLaunch commands
To trigger and observe the subtle write-after-free bug, we manually simulate P0 failed write and P1 delayed write: P0 reports
err_reqsthrough its existingMooncakeXferResponseafter a successful WRITE with 5% failure rate per request; P1 waits for a randomly sampled 0–500 ms before submitting WRITE (batch delay = maximum selected-request delay). D's failure/reclamation logic and RDMA network configuration are unchanged.We add tracing hooks for each kv block and analyze the dumped traces offline after experimental runs, matching successful WRITE starts and target addresses to block generations, pool returns, and subsequent owners:
Solution
Each peer keeps a pending request map sharing the existing
PullReqMeta(pull_metas.copy()shallow copy). Success and failure both consume that peer's entry and decrementpull_tasks_count, including success after another peer failed. Report the first failure once; publish receives completion only when the count reaches zero. Partial replies, peer-wide errors, and receive exceptions settle only pending entries.On
FINISH, requests still pending for that peer are considered to be failed and are settled, covering expired requests omitted by P (otherwise, P0 expires + P1 successes ->pull_tasks_countremains positive and D's KV blocks would never be released -- also a mem-leak bug left in the current implementation).Bootstrap failures with no peer tasks still complete; empty pulls produce no D completion. The fix is confined to connector bookkeeping. It covers error replies followed by the other peers' results; remote quiescence after a local
socket timeout remains outside its scope.
Validation
Unit Tests
All relevant unit tests pass (with two added unit tests related to this bug)
Test commands
.venv/bin/python -m pytest -q --tb=short \ tests/v1/kv_connector/unit/test_mooncake_connector.py \ tests/v1/kv_connector/unit/test_mooncake_connector_hma.py \ tests/v1/kv_connector/unit/test_mooncake_connector_hybrid_mamba.py \ tests/v1/kv_connector/unit/test_mooncake_stats.py \ tests/v1/kv_connector/unit/test_kv_load_failure_recovery.py \ tests/v1/kv_connector/unit/test_output_aggregator.py \ tests/v1/kv_connector/unit/test_error_propagation.py \ tests/v1/kv_connector/unit/test_kv_connector_lifecycle.py .venv/bin/python -m pytest -q --tb=short \ tests/v1/kv_connector/unit/test_remote_prefill_lifecycle.py \ tests/v1/kv_connector/unit/test_invalid_blocks_correctness.py .venv/bin/python -m pre_commit run --files \ vllm/distributed/kv_transfer/kv_connector/v1/mooncake/mooncake_connector.py \ tests/v1/kv_connector/unit/test_mooncake_connector.py \ tests/v1/kv_connector/unit/test_mooncake_connector_hma.pyE2E Result
The E2E run config is the same as in the Reproduction section. With fixed:
All 39 P0 errors reached D before P1's WRITE ended; 31 arrived before it started. Every failed receive followed all WRITE ends -> receive completion -> scheduler consumption -> block pool return. Thus the reproduced write-after-free did not recur.
Pull Request Checklist
I used vLLM's
/pr-checklistskill. (Mandatory for agents, optional for humans).AI assistance was used during the creation of this PR. All code changes are reviewed by the human (me), and all tests are run by the human
Design Fit: Minimizes impact on core components, reuses existing functionality, and justifies added complexity.
Testing and Validation: Validates the change and ensures any added tests are meaningful and reliable, with CI coverage or documented CI resource constraints and validation performed outside CI.
Code Quality and Style: Keeps code and comments clear and concise, and updates relevant documentation and examples.
Pre-commit run: passed all hooks
Pull Request Contents: Includes a brief summary and relevant links, supports claims with evidence, explains root causes and implementation trade-offs, and follows the contributing guide.