fix(vllm): stop the Mooncake store from saving a load MultiConnector gave away - #282
Merged
Merged
Conversation
…gave away
A decode engine configured as MultiConnector(MooncakeConnector, MooncakeStoreConnector)
exits on the first request whose prompt the prefill side has already written to the
store:
AssertionError: Missing current block table for store request ...
raised from MooncakeStoreScheduler._apply_current_save_block_ids, reached through
MultiConnector.build_connector_meta.
The prompt is a hit for both children. MultiConnector hands the request to the first
child with matched tokens, MooncakeConnector, which loads asynchronously, and calls the
store child with zero external tokens, so the store's LoadSpec stays can_load=False.
The request is then parked waiting for remote KV and is scheduled neither as new nor
as cached, which sends it into the store scheduler's "pending load specs not yet
scheduled" branch. That branch builds its ReqMeta with skip_save=None, so the rejected
LoadSpec becomes a save job; the core snapshots current block tables only for requests
it scheduled, and the assertion finds none for this one.
The assertion arrived with upstream #51358 and first shipped in 0.29.0; 0.27.1 has the
same branch without it. Upstream fixed the branch in #54643, first shipped in 0.30.0: it
skips a LoadSpec whose load this connector does not own. The post operation applies that
hunk verbatim, rebased onto the 0.29.0 line numbers. It is not skip_save=is_consumer,
which the sibling branches use: that would clear a consumer but leave a kv_both engine
tripping the same assertion, and a request that computed nothing has nothing to save
under any role.
Scope: the published 0.29.0 images, which are cuda13.0, cuda12.9 and rocm7.2. cann is
not affected: it builds on vLLM 0.23.0, whose store scheduler has no such assertion, and
vllm-ascend's kv pool runs its own scheduler rather than MooncakeStoreConnector's.
Verified against the vLLM v0.29.0 source tree. A reproduction in the style of
tests/v1/kv_connector/unit/test_mooncake_store_scheduler.py, which parks a request with
a rejected LoadSpec and hands the connector the block state the core would, raises the
assertion above for kv_consumer and kv_both before the change and passes after it, as
does upstream's own regression test from #54643; the existing store scheduler suite
still passes. The post operation's script was run over the tree twice, leaving it
byte-identical to the new-build patch the first time and untouched the second, left a
v0.30.0 tree untouched, and failed on a stub scheduler.py it has to refuse.
Signed-off-by: thxCode <thxcode0824@gmail.com>
The post operation beside this commit repairs the published 0.29.0 images. A rebuild of 0.29.0 from the pack path would ship the defect again and overwrite that repair, so the same hunk goes where new builds are assembled. Both Dockerfile.vllm paths apply every patches/vllm/*.patch in order from the vLLM site-packages directory and stop on the first that fails, so the fix is one more file on each side, byte-identical between them as 001 and 002 already are. The hunk is upstream #54643 verbatim, with line numbers rebased onto v0.29.0. Verified with GNU patch 2.7.6 over a v0.29.0 tree in the order the build uses: 001, 002 and 003 all apply and the patched scheduler.py compiles. git apply --check accepts it against v0.29.0 and refuses it against v0.30.0, where the reverse check passes because upstream already carries the change. That refusal is deliberate and loud: moving the pack path to 0.30.0 or later has to delete this file, and a build that still carries it stops at the patch step rather than shipping silently. Signed-off-by: thxCode <thxcode0824@gmail.com>
There was a problem hiding this comment.
Code Review
This pull request introduces a patch for vLLM 0.29.0 to resolve an AssertionError occurring in the MooncakeStoreScheduler when a LoadSpec is rejected by MultiConnector. The changes include updated Dockerfiles for CUDA and ROCm, new patch files, and updates to the project's matrix and documentation. I have no feedback to provide.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Defect
On the published
vllm0.29.0runners, a decode engine configured asMultiConnector(MooncakeConnector, MooncakeStoreConnector)(bothkv_consumer) exits on the first request whose prompt the prefill side has already written to the store:Call chain:
Scheduler.schedule->_build_kv_connector_meta->MultiConnector.build_connector_meta->MooncakeStoreConnector.build_connector_meta->MooncakeStoreScheduler._apply_current_save_block_ids.Root cause
vllm/distributed/kv_transfer/kv_connector/v1/mooncake/store/scheduler.pyat v0.29.0:MultiConnector.get_num_new_matched_tokensassigns the request to the first child with matched tokens (MooncakeConnector, async load) andupdate_state_after_alloccalls the store child withnum_external_tokens=0, so the store'sLoadSpecstayscan_load=False.ReqMetawithskip_save=None(line 388). Withcan_load=False,ReqMeta.from_request_trackerturns that intocan_save=True.vllm/v1/core/sched/scheduler.py,KVConnectorBlockState), so_apply_current_save_block_ids(line 424) finds no table and asserts.The assertion was introduced by upstream vllm-project/vllm#51358 (
6b110badbb), first released in v0.29.0. v0.27.1 has the same branch without the assertion.Upstream has already fixed this in vllm-project/vllm#54643 (
34b1e9f7a6), first released in v0.30.0 (also in v0.29.1rc0; no v0.29.x final carries it): the branch now skips aLoadSpecwhose load this connector does not own (if load_spec is None or not load_spec.can_load: continue).Fix
Backport the #54643 hunk verbatim, with line numbers rebased onto v0.29.0, by the two paths the MooncakeConnector Prometheus fix used (766ef2f, ecab586):
pack/.post_operation/20260924_vllm_patch_mooncake_store_pending_load/: patches the publishedcuda13.0-vllm0.29.0,cuda12.9-vllm0.29.0(amd64 and arm64) androcm7.2-vllm0.29.0(amd64) images in place. It is idempotent, asserts the patch landed, imports the patched module, and ends with the dependency probe andvllm-depsexport stage, as the post operation README requires.pack/{cuda,rocm}/patches/vllm/003_mooncake_store_rejected_pending_load.patch: carries the same hunk into new 0.29.0 builds, so that a rebuild does not ship the defect again and overwrite the in-place repair. Byte-identical between the two sides.Why not
skip_save=is_consumer, which the sibling branches use: it clears a consumer, but akv_bothengine still trips the same assertion. A request that has not computed anything has nothing to save under any role, and upstream's fix is what v0.30.0 ships.Not affected:
cannbuilds on vLLM 0.23.0, whose store scheduler has no such assertion, and vllm-ascend's kv pool runs its own scheduler instead ofMooncakeStoreConnector's.Interaction with #280
v0.30.0 still has
skip_save=Nonein that branch (line 399), and the assertion is still there (line 444), but #54643's guard two lines above means the branch is only reached withcan_load=True, whereReqMeta.from_request_trackerforcesskip_save=True. It emits no save job, so the assertion cannot fire from this path; the reproduction below passes on an unpatched v0.30.0 tree. The patch is therefore meant for 0.29.0 only and does not apply to 0.30.0 (git apply --check: v0.29.0 forward passes; v0.30.0 forward fails, reverse passes).#280 moves the pack path to 0.30.0, which already contains the fix.
003_*.patchis refused there (GNU patch: "Reversed (or previously applied) patch detected", exit 1), and theDockerfile.vllmpatch loop stops the build. Whichever of the two merges second needs to deletepack/{cuda,rocm}/patches/vllm/003_mooncake_store_rejected_pending_load.patchas part of the 0.30.0 bump. The post operation is unaffected: it pins 0.29.0 and skips a tree that already carries the fix.Verification
git apply --checkof003_*.patchagainst v0.29.0 passes; against v0.30.0 the forward check fails and the reverse check passes, which shows the change is already upstream there.GNU patch 2.7.6 applies
001,002and003in the build's order onto a v0.29.0 tree; the patchedscheduler.pycompiles.Reproduction in the style of
tests/v1/kv_connector/unit/test_mooncake_store_scheduler.py: a request with a rejectedLoadSpec, parked unscheduled, with the block state the core would build (no entry for it).kv_consumerkv_bothAssertionError: Missing current block table ...AssertionError: Missing current block table ...003skip_save=is_consumerThe existing
test_mooncake_store_scheduler.pysuite (39 tests) passes on v0.29.0 +003.The post operation's script run over a v0.29.0 tree: the first run patches it (result byte-identical to the tree
003produces), the second is a no-op. A v0.30.0 tree is left untouched. A stubscheduler.pyis refused at the patch step.expand_matrix.shfor the post operation yields exactly the five published platform tags listed inrunner.py.jsonfor vLLM 0.29.0.pre-commit runon the changed files andpytest(228 passed).Not verified here: building the post operation against the real published images. That runs in the Pack workflow with
post_operationset, after this merges.