Prhub

#27238 Add quiet mode for busy mem check (level 1: buffer + dump on leak)

原始 PR 作者 hnyls2002 合并时间 2026-06-04 16:31 文件变更 1 提交数 5 评论 4 代码增减 +27 / -5

执行摘要

为繁忙时内存检查添加安静模式,减少日志噪声

繁忙时内存检查在每次事件循环迭代中都运行,之前当级别 > 1(含 2)时,每轮迭代都会记录 [Mem Check (BUSY)] 日志,在高负载场景下产生大量噪声,影响可观测性和排查效率。PR 希望在不牺牲泄漏检测能力的前提下,通过缓存日志的方式减少稳态日志量,仅在泄漏发生时输出完整上下文。

该 PR 设计简洁,改动集中,风险低,值得合并。建议在文档中备注级别 1 的缓冲区容量说明,以便用户了解极限场景下的日志覆盖能力。

讨论亮点

PR 没有产生 review 评论,所有决策由作者 hnyls2002 通过多个提交自行迭代完成:第一版实现基本 buffer 逻辑,第二版明确分化为 level 1 和 level 2,后续提交清理注释和合并主分支。

实现拆解

  1. 新增环形缓冲区和配置常量:在文件顶部定义 BUSY_MEM_CHECK_LOG_RING_SIZE = 1000,在 SchedulerInvariantChecker 数据类中新增字段 recent_busy_msgs: Deque[str],使用 deque(maxlen=1000) 初始化,自动维护最近 1000 条日志。
  2. 重构 self_check_during_busy 的日志逻辑:原有逻辑仅在 level > 1 时逐条 logger.info。现改为先读取 level 值,构造完整的日志行 full_lineswa_line(如果存在),然后根据 level 分支处理:
    • level > 1(非安静模式):直接 logger.info 输出每轮消息,保持原行为。
    • level == 1(安静模式):将构造好的消息行追加到 self.recent_busy_msgs 中,不输出;但若检测到泄漏(full_leakswa_leak 为真),则循环输出缓冲区中所有消息,用于泄漏时的上下文诊断。
  3. 保留断言机制不变:无论哪种级别,泄漏时仍会触发 assert not full_leakassert not swa_leak,确保泄漏被及时捕获。
文件 模块 状态 重要度
python/sglang/srt/managers/scheduler_components/invariant_checker.py 调度器 modified 7.25

关键符号

SchedulerInvariantChecker.self_check_during_busy

关键源码片段

python/sglang/srt/managers/scheduler_components/invariant_checker.py core-logic

唯一的变更文件,实现了所有逻辑:新增环形缓冲区字段、修改 self_check_during_busy 方法以支持三种日志级别,并在泄漏时 dump 历史消息后仍保持断言。

# 文件顶部新增环形缓冲区大小常量
BUSY_MEM_CHECK_LOG_RING_SIZE = 1000@dataclass(kw_only=True, slots=True)
class SchedulerInvariantChecker:
    # ... 其他字段不变 ...
    recent_busy_msgs: Deque[str] = field(
        default_factory=lambda: deque(maxlen=BUSY_MEM_CHECK_LOG_RING_SIZE)
    )
​
    def self_check_during_busy(self):
        # ... 前置检查代码不变 ...
​
        # 读取当前级别
        level = envs.SGLANG_ENABLE_STRICT_MEM_CHECK_DURING_BUSY.get()
        # 构造日志行(基于池状态和 uncached 计算,略)
        full_line = f"[Mem Check (BUSY)] {full_msg}"
        swa_line = f"[Mem Check (BUSY)] {swa_msg}" if swa_msg else None
​
        if level > 1:
            # Verbose: 每轮直接输出,原行为
            logger.info(full_line)
            if swa_line:
                logger.info(swa_line)
        elif level == 1:
            # Quiet: 缓冲日志,仅泄漏时 dump
            self.recent_busy_msgs.append(full_line)
            if swa_line:
                self.recent_busy_msgs.append(swa_line)
            if full_leak or swa_leak:
                # 泄漏时打印缓冲区内所有历史消息,用于定位趋势
                for msg in self.recent_busy_msgs:
                    logger.info(msg)
​
        # 断言机制保持与原来一致,不因级别改变而跳过
        assert not full_leak, f"Full Pool Mem Leak Detected! {full_msg}"
        assert not swa_leak, f"SWA Pool Mem Leak Detected! {swa_msg}"

(注:因篇幅省略前置检查和 pool check 方法,其逻辑未变更。)

评论区精华

没有提炼出高价值讨论线程

当前评论区没有形成足够清晰的争议点或结论,后续有更多讨论时会体现在这里。

风险与影响

  1. 环形缓冲区内存占用:1000 条字符串消息的内存占用在数百 KB 级别,属于可接受范围;但若消息较长或未来频繁改动格式,需关注内存增长。
  2. 日志丢失风险:安静模式下,当泄漏持续发生时,缓冲区可能被后续消息覆盖旧消息,导致泄漏前早期趋势丢失。当前 1000 条容量对于典型调度循环场景已足够,但仍需监控是否够用。
  3. 行为一致性:级别 1 在无泄漏时完全不输出日志,可能被运维人员误以为检查未运行。需要文档或注释明确说明。

影响范围仅限 SchedulerInvariantChecker 类的自检方法,该模块本身是 debug/assert 用途,不构成关键业务路径。用户需要显式设置环境变量 SGLANG_ENABLE_STRICT_MEM_CHECK_DURING_BUSY=1 才会启用安静模式。对于生产环境,启用级别 1 可以在保持泄漏检测的同时显著减少日志泛洪,提升可观测性。

缺少测试覆盖

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论