[Bugfix] Keep mamba align prefill chunks block-aligned past last_cache_position - #51113
Merged
njhill merged 2 commits intoAug 6, 2026
Merged
Conversation
ivanium
force-pushed
the
fix/mamba-align-prefill-chunk-poisoning
branch
from
August 5, 2026 04:12
8339606 to
e20577f
Compare
ZJY0516
marked this pull request as ready for review
August 5, 2026 09:01
ZJY0516
requested review from
ApostaC,
WoosukKwon,
alexm-redhat,
heheda12345,
njhill,
orozery,
robertgshaw2-redhat and
ywang96
as code owners
August 5, 2026 09:01
njhill
approved these changes
Aug 5, 2026
…e_position In mamba_cache_mode="align", slot p holds the recurrent state after exactly (p + 1) * block_size tokens, and state is written at chunk ends, so chunk ends must be block-aligned. The split only enforced that below last_cache_position, which EAGLE/MTP backs off by one block (and zeroes for prompts under two blocks). Past it any chunk end was accepted, so a budget-fragmented chunk left a slot short and a later chunk crossing that slot's boundary published it under an offset it never reached. Requests resuming from the poisoned hash silently restore a truncated state (vllm-project#43559). Gate the end-alignment on the prefill end instead, and drop the last_cache_position condition on the mid-block realign stop so an unaligned resume (partial hash hit, or unaligned external tokens from a KV connector) cannot run past its block either. Only the prefill's final chunk may end unaligned; decode advances that slot to its boundary. Co-authored-by: Hernan <kodek@eat1337.com> Co-authored-by: yanghui1-arch <3053034939@qq.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: Yifan Qiao <yifanqiao@inferact.ai>
ivanium
force-pushed
the
fix/mamba-align-prefill-chunk-poisoning
branch
from
August 5, 2026 21:06
e20577f to
af2d605
Compare
4 tasks
Collaborator
Author
|
/ci run |
|
✅ Triggered Buildkite CI #82576 for commit |
plasticchris
added a commit
to plasticchris/vllm
that referenced
this pull request
Aug 10, 2026
Port the invariants from upstream vLLM vllm-project#51113 so fragmented or resumed prefills cannot publish recurrent state under the wrong prefix hash.
4 tasks
xwu-intel
pushed a commit
to xwu-intel/vllm
that referenced
this pull request
Aug 13, 2026
…e_position (vllm-project#51113) Signed-off-by: Yifan Qiao <yifanqiao@inferact.ai> Co-authored-by: Hernan <kodek@eat1337.com> Co-authored-by: yanghui1-arch <3053034939@qq.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: Wu, Xiaochang <xiaochang.wu@intel.com>
manojkilaru97
pushed a commit
to manojkilaru97/vllm
that referenced
this pull request
Aug 13, 2026
…e_position (vllm-project#51113) Signed-off-by: Yifan Qiao <yifanqiao@inferact.ai> Co-authored-by: Hernan <kodek@eat1337.com> Co-authored-by: yanghui1-arch <3053034939@qq.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
skavulya
pushed a commit
to skavulya/vllm
that referenced
this pull request
Aug 15, 2026
…e_position (vllm-project#51113) Signed-off-by: Yifan Qiao <yifanqiao@inferact.ai> Co-authored-by: Hernan <kodek@eat1337.com> Co-authored-by: yanghui1-arch <3053034939@qq.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This was referenced Aug 16, 2026
Open
1 task
1 task
zyp2014
pushed a commit
to zyp2014/vllm
that referenced
this pull request
Aug 21, 2026
…e_position (vllm-project#51113) Signed-off-by: Yifan Qiao <yifanqiao@inferact.ai> Co-authored-by: Hernan <kodek@eat1337.com> Co-authored-by: yanghui1-arch <3053034939@qq.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
1 task
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.
Purpose
Fix prefix-cache poisoning on hybrid Mamba/GDN models in
mamba_cache_mode="align"when EAGLE/MTP speculative decoding is on — the correctness half of #43559.MambaManager.cache_blockshashes block-table slotpas the recurrent state at exactly(p + 1) * block_sizetokens._mamba_block_aligned_splitonly enforced that invariant belowlast_cache_position, which EAGLE backs off by one block (and zeroes for prompts shorter than two mamba blocks). Past that point any chunk end was accepted, so:state@364.1600, socache_blockshashes that same slot asstate@1600.A single request is accidentally safe — its low slots are null blocks, which
cache_full_blocksskips — which is why this only appears under concurrency and was hard to pin down.The same hole exists for an unaligned start: the mid-block realign stop was gated on
next_block_boundary <= last_cache_position, so a prefill resuming off-grid (finerprefix_match_unit, or unaligned externally computed tokens from a KV connector) could run straight past its block's boundary and poison that slot too.Fix
prefill_endrather thanlast_cache_position. Only the prefill's final chunk may end unaligned; decode completes that slot via the fused postprocess path.last_cache_positioncondition on the mid-block realign stop.last_cache_positionis kept as a mandatory stop — it still serves hit rate by materializing a snapshot where a same-length request's eagle-dropped hit actually lands. A chunk the budget cannot fund returns 0, which both call sites already treat as "defer" (scheduler.pyeven lists it as skip reason 4).Credit and prior work
This is not an original diagnosis. Two earlier PRs reached the same root cause independently, and both are credited as co-authors on the commit:
last_cache_position. It also carries the end-to-end validation this PR cannot reproduce locally (10 identical 2002-token requests on Qwen3.6-27B-FP8, 5/10 corrupted -> clean, decode throughput unchanged). It has beenneeds-rebasesince 2026-07-14 and its base is 859 commits behindmain:_mamba_block_aligned_splitwas since restructured by [Core] Preserve Marconi caching with selective hybrid cache retention #47782 (which removed thenum_uncached_common_prefix_tokensparameter that PR edits) and [New model] Kimi K3 #50000, so the if/elif chain it rewrites no longer exists. This PR is that fix re-derived against currentmain.supports_eagle_cache_peek, false forMambaSpec). Closed unmerged on 2026-07-19 after going stale on conflicts.I am glad to close this in favour of a rebased #45477 if @kodek prefers to carry it; the intent here is to unstick a fix that has died on rebase twice, not to displace it.
One difference worth review: #45477 nests its mid-block realign inside
num_computed_tokens < last_cache_position, so a mid-block start at or pastlast_cache_positioncan still run through the boundary. This PR drops that condition, andtest_unaligned_resume_never_runs_past_its_blockcovers it. That reading is from the diff — I have not run #45477's branch, so @kodek should confirm.Other related PRs
replay_boundarystop) touch the same function but are hit-rate changes; both compose with this.This PR does not close #43559 on its own: the thread also covers CUDA illegal-access crashes (#40756, #50021), the MTP + reasoning off-by-one (#44927), and degenerate loops on deep agentic conversations (#47087).
Test Plan
.venv/bin/python -m pytest tests/v1/core/test_mamba_align_chunk_split.py -q .venv/bin/python -m pytest tests/v1/core/prefix_cache -q .venv/bin/python -m pytest tests/v1/core/test_scheduler.py tests/v1/core/test_prefix_caching.py \ tests/v1/core/test_single_type_kv_cache_manager.py tests/v1/core/prefix_cache -qNew CPU-only regression tests in
tests/v1/core/test_mamba_align_chunk_split.pydrive the realKVCacheManager(full-attn 16 + mamba-align 1600,num_speculative_blocks=3,use_eagle=True) and the real split helper, with an oracle mirroring the GDN/postprocess state writes, and assert that every hash-cached mamba slot holds its boundary state.test_unaligned_resume_never_runs_past_its_blockcoverspartial_hiton/off x five resume offsets.Test Result
maintest_mamba_align_chunk_split.pytests/v1/core/prefix_cacheThe 35 failures are pre-existing on this checkout (encoder-cache / EC-connector tests) and identical with and without the patch.
The poisoning shows up on
mainas:The unaligned-resume failures on
mainland exactly where the old gate flips: withlast_cache_position = 1600, offsets331and1599pass and1601,2531,3011fail, for bothpartial_hitvalues.Scheduling cost
Enumerated old vs new chunk counts over 85 configs (block size × prompt length × per-step budget share, EAGLE on,
max_num_scheduled_tokens=8192):The affected window is
[last_cache_position, prefill_end], whose size is(P mod block_size) + block_size— under two blocks. Whenever the step's budget covers it,end == prefill_endand the new gate is a no-op, so single-request and light-load prefill are bit-identical to before. Under contention a chunk shrinks to the boundary or the request defers a step; the token budget is shared, so a deferred request does not idle the step.Real-workload audit
The invariant can be checked at runtime, so rather than inferring the bug from an accuracy delta I measured it directly. A runtime audit hook records, per request, the token offset each mamba slot actually holds (mirroring the kernel: a prefill chunk leaves the running slot at the chunk end) and flags any slot published under a different offset. Real 20-shot gsm8k prompts, 200 questions,
Qwen/Qwen3.5-0.8B(hybrid GDN, mamba block 560), MTPnum_speculative_tokens=3,--enable-prefix-caching,max_num_seqs=32,max_num_batched_tokens=8192:mainThat is the bug happening on ordinary gsm8k traffic: a slot advertised as the state after 3920 tokens while holding the state after 3662, reachable by any later request whose hit lands on it.
Model evaluation
gsm8k 20-shot, 500 questions,
Qwen/Qwen3.6-27B, TP=1,--enforce-eager, greedy, on unpatchedmain:At n=500 the 1σ band is ±1.7 points, so this matrix neither confirms nor refutes an end-to-end effect and I am not claiming an accuracy delta from it. The audit above explains why: on a homogeneous same-length gsm8k workload the poisoning fires rarely and the eagle drop usually keeps the reconciled hit below the poisoned slot, so little of it is consumed. The failure mode reporters describe needs mixed-length requests over a shared prefix, where a longer request's hit reaches the poisoned slot. #45477 reports that end-to-end amplification on Qwen3.6-27B-FP8.
Note on AI assistance
AI assistance (Claude Code) was used for the investigation, the patch, and the tests. Prior art in #45477 and #47861 is credited above and on the commit. The root cause was derived from the code and confirmed by the unit-level reproduction above before the related PRs were read. All results in this description were produced by running the listed commands on this branch.