已关闭
[Compatibility] L1 ingest silently skips map-encoded `BlockStored` observed with vLLM 0.28.0 #17
juedingfengzm创建于 9月3日关闭于 9月9日
9月3日 添加了label:sig/sig-ai-inference
openFuyao-bot
9月3日 评论:
9月3日 评论:
欢迎来到 openFuyao 社区
Hey @juedingfengzm , 感谢你对社区的贡献.
机器人使用手册
有关指令的使用,可以点击 此处 查看详情。开发人员可以在每个PR或Issue下方评论特定指令来触发机器人任务。您可以自助配置此仓库的 PR 合并规则,更多详情,请参阅此处。
联系指引
有疑问可以联系 SIG: sig-ai-inference ,
维护者是: @foxbit, @regandy ,
审核者是: @ShiroChen, @piamposer-li, @qinwenzh .


9月3日 添加了label:feature-enhancement
9月4日 关联了pull request:fix(ingest/l1): accept map-encoded KV events in L1 ingest for openFuyao/cache-indexer#17
9月4日 关联了pull request:fix(ingest/l1): accept map-encoded KV events in L1 ingest for openFuyao/cache-indexer#17
pengkaijie
9月9日 评论:
9月9日 评论:
感谢老师提出这个 Issue!
我们已确认 cache-indexer 的 L1 ingest 当前仅处理 array-encoded 事件,无法兼容 map-encoded 事件。vLLM 0.24.0 之后引入了 map/MessagePack 编码格式,导致新版本的事件无法被正确解析。
目前已在 PR #47 中修复,已实现对map-encoded结构的兼容,同时0.24.0之前的版本亦能继续支持。


