Prhub

#27446 Fix PP is_fully_idle missing in-flight microbatches

原始 PR 作者 fzyzcjy 合并时间 2026-06-07 17:38 文件变更 4 提交数 12 评论 6 代码增减 +58 / -14

执行摘要

修复 PP 下 is_fully_idle 忽略在途微批次

PR body指出:is_fully_idle() cannot see in-flight PP microbatches. 在流水线并行下,调度器将在途微批次保存在self.mbs中;当最后一块prefill chunk被调度后,chunked_req已被清空,running_batch等结构也为空,但微批次仍然在流水线中飞行。此时若处理/flush_cache,它会通过idle检查并重置KV缓存,导致后续处理批结果时触发assert node is self.root_node错误。这是PP+分块预填充部署调用/flush_cache时可能复现的bug。

该PR值得精读,特别是调度器idle-gating逻辑的细节和PP下竞态条件的分析。修复方案简洁,测试设计精妙,适合作为类似竞争条件修复的参考。

讨论亮点

gemini-code-assist[bot]提出两条建议:在_pp_microbatches_drained_get_all_reqs中,使用getattr和默认空列表来访问running_mbsmbslast_mbs等属性,避免在调度器初始化不完全时引发AttributeError。这些建议未在PR中采纳或解决,但PR已合并。

实现拆解

  1. Scheduler idle-gating修复:在python/sglang/srt/managers/scheduler.py中,提取_pp_microbatches_drained()方法,在原有running_mbs检查基础上,额外检查self.mbs中所有微批次槽是否为空。is_fully_idle()改用该方法,确保idle判断考虑所有在途微批次。
  2. 测试框架请求可见性修复:在python/sglang/test/scripted_runtime/context/queries.py_get_all_reqs()中,当PP开启时,遍历所有微批次槽(mbslast_mbsrunning_mbs),防止在途请求被遗漏导致finished状态提前为true。
  3. 测试框架重置逻辑修复:在python/sglang/test/scripted_runtime/scheduler_hook.py_reset_engine_state()中,改用scheduler.is_fully_idle()判断引擎是否安静,并在超时时抛出RuntimeError,避免静默flush_cache
  4. 新增回归测试:在test/registered/chunked_prefill/test_scripted_core_4gpu.py中新增test_pp_flush_cache_during_inflight_chunk_results,该测试精确构造竞争窗口(队列和当前槽清空但微批次在途),执行flush_cache并断言请求能正常完成。
文件 模块 状态 重要度
python/sglang/srt/managers/scheduler.py 调度器 modified 7.09
test/registered/chunked_prefill/test_scripted_core_4gpu.py 测试用例 modified 6.61
python/sglang/test/scripted_runtime/scheduler_hook.py 测试钩子 modified 5.58
python/sglang/test/scripted_runtime/context/queries.py 查询工具 modified 5.32

关键符号

_pp_microbatches_drained _reset_engine_state _get_all_reqs test_pp_flush_cache_during_inflight_chunk_results _script_flush_during_inflight_chunk_results

关键源码片段

python/sglang/srt/managers/scheduler.py core-logic

核心修复文件,新增 `_pp_microbatches_drained` 方法并修改 `is_fully_idle` 调用。

def is_fully_idle(self, for_health_check=False) -> bool:
    idle = (
        self.running_batch.is_empty()
        and self.chunked_req is None
        and not self.dllm_manager.any_staging_reqs()
        and (self.last_batch is None or self.last_batch.is_empty())
        and (self.cur_batch is None or self.cur_batch.is_empty())
        and (not self.enable_overlap or len(self.result_queue) == 0)
        and self._pp_microbatches_drained()
    )
    # ... 其余检查 ...
    return idledef _pp_microbatches_drained(self) -> bool:
    """检查所有PP微批次是否都已排空。"""
    if self.ps.pp_size == 1:
        return True # 单机无 PP
    return all(x.is_empty() for x in self.running_mbs) and all(
        mb is None or mb.is_empty() for mb in self.mbs
    )

评论区精华

防御性访问 mbs/running_mbs 设计

gemini-code-assist[bot] 建议在 `_pp_microbatches_drained` 和 `_get_all_reqs` 中使用 `getattr` 并设置默认空列表,避免 `AttributeError`。

结论:未采纳,PR 已合并。 · pending

风险与影响

  • 回归风险:修改了is_fully_idle(),该函数用于多个场景(flush_cache、HiCache attach/detach、健康检查等)。但通过保留pp_size==1短路和新增测试,风险较低。
  • 性能影响:循环检查所有微批次槽(最多pp_async_batch_depth + 1个),仅在idle判定时执行,非热点路径,影响可忽略。
  • 防御性不足:gemini-code-assist[bot]提出的AttributeError风险在现有初始化顺序下不会触发,但未来重构可能暴露。
  • 用户影响:修复了PP+分块预填充场景下flush_cache导致KV缓存损坏的bug,对使用RL权重更新等流程的用户至关重要。
  • 系统影响:idle-gating行为更准确,使所有依赖idle状态的操作更加安全。
  • 团队影响:新增的回归测试为同类竞态条件提供了可复现的验证方法。
PP idle-gating 变更 竞争条件修复 KV 缓存损坏风险

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论