快速结论:该现象出现在 vLLM 使用 OffloadingConnector + TieringOffloadingSpec 的 "fs" 二级缓存、两个独立引擎实例共享同一 root_dir 时:B 实例查询 A 写入的前缀后,vllm:kv_offload_tiering_active_promotion_jobs:('1:fs',) 始终为 0,B 回退到全量重算。优先排查的是:把 active_promotion_jobs 当成”是否发生 promotion”的判据是否成立,以及 chunk_queries/lookup_async_delay_seconds 是否被误读为命中证据。
适用环境:vLLM 0.31.0(镜像 vllm/vllm-openai:v0.31.0);模型 meta-llama/Llama-3.1-8B-Instruct;两个实例各 1 张 GPU、位于不同主机;--block-size 64;两侧均设置 PYTHONHASHSEED=123。原始报告使用 3FS over FUSE 挂载,后续复测改用普通共享 POSIX 网络文件系统(非 3FS)。
最快修复方案:暂无确认的一步修复方案。Issue 最终未复现该问题,原报告中的若干判据被作者本人撤回;在更新后的共享 POSIX 复测中,lookup 命中 67/67、read_bytes 达到 562,036,736,external_prefix_cache_hits 为 4,288(= 67 × 64),promotion 路径本身工作正常。
注意事项:本次复测不在 3FS 上进行(作者因无关的 3FS RDMA 问题无法重跑),因此 3FS/FUSE 场景下的行为仍属未验证;维护者的本地探针也只覆盖本地文件系统上的源码级测试,不代表 Llama 服务部署或 3FS 环境的完整复现。
问题场景
用户在 vLLM 中启用 KV 缓存分层卸载:双实例(native1 / native2)各启动一个引擎,均配置 OffloadingConnector、kv_role: kv_both,并通过 kv_connector_extra_config 指定 TieringOffloadingSpec,cpu_bytes_to_use 为 10737418240、block_size 为 64,二级层为 {"type":"fs","root_dir":"","n_read_threads":16,"n_write_threads":16}。先由实例 A 写入一个新前缀,等待约 20 秒后由实例 B 以相同前缀首次查询,期望 B 从 fs 层提升(promotion)数据而非重算。
报错原文
[Bug] TieringOffloadingSpec fs-tier lookup succeeds but never triggers a promotion job (active_promotion_jobs stays 0)
vllm:kv_offload_tiering_active_promotion_jobs:('1:fs',) # stayed at 0 for the entire test
ERROR [thread_pool.py:181] Job 0 block I/O failed: [Errno 17] File exists:
.../1870b67d5b659d799e715c585ce8d3e9e0a4e8e2c01131bcc00f51eb23bde6af.bin
原因分析
按 Issue 最终结论,原报告的核心判断不成立,已由报告作者逐条更正:
- 不能以
active_promotion_jobs判断是否发生 promotion。它是当前进行中任务的瞬时计数,请求结束后回落为 0 属正常。维护者与作者一致确认:该 gauge 只反映仍在运行的 job。复测中 B 在请求期间约每 20 ms 采样,90 次里 7 次大于 0、72 次里 7 次大于 0,峰值为 1,请求前后均为 0。 chunk_queries与lookup_async_delay_seconds不能作为命中证据。二者在 miss 时同样会记录,因此原报告”lookup 确实找到了条目”的说法缺乏证据;3FS 那次运行也没有记录chunk_hits、read_bytes或promotion_allocation_failures。- 路径与哈希未出现不匹配。独立初始化的进程在
PYTHONHASHSEED=123下生成相同的哈希与文件名,A 存储的 67 个文件与 B 查询的 67 个路径完全一致,全部命中。 - 并发写导致的
EEXIST属于另一条问题(由 #61390 覆盖):内容寻址块已被另一写入方落盘时,被计为cascade_job_failure,而非按”去重后无需操作”处理。
综上,在共享 POSIX 文件系统上,未复现”lookup 成功但不触发 promotion”的主问题;3FS 挂载下是否仍存在差异,可能原因尚未隔离。
环境排查
- vLLM 版本是否为 0.31.0(或对应镜像),以及是否包含 #61390 相关修复。
- 两个实例的
PYTHONHASHSEED是否严格一致(复测使用 123)。 --block-size、模型路径、--kv-transfer-config的cpu_bytes_to_use、block_size和secondary_tiers参数是否两侧完全一致。root_dir是否为两侧可见且可读写的同一挂载;3FS over FUSE 与普通 POSIX 网络文件系统需分开验证。- GPU 数量、是否启用
--enable-prefix-caching --enforce-eager,以及主机间网络文件系统的读写可见性延迟。 - 指标采样方式:是否在
active_promotion_jobs的采样中覆盖了请求运行期间,而非只读请求前后的值。
解决步骤
- 先纠正判据:不要只看请求结束后的
active_promotion_jobs,改为在请求执行期间高频采样;同时收集chunk_queries、chunk_hits、read_bytes、promotion_allocation_failures、promotion_job_failures、cascade_job_failures、external_prefix_cache_hits与 GPUprefix_cache_hits的增量。 - 确认 A 写入的文件名与 B 查询的文件名是否一一对应(可优先尝试增加仅记录日志的 lookup 包装,不改变返回值),排除 key/path 不一致。
- 核对两侧
PYTHONHASHSEED、--block-size与kv-transfer-config是否逐字一致。 - 在普通共享 POSIX 文件系统上复跑一次单前缀的两实例测试,记录上述指标与请求后新增
.bin文件数;若 0 个新增且external_prefix_cache_hits等于命中块数,则说明 fs 层复用生效。 - 若仅在 3FS over FUSE 下复现,将问题限定到文件系统语义层排查;否则继续对照
chunk_hits与read_bytes判断是未命中、命中但受限于 CPU 容量,还是命中但指标采样未覆盖。 - 遇到并发写
EEXIST报错时,按 #61390 追踪写入侧去重逻辑,不要与 promotion 问题混为一谈。
验证方法
在 B 首次请求同一前缀期间,若 active_promotion_jobs 出现非零采样(复测中峰值为 1)、chunk_hits 等于 chunk_queries、read_bytes 与 A 的 write_bytes 一致(复测均为 562,036,736)、external_prefix_cache_hits 等于命中块数 × 64(复测为 4,288),且 B 未新增任何 .bin 文件、各类 failure 计数为 0,即可确认 fs 层 lookup 与 promotion 链路正常。注意该结论仅在复测所用的共享 POSIX 文件系统上成立。
参考来源
AI 工具推荐
想把多个 AI 模型放在一个入口?
GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。
推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。
这个方案解决了吗?
可以继续搜索完整报错,或查看同一工具的其他排查指南。