9月9日 关联了看板:openFuyao - v26.09问题单
9月9日 关联了里程碑:v26.09
9月9日 issue状态由 开启 改变为 待测试
9月9日 issue状态由 待测试 改变为 已完成
9月9日 关闭了 issue
问题描述
在nvidia显卡环境使用vLLM
0.28.0部署模型测试,发现cache-index一直没有L1 cache命中。在下述实测配置中,vLLM0.28.0发出的BlockStored事件使用 MessagePack map 编码。cache-indexerefa9a4d的 L1 ingest 仅处理 array event,因此该事件会被静默跳过,无法写入 L1 index。环境
https://gitcode.com/openFuyao/cache-indexer.gitefa9a4d88ffdc832ea2f75bd3017284886089e64vllm/vllm-openai:v0.28.0-cu129vllm.__version__:0.28.0Qwen/Qwen3-1.7BMooncakeStoreConnectorkv_both128sha256_cborVLLM_KV_EVENTS_USE_INT_BLOCK_HASHES=1kv-events关键 vLLM 参数:
复现方式
需要两个终端。终端 A 先运行 subscriber,终端 B 再发送请求。
终端 A:订阅并解码事件
在对应pod的vllm容器里执行,将
<vllm-pod-ip>替换为 vLLM 实例地址,并将以下内容保存为capture_kv_event.py:import json import time import msgpack import zmq ctx = zmq.Context.instance() sock = ctx.socket(zmq.SUB) sock.setsockopt(zmq.SUBSCRIBE, b"kv-events") sock.setsockopt(zmq.RCVTIMEO, 30000) sock.connect("tcp://<vllm-pod-ip>:5557") # 等待 ZeroMQ SUB/PUB 握手完成。 time.sleep(3) print("subscriber ready", flush=True) frames = sock.recv_multipart() if len(frames) < 3: raise RuntimeError(f"expected at least 3 frames, got {len(frames)}") batch = msgpack.unpackb(frames[2], raw=False, strict_map_key=False) events = batch[1] block_stored = next(( event for event in events if isinstance(event, dict) and event.get("type") == "BlockStored" ), None) if block_stored is None: raise RuntimeError( f"no map-encoded BlockStored in batch; " f"event types={[type(event).__name__ for event in events]}" ) print(json.dumps({ "frame_count": len(frames), "topic": frames[0].decode("utf-8", "replace"), "batch_python_type": type(batch).__name__, "batch_length": len(batch), "events_python_type": type(events).__name__, "event_count": len(events), "block_stored": { "python_type": type(block_stored).__name__, "type": block_stored.get("type"), "keys": sorted(block_stored.keys()), "block_hashes_count": len(block_stored.get("block_hashes", [])), "token_ids_count": len(block_stored.get("token_ids", [])), "block_size": block_stored.get("block_size"), "parent_block_hash": block_stored.get("parent_block_hash"), "medium": block_stored.get("medium"), "group_idx": block_stored.get("group_idx"), "kv_cache_spec_kind": block_stored.get("kv_cache_spec_kind"), }, }, indent=2))运行:
等待输出
subscriber ready后,再在终端 B 发送请求。终端 B:发送具有全新长前缀的请求
唯一标识放在 prompt 开头,避免整条 block hash 链命中已有缓存:
RUN_ID="kv-event-$(date +%s)-$RANDOM" LONG=$(python3 -c \ "print('这是用于验证 KV 缓存事件格式的合成文本。' * 100)") CONTENT="验证批次:$RUN_ID。$LONG 请简要回复确认。" curl -sS http://<vllm-pod-ip>:8000/v1/chat/completions \ -H 'Content-Type: application/json' \ -d "$(jq -nc --arg content "$CONTENT" '{ model: "Qwen/Qwen3-1.7B", messages: [{role: "user", content: $content}], max_tokens: 4, temperature: 0 }')"直接观察到的 ZMQ payload
payload 由独立 Python subscriber 直接从 vLLM ZMQ publisher 捕获,不依赖 cache-indexer 解码。
以下内容删除了具体 block hashes、token IDs、timestamp 和 sequence number,只保留结构、非敏感字段值及元素数量:
{ "frame_count": 3, "topic": "kv-events", "batch_python_type": "list", "batch_length": 3, "events_python_type": "list", "event_count": 1, "block_stored": { "python_type": "dict", "type": "BlockStored", "block_hashes_count": 16, "token_ids_count": 2048, "block_size": 128, "parent_block_hash": null, "medium": "GPU", "group_idx": 0, "kv_cache_spec_kind": "full_attention", "keys": [ "block_hashes", "block_size", "extra_keys", "group_idx", "kv_cache_spec_kind", "lora_id", "lora_name", "medium", "parent_block_hash", "token_ids", "type" ] } }由此确认:
基线代码行为
抓包时通过独立 subscriber 获取 payload;现场 cache-indexer 已运行包含候选兼容修改的验证镜像。以下故障行为不是由该验证镜像的运行日志推断,而是由实抓 payload 与固定基线 commit
efa9a4d88ffdc832ea2f75bd3017284886089e64的代码路径共同确定。当前开发工作区已包含候选 map 兼容修改,因此核对基线时应查看上述固定 commit,而不是工作区文件。
问题位于
pkg/ingest/l1/manager.go的(*subscription).handlePayload:for _, evRaw := range eventsRaw { evArr, ok := evRaw.([]interface{}) if !ok || len(evArr) == 0 { continue }在 cache-indexer 当前使用的
vmihailenco/msgpack解码方式下:因此实抓
BlockStored的类型断言返回ok=false,代码直接执行continue,不会调用handleBlockStored,也不会产生 event-level warning。实际影响
KVEventBatch外层解码可以正常完成;BlockStored不会进入 L1 indexer;预期行为
cache-indexer 应接受上述 map event,同时保持对当前 array representation 的兼容。
支持两种 representation 可以保留 cache-indexer 当前已接受的 payload,同时兼容本次观察到的 vLLM payload。
建议修复方向
在 L1 wire-format ingest 层统一读取具体 event:
type和对应 map key 读取 tag 和字段;一个最小兼容修改应不需要改变本次观察到的 batch framing 或现有 L1 indexer 接口。
建议覆盖:
范围
本 Issue 仅讨论具体 KV event 的 array/map wire representation。Multi-group 及其他 KV cache 数据模型语义未在本 Issue 中验证,应独立评估。
该问题只直接影响 L1 KV event ingest;L3 由独立的 Mooncake 索引链路维护。