已关闭
[Medium][optix/multihost_infer] health() 每次轮询发真实推理请求,压测运行期被 scheduler 每秒注入探针并可能误杀 trial #411
mjsz创建于  13 天前关闭于  5 天前
mjsz
mjsz成员
13 天前 创建

Bug 验证报告:PR686 发现 3(health() 探针在压测运行期每秒注入真实推理请求)

  • run-id: bugverify-pr686-f3-20260903-01(v3:2026-09-04 用户路径实测回填完成)
  • 验证日期: 2026-09-03(内部佐证)/ 2026-09-04(用户路径真机实测)
  • 结论: ✅ 确认是 bug,用户路径实测闭环(必现)。在真机(8×910B2 共享服务器,单卡运行 vLLM 0.22.1)按 README 官方流程执行 msmodeling optix -e multihost_infer -b evalscopeperf --backup,tcpdump 与 vLLM API 访问日志双通道证实:压测运行期间探针推理请求每秒 1 条持续注入,贯穿整个压测窗口
  • 建议 Issue 标题: [Medium][optix/multihost_infer] health() 每次轮询发真实推理请求,压测运行期被 scheduler 每秒注入探针并可能误杀 trial

Problem

MultiHostSimulator.health() 在 master /health 返回 200 时无条件调用 test_cluster_simple() 发一条真实 completion 请求(prompt="Hello"max_tokens=10、timeout=30,simulator.py:78-95,123-129)。设计意图仅覆盖启动等待,但 optix scheduler 的 monitoring_status()benchmark 压测运行期间对"实现了 health() 且不是内置 Simulator 类"的模拟器每秒调用一次 health()(optix/optimizer/scheduler.py:520-528,536 time.sleep(1))。MultiHostSimulator 结构化满足 SupportsHealth、不满足内置排除条件,落入该分支。后果:① 压测窗口每秒 1 条额外推理请求污染吞吐/延迟测量;② 高负载下探针瞬时失败 → health() 非 running → scheduler 抛 SubprocessError 误杀当轮 trial(scheduler.py:522-528)。

Reproduction(用户方式:命令 + 配置,与 README《使用》章节一致;已实测可跑通)

环境:任意可运行 vLLM 的 Linux(实测在 8×910B2 共享服务器的 vllm-ascend:v0.22.1rc1 容器内、单 NPU、Qwen3-0.6B 权重完成)。

Step 1: 写两个配置文件(内容整体内联,实测原样可用)

集群配置 <插件目录>/config.toml(单机:只写 node,不写 workers;CLUSTER_CONFIG_PATH 指向它):

docker_use_sudo = false

[vllm_mix]
chips_per_node = 8

[[vllm_mix.node]]
host = "<本机IP>"
nic_name = "<本机网卡名>"     # 显式指定,跳过探测

寻优配置(optix/config.toml[vllm] + [evalscopeperf] 段):

[vllm]
[vllm.command]
host = "0.0.0.0"
port = "9975"
model = "/data/models/Qwen3-0.6B"          # 任意可加载的小模型
served_model_name = "qwen"
others = "--max-model-len 4096 --gpu-memory-utilization 0.4"

[[vllm.target_field]]
name = "MAX_NUM_BATCHED_TOKENS"
config_position = "env"
min = 4096
max = 4096
dtype = "int"
value = 4096

[[vllm.target_field]]
name = "MAX_NUM_SEQS"
config_position = "env"
min = 32
max = 64
dtype = "int"
value = 32

[[vllm.target_field]]
name = "CONCURRENCY"
config_position = "env"
min = 16
max = 16
dtype = "int"
value = 16

[[vllm.target_field]]
name = "REQUESTRATE"
config_position = "env"
min = 16
max = 16
dtype = "float"
value = 16

[evalscopeperf]
output_path = "/tmp/evalscope_out"
[evalscopeperf.command]
url = "http://127.0.0.1:9975/v1/completions"
model = "qwen"
tokenizer_path = "/data/models/Qwen3-0.6B"
dataset = "openqa"                          # evalscope 内置数据集(本地 jsonl 路径不被识别为数据集插件)
outputs_dir = "/tmp/evalscope_out"
others = "--parallel 16 --number 400"
  • 预期: 配置就绪
  • 实际: ✅ 插件正常生成 start_node.sh 并注入寻优变量

Step 2: 另开终端挂抓包(探针观测点)

tcpdump -i lo -A -s0 'tcp port 9975' > probe_capture.txt 2>&1
  • 实际: ✅ 全程抓到探针与压测流量

Step 3: 启动寻优(README 同款命令)

msmodeling optix -e multihost_infer -b evalscopeperf --backup -c <上述寻优配置>
  • 预期: 基线 trial:生成脚本 → 启动服务 → 健康等待 → evalscope 压测
  • 实际: ✅ 服务启动成功(health 通过),evalscope perf 以 --dataset openqa --parallel 16 --number 400 真实发载(实测进度 41/400 → 336/400 持续运行)

