Prhub

#20287 fix: scheduler launch hang when non-current rank dies

原始 PR 作者 alphabetc1 合并时间 2026-03-29 15:28 文件变更 1 提交数 19 评论 20 代码增减 +36 / -11

执行摘要

修复调度器启动时非当前 rank 死亡导致的挂起问题。

根据 PR body,动机是解决初始化时等待循环只监控当前读取 rank 的问题:如果其他 rank 被杀死(例如由 OS OOM killer 通过 SIGKILL),进程会无限阻塞在 recv() 上,导致整个启动挂起。具体复现场景涉及内存限制下 workers 被 OOM killer 杀死的情况,当 rank 0 存活而更高 rank 死亡时,启动器会无错误地挂起。

该 PR 值得精读,特别是对于涉及分布式启动或进程管理的工程师。关注的设计决策包括:从阻塞到轮询的转变、检查所有进程而非仅当前进程的健壮性权衡,以及错误消息的优化建议。这些决策体现了在可靠性和性能之间的平衡。

讨论亮点

review 中的核心讨论包括:

  1. gemini-code-assist[bot] 建议提取错误消息模板以减少代码重复,属于风格优化。
  2. slin1237 提出是否跳过已确认 ranks 以优化检查范围,建议使用 for j in range(i, len(scheduler_procs)):。alphabetc1 回复称“Even a rank that’s already been confirmed can still die afterward, and this check should be fairly cheap”,最终决定保持检查所有进程,以确保健壮性。讨论焦点是设计权衡(检查范围)和代码清晰度,没有未解决疑虑。

实现拆解

实现方案集中在 python/sglang/srt/entrypoints/engine.py 文件的 _wait_for_scheduler_ready 函数。关键改动点:

  1. 将阻塞接收改为使用 poll(timeout=5.0) 进行轮询,避免无限等待。
  2. 在轮询超时时,添加一个循环检查所有 scheduler_procs 进程是否存活,如果检测到任何 rank 死亡,立即调用 join() 并抛出 RuntimeError 包含诊断信息(如提示 OOM killer)。
  3. 更新错误处理逻辑,在 EOFError 时也抛出类似错误。这确保了无论迭代顺序,所有 rank 的死亡都能被快速检测。
文件 模块 状态 重要度
python/sglang/srt/entrypoints/engine.py entrypoints modified 8.0

关键符号

_wait_for_scheduler_ready

分析完成后,这里会展示 LLM 生成的相对完整源码片段和详细注释。

评论区精华

检查范围优化 设计

slin1237 建议跳过已确认 ranks 以优化检查循环,alphabetc1 反对,认为已确认 rank 仍可能后续死亡且检查成本低。

结论:最终保持检查所有进程,以确保健壮性。 · 已解决

错误消息模板提取 style

gemini-code-assist[bot] 建议提取错误消息到模板字符串以减少重复,提升代码清晰度。

结论:未明确是否采纳,但 PR 被批准,表明风格建议被考虑。 · 已解决

风险与影响

技术风险包括:

  1. 性能风险:轮询超时 5.0 秒和检查所有进程可能增加启动延迟,尤其在大量 ranks 时;但基于讨论,检查成本被认为较低。
  2. 回归风险:修改核心启动路径 _wait_for_scheduler_ready,如果轮询逻辑或错误处理有误,可能导致假阳性或假阴性故障检测。
  3. 兼容性风险:无,变更不涉及外部接口。风险较低,因为改动较小且经过 review。

影响范围:

  1. 对用户:解决启动挂起问题,提升用户体验,错误消息帮助诊断 OOM 问题,影响程度中等。
  2. 对系统:增强调度器启动的可靠性,防止无声故障,影响核心启动路径。
  3. 对团队:代码变更集中,易于维护,但需要关注轮询开销对性能的潜在影响。
核心路径变更 潜在性能开销

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论