Prhub

#6861 [vllm] fix: crash in start_profile/stop_profile on non-master nodes when nnodes > 1

原始 PR 作者 kyle-zhangchi 合并时间 2026-07-01 05:50 文件变更 2 提交数 2 评论 5 代码增减 +30 / -0

执行摘要

修复多节点 profile 崩溃

PR body 明确指出:当 replica 跨多节点(nnodes > 1)时,只有 node_rank == 0 运行 run_server() 初始化 self.engine,其他节点运行 run_headless() 使 self.engine 为 None。但 start_profile() 和 stop_profile() 通过 asyncio.gather 广播到所有 replica,导致非 master 节点访问 self.engine 时抛出 AttributeError。

值得合并,修复明确,风险低,测试完备。对于依赖于多节点 profile 的团队,此 PR 修复了严重的运行时崩溃问题。

讨论亮点

主要讨论围绕测试修复展开。审核者 @Luosuu 要求作者修复失败的测试 test_vllm_start_stop_profile。作者 @kyle-zhangchi 在后续提交中增加了新测试用例并修正了原有测试(添加 mock_self.node_rank = 0)。后续 CI 通过后,@kyle-zhangchi 指出失败测试与本次变更无关,@Luosuu 批准合并。

实现拆解

  1. 核心逻辑修复:在 verl/workers/rollout/vllm_rollout/vllm_async_server.pystart_profilestop_profile 方法开头各增加一句 if self.node_rank != 0: return,在调用 self.engine 之前提前退出。这与文件中已有的 clear_kv_cacherelease_kv_cacheresume_kv_cache 等方法的 node_rank 守卫模式保持一致。
  2. 补充单元测试:在 tests/utils/test_server_profiler.py 中新增 test_vllm_start_stop_profile_non_master_node 异步测试用例。该测试设置 mock_self.node_rank = 1,分别调用 start_profilestop_profile,并验证 mock_engine.start_profilemock_engine.stop_profile 均未被调用(assert_not_called)。
  3. 调整现有测试:在已有的 test_vllm_start_stop_profile 用例中,为 mock_self 显式设置 mock_self.node_rank = 0,以确保该测试在 master 节点场景下依然正确。
文件 模块 状态 重要度
verl/workers/rollout/vllm_rollout/vllm_async_server.py vLLM 卷出 modified 5.75
tests/utils/test_server_profiler.py 性能分析器 modified 5.78

关键符号

start_profile stop_profile test_vllm_start_stop_profile_non_master_node

关键源码片段

verl/workers/rollout/vllm_rollout/vllm_async_server.py core-logic

核心修复文件,在 start_profile 和 stop_profile 中添加 node_rank 守卫,避免非 master 节点崩溃。

# verl/workers/rollout/vllm_rollout/vllm_async_server.py
# 修改前:start_profile 和 stop_profile 没有 node_rank 守卫,
# 在 nnodes > 1 时,非 master 节点的 self.engine 为 None,引发 AttributeError。
# 修改后:与 clear_kv_cache / release_kv_cache / resume_kv_cache 保持一致,
# 非 master 节点提前 return,不再调用 engine 方法。async def start_profile(self, **kwargs):
    if self.node_rank != 0:
        return
    if (
        self.profiler_controller.check_enable()
        and self.profiler_controller.check_this_rank()
        and self.profiler_controller.is_discrete_mode()
    ):
        await self.engine.start_profile(**kwargs)async def stop_profile(self):
    if self.node_rank != 0:
        return
    if (
        self.profiler_controller.check_enable()
        and self.profiler_controller.check_this_rank()
        and self.profiler_controller.is_discrete_mode()
    ):
        await self.engine.stop_profile()
tests/utils/test_server_profiler.py test-coverage

新增测试覆盖非 master 节点场景,并修复原有测试以正确设置 node_rank。

# tests/utils/test_server_profiler.py
# 新增测试:验证非 master 节点不会调用 engine 的 profile 方法
async def test_vllm_start_stop_profile_non_master_node(self):
    try:
        from verl.workers.rollout.vllm_rollout.vllm_async_server import vLLMHttpServer
    except ImportError:
        self.skipTest("vllm or dependencies not installed")
        return
​
    mock_profiler = MagicMock()
    mock_profiler.check_enable.return_value = True
    mock_profiler.check_this_rank.return_value = True
    mock_profiler.is_discrete_mode.return_value = True
​
    mock_engine = AsyncMock()
​
    mock_self = MagicMock()
    mock_self.node_rank = 1 # non-master node, should skip
    mock_self.profiler_controller = mock_profiler
    mock_self.engine = mock_engine
​
    await vLLMHttpServer.start_profile(mock_self)
    mock_engine.start_profile.assert_not_called()
​
    await vLLMHttpServer.stop_profile(mock_self)
    mock_engine.stop_profile.assert_not_called()# 同时,在已有的 test_vllm_start_stop_profile 中增加了 mock_self.node_rank = 0

评论区精华

测试修复 测试

@Luosuu 要求修复 test_vllm_start_stop_profile 测试。@kyle-zhangchi 随后提交修复,在原有测试中添加 mock_self.node_rank = 0,并新增非 master 节点测试。

结论:测试已修复并通过 CI。 · 已解决

风险与影响

风险极低。变更只添加了 if guard,不改变已有控制流,且与项目中其他类似方法(如 clear_kv_cacherelease_kv_cache)的模式一致。测试覆盖了 master 和 non-master 两种场景。

仅影响多节点部署(nnodes > 1)下 vLLM rollout 的 profile 功能。修复后非 master 节点不会再因访问 None 的 engine 而崩溃,profile 操作仅在 master 节点执行。

修复简单,风险低

关联 Issue

未识别关联 Issue

当前没有检测到明确关联的 Issue 链接,后续同步到相关引用后会出现在这里。

完整报告

参与讨论