Step 4: 压测窗口内观察 Step 2 抓包(核心观测)

  • 预期(正确行为): 压测期间服务端只收到 evalscope 发出的压测请求
  • 实际: ✅ 复现 bug——整个压测窗口内,特征 payload 探针每秒 1 条持续注入:
    • 探针请求 payload 恒为 {"model": "qwen", "prompt": "Hello", "max_tokens": 10}(即 test_cluster_simple 的硬编码签名,与 evalscope 的 openqa 数据集流量截然可分);
    • 逐秒计数(tcpdump 抓包,07:41:38–07:42:27 窗口,每秒 2 个报文 = 1 个请求拆包):每秒恒定 1 条探针,与 scheduler sleep(1) 轮询节拍完全一致;
    • vLLM API server 访问日志同样显示 GET /health(每秒轮询)与 POST /v1/completions(每秒探针)交替出现;
    • 真实负载轮(openqa 400 请求压测中)探针计数持续增长(压测进行至 84% 时已捕获 614 条 Hello 探针报文);
    • 注入速率与 CONCURRENCY/REQUESTRATE 设置无关。

Step 5: 对照(证明非压测流量)

  • ✅ 固定 payload 法:evalscope 使用 openqa 数据集(prompt 各不相同),探针恒为 "prompt":"Hello",逐条可分;
  • 计数差法可用:压测窗口秒数 ≈ /v1/completions 总数 − evalscope 报告请求数。

Step 6(可选,误杀现象): 满负载下探针超时 → SubprocessError 误杀 trial。本次实测未单独复现该分支(探针均 200),维持代码路径证明(scheduler.py:522-528 条件)+ 内部佐证 T3。

Expected

运行期状态监控只应读取轻量健康端点(/health);推理探针仅用于启动就绪判定;探针异常不应左右压测期 trial 成败。

Actual

压测全程每秒 1 条 max_tokens=10 的探针请求进入服务(真机实测,两种观测通道一致);探针一旦非 200/超时,即使服务本身健康,scheduler 也按服务失败处理终止当轮 trial(代码路径确定)。代码无任何开关可关闭该行为。

Root Cause

① 插件把"集群级就绪判定"(需真实推理)直接实现进被 scheduler 高频轮询的 health(),未区分启动阶段与运行阶段;② scheduler 的监控轮询按"是否内置 Simulator 类"做排除(scheduler.py:520-521),插件模拟器结构化满足 SupportsHealth(protocols.py:19-21)且不是 Simulator 子类,被当作需每秒健康检查的对象;内置 vllm/mindie 模拟器因是该子类被排除——同场景行为不对称。

Impact

  • 压测测量被背景负载污染:每秒 1 条探针持续占用并发槽位与调度周期,绝对吞吐/延迟系统性失真(参数相对排序大概率保留)
  • 误杀 trial 且带方向性偏置:满负载参数组合最易把探针挤到超时,可能系统性淘汰最优参数(代码路径确定 + 内部佐证;真机满负载触发为概率性)
  • 不损坏数据、不崩溃,定级 Medium

事实 / 推断 / 待确认

  • 事实:真机用户路径复现(每秒探针 + 双观测通道 + 真实负载轮);内部佐证 S1-S4/T1-T3(类分发、1:1 注入、每秒速率、探针失败→杀 trial 条件);代码行号引用。
  • 推断:探针对吞吐数字的具体扭曲幅度;满负载下误杀频率与向保守参数的偏置。
  • 待确认:无(机制闭环)。

原始抓包、运行日志与配置已完整存档,必要时可提供。

修复方向(供 Issue 讨论)

  1. health() 区分阶段:启动阶段(Stage.start)才发推理探针,running 后 N 秒内只查 /health(节流缓存最近探针结果);
  2. 或插件实现 check_success(),让 scheduler 走 benchmark 判定路径,绕开每秒 health 轮询;
  3. 或 scheduler 侧放宽:非内置模拟器运行期降频轮询/只查进程与轻端点。
likedislike
mjszmjsz成员
13 天前 添加了label:bug
kai1949
kai1949成员
13 天前 评论:

/label add triaged
👋 您好,欢迎向 MindStudio-Modeling 提交 Issue!
我们已收到您的反馈,感谢你对开源社区的支持。🎉

📅处理时效: 维护团队将在8小时内 查看并回复您的问题(工作日)。
🔍自助查询: 在等待期间,建议您先查阅以下资料,可能已有解决方案:

📖 MindStudio-Modeling官方文档
📝 贡献者指南

请确保 Issue 描述清晰,包含复现步骤和日志,这将帮助我们更快定位问题。谢谢!

likedislike
ascend-robotascend-robot成员
13 天前 添加了label:triaged
Tttcool成员
12 天前 移除了负责人 tt0cool
Tttcool成员
12 天前 将 liu977803265 设为负责人
Zzhenghaojie成员
8 天前 关联了里程碑:MindStudio 26.3.0
Zzhenghaojie成员
8 天前 关联了里程碑:MindStudio 26.2.0
liu977803265liu977803265成员
6 天前 关联了pull request:【多机混布】health检查接口增强& 杀残留进程增强
liu977803265liu977803265成员
5 天前 issue状态由 TODO 改变为 DONE
ascend-robotascend-robot成员
5 天前 添加了label:resolved
liu977803265liu977803265成员
5 天前 关闭了 issue
liu977803265liu977803265成员
5 天前 添加了label:resolved