已关闭
[Compatibility] L1 ingest silently skips map-encoded `BlockStored` observed with vLLM 0.28.0 #17
juedingfengzm创建于  9月3日关闭于  9月9日
juedingfengzm
9月3日 创建

问题描述

在nvidia显卡环境使用vLLM 0.28.0 部署模型测试,发现cache-index一直没有L1 cache命中。在下述实测配置中,vLLM 0.28.0 发出的 BlockStored 事件使用 MessagePack map 编码。cache-indexer efa9a4d 的 L1 ingest 仅处理 array event,因此该事件会被静默跳过,无法写入 L1 index。

本报告只描述以下版本和配置中观察到的,不表示所有 vLLM 0.28.0 配置都使用相同编码。

环境

  • cache-indexer 仓库:https://gitcode.com/openFuyao/cache-indexer.git
  • 基线源码:commit efa9a4d88ffdc832ea2f75bd3017284886089e64
  • vLLM 镜像:vllm/vllm-openai:v0.28.0-cu129
  • vllm.__version__:0.28.0
  • 模型:Qwen/Qwen3-1.7B
  • KV connector:MooncakeStoreConnector
  • KV role:kv_both
  • Prefix caching:enabled
  • Block size:128
  • Hash algorithm:sha256_cbor
  • VLLM_KV_EVENTS_USE_INT_BLOCK_HASHES=1
  • KV event publisher:ZMQ,topic 为 kv-events

关键 vLLM 参数:

--block-size 128
--enable-prefix-caching
--prefix-caching-hash-algo sha256_cbor
--enable-prompt-tokens-details
--kv-events-config '{
  "enable_kv_cache_events": true,
  "publisher": "zmq",
  "topic": "kv-events"
}'
--kv-transfer-config '{
  "kv_connector": "MooncakeStoreConnector",
  "kv_role": "kv_both",
  "kv_connector_extra_config": {
    "mooncake_protocol": "tcp",
    "enable_client_http_server": true
  }
}'

复现方式

需要两个终端。终端 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))

运行:

python3 capture_kv_event.py

等待输出 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"
    ]
  }
}

由此确认:

KVEventBatch 外层       → MessagePack array → Python list
batch.events           → MessagePack array → Python list
具体 BlockStored event → MessagePack map   → Python dict

基线代码行为

抓包时通过独立 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 解码方式下:

当前已支持的 array representation → []interface{}
实抓的 map representation         → map[string]interface{}

因此实抓 BlockStored 的类型断言返回 ok=false,代码直接执行 continue,不会调用 handleBlockStored,也不会产生 event-level warning。

实际影响

  • ZMQ 连接和 KVEventBatch 外层解码可以正常完成;
  • map-encoded BlockStored 不会进入 L1 indexer;
  • cache-indexer 无法使用这些事件维护对应实例的 L1 cache view;
  • 依赖 L1 hit ratio 的缓存感知路由无法获得这些前缀的实例位置信息。

预期行为

cache-indexer 应接受上述 map event,同时保持对当前 array representation 的兼容。

支持两种 representation 可以保留 cache-indexer 当前已接受的 payload,同时兼容本次观察到的 vLLM payload。

建议修复方向

在 L1 wire-format ingest 层统一读取具体 event:

  • array event:从现有 array index 读取 tag 和字段;
  • map event:从 type 和对应 map key 读取 tag 和字段;
  • 后续复用现有 normalization 和 indexer 逻辑。

一个最小兼容修改应不需要改变本次观察到的 batch framing 或现有 L1 indexer 接口。

建议覆盖:

  • array/map 形式的主要 KV events;
  • 未知或畸形 event 不阻止后续合法 event;
  • 接近实抓结构的回归用例。

范围

本 Issue 仅讨论具体 KV event 的 array/map wire representation。Multi-group 及其他 KV cache 数据模型语义未在本 Issue 中验证,应独立评估。

该问题只直接影响 L1 KV event ingest;L3 由独立的 Mooncake 索引链路维护。

likedislike
openFuyao-botopenFuyao-bot成员
9月3日 添加了label:sig/sig-ai-inference
openFuyao-bot
openFuyao-bot成员
9月3日 评论:

欢迎来到 openFuyao 社区

Hey @juedingfengzm , 感谢你对社区的贡献.

机器人使用手册

有关指令的使用,可以点击 此处 查看详情。开发人员可以在每个PR或Issue下方评论特定指令来触发机器人任务。您可以自助配置此仓库的 PR 合并规则,更多详情,请参阅此处。

联系指引

有疑问可以联系 SIG: sig-ai-inference ,
维护者是: @foxbit, @regandy ,
审核者是: @ShiroChen, @piamposer-li, @qinwenzh .

likedislike
zhengqinwenZZZzhengqinwenZZZ成员
9月3日 添加了label:feature-enhancement
pengkaijiepengkaijie
9月4日 关联了pull request:fix(ingest/l1): accept map-encoded KV events in L1 ingest for openFuyao/cache-indexer#17
pengkaijie
pengkaijie
9月9日 评论:

感谢老师提出这个 Issue!
我们已确认 cache-indexer 的 L1 ingest 当前仅处理 array-encoded 事件,无法兼容 map-encoded 事件。vLLM 0.24.0 之后引入了 map/MessagePack 编码格式,导致新版本的事件无法被正确解析。
目前已在 PR #47 中修复,已实现对map-encoded结构的兼容,同时0.24.0之前的版本亦能继续支持。

likedislike
zhengqinwenZZZzhengqinwenZZZ成员
9月9日 关联了看板:openFuyao - v26.09问题单
zhengqinwenZZZzhengqinwenZZZ成员
9月9日 关联了里程碑:v26.09
zhengqinwenZZZzhengqinwenZZZ成员
9月9日 issue状态由 开启 改变为 待测试
zhengqinwenZZZzhengqinwenZZZ成员
9月9日 issue状态由 待测试 改变为 已完成
zhengqinwenZZZzhengqinwenZZZ成员
9月9日 关闭了 issue