Prhub

#37194 [Fix] Shut hicache test servers down gracefully before SIGKILL

原始 PR 作者 hnyls2002 合并时间 2026-08-31 13:45 文件变更 13 提交数 2 评论 3 代码增减 +33 / -43

执行摘要

测试服务器关机改为优雅关闭,释放钉住主机 KV 池

PR body 明确说明了动机:Replaces bare kill_process_tree with the existing terminate_and_kill_process_tree helper in tests that enable hierarchical cache, so the server releases its pinned host KV pool in userspace instead of leaving the kernel to unpin it during reclaim. 即裸 SIGKILL 会让内核延迟解钉,显存残留时间过长,导致下一个测试因显存不足而失败。

值得快速浏览而非精读。作为“测试进程清理标准化”的示例,test_hicache_storage.py 中清晰的动机注释和统一 helper 的使用方式是主要看点;不需要深入理解 HiCache 内部实现。

讨论亮点

本 PR 无实质 review 讨论(review_comments_count 为 0),Issue 评论仅为流程性操作:作者发出 /rerun-test test_hicache_variants.py .../tag-and-rerun-ci,github-actions bot 汇总了各 runner 的重跑结果,全部为

实现拆解

  1. 锁定目标范围:覆盖所有启用 --enable-hierarchical-cache 的 registered 测试,包括 HiCache 存储、变体、PP、Qwen3.5、NPU、AMD MI35X、Inkling-Small-NVFP4、Kimi-K3-B300 以及 PD 解耦卸载测试,共 13 个文件。
  2. 统一导入来源:从 sglang.test.test_utils 导入 terminate_and_kill_process_tree,同时移除 sglang.srt.utils 中不再使用的 kill_process_tree 引用,将测试进程清理逻辑收敛到同一个工具函数。
  3. 替换手写关闭序列test_hicache_storage.py 中原来的 terminate() + wait(timeout=60) + kill_process_tree(pid) 三行被 helper 单行替代;test_disaggregation_decode_offload.py 中 prefill/decode/load balancer 三个进程的 kill_process_tree(pid) + wait() 也改为三次 helper 调用,语义等价(helper 内部负责 TERM → 等待 → KILL 的完整序列)。
  4. 保留必要的收尾等待:各测试继续保留 time.sleep(5)_wait_for_gpu_idle_in_ci(timeout=...),给驱动与内存回收留出余量。
  5. CI 配套验证:作者通过 /rerun-test 在 1-gpu-h100、1-gpu-5090、2-gpu-h100、4-gpu-h100 等 runner 上重跑了 7 个目标测试,全部通过;无新增配置或部署改动。
文件 模块 状态 重要度
test/registered/hicache/test_hicache_storage.py 缓存存储 modified 4.95
test/registered/models_e2e/test_inkling_small_nvfp4.py 模型精度 modified 4.41
test/registered/disaggregation/test_disaggregation_decode_offload.py PD 解耦 modified 4.3
test/registered/models_e2e/test_kimi_k3_b300.py K3 机型 modified 4.2
test/registered/hicache/test_hicache_storage_runtime_attach_detach.py 运行时挂载 modified 4.11
test/registered/hicache/test_hicache_variants.py 缓存变体 modified 4.06
test/registered/amd/test_deepseek_r1_hicache_mi35x.py DeepSeek 缓存 modified 3.99
test/registered/hicache/test_hicache_storage_file_backend.py 文件后端 modified 3.99
test/registered/hicache/test_pp_with_hicache.py 管道并行 modified 3.99
test/registered/hicache/test_qwen35_hicache.py Qwen 缓存 modified 3.99
test/registered/npu/basic_function/HiCache/test_npu_hicache_mha.py NPU 缓存 modified 3.59
test/registered/npu/basic_function/HiCache/test_npu_hicache_mla.py NPU 缓存 modified 3.59
test/registered/npu/basic_function/HiCache/test_npu_hierarchical_cache.py NPU 缓存 modified 3.59

关键符号

TestHiCache.tearDownClass HiCacheBaseServer.tearDownClass _stop_server TestDisaggregationDecodeOffload.test_mmlu_double_eval

关键源码片段

test/registered/hicache/test_hicache_storage.py test-coverage

改动最核心的 HiCache 存储测试,原手写 terminate+wait+kill 序列被 helper 单行替代,保留了对钉住主机 KV 池泄漏问题的解释性注释。

class TestHiCache(CustomTestCase, MMLUMixin):
    @classmethod
    def setUpClass(cls):
        # 启动带分层缓存的服务器:file 后端、大页,hicache 容量按平台调整
        cls.process = popen_launch_server(
            cls.model,
            cls.base_url,
            timeout=DEFAULT_TIMEOUT_FOR_SERVER_LAUNCH,
            other_args=[
                "--enable-hierarchical-cache",
                "--mem-fraction-static", 0.7,
                "--hicache-size", 100 if not _is_hip else 200,
                "--page-size", "64",
                "--hicache-storage-backend", "file",
            ],
        )
​
    @classmethod
    def tearDownClass(cls):
        # 先发 SIGTERM 让服务器在用户态注销钉住的主机 KV 池,
        # 超时后再 SIGKILL;裸 kill 会让内核在页回收时才解钉,
        # 显存迟迟不释放,会拖垮下一个测试用例。
        terminate_and_kill_process_tree(cls.process)
        time.sleep(5) # 给驱动与内存回收留出余量
test/registered/disaggregation/test_disaggregation_decode_offload.py test-coverage

PD 解耦卸载测试中三个进程(prefill/decode/load balancer)的 kill+wait 被 helper 替代,删除显式 wait,是行为变化最明显的一处。

def test_mmlu_double_eval(self):
    """两轮 MMLU:先 offload 到磁盘,重启节点后再加载,验证分数一致。"""
    args = SimpleNamespace(
        base_url=f"http://{self.base_host}:{self.lb_port}",
        model=self.model,
        eval_name="mmlu",
        num_examples=256,
        num_threads=32,
    )
    metrics1 = run_eval(args)
    time.sleep(10) # 确保所有 offload 都提交到磁盘
​
    # 依次优雅关闭 prefill / decode / load balancer 三个进程。
    # 相比裸 kill_process_tree:先 SIGTERM 让服务器完成 KV 池注销,
    # 超时后再 SIGKILL;helper 内部负责等待退出,无需显式 wait()。
    terminate_and_kill_process_tree(self.process_prefill)
    terminate_and_kill_process_tree(self.process_decode)
    terminate_and_kill_process_tree(self.process_lb)
​
    self.start_prefill()
    self.start_decode()
    self.launch_lb()
    self.wait_server_ready(self.prefill_url + "/health")
    self.wait_server_ready(self.decode_url + "/health")
    metrics2 = run_eval(args)

评论区精华

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

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

风险与影响

风险面仅限于测试基础设施,无产品代码变更。关键点如下:

  • terminate_and_kill_process_tree 依赖服务器对 SIGTERM 的响应;若服务器无法在内部超时内退出,helper 会回退到 SIGKILL,行为与旧的裸 kill 相同,因此最坏情况不会比之前更差。
  • test_disaggregation_decode_offload.py 删除了显式 wait(),改为依赖 helper 内部等待,语义等价,但未来若修改 helper 的等待行为,需回归该测试。
  • 涉及 13 个文件、多平台(AMD、NPU、Blackwell B200/B300、H100),但全部是同一模式的机械替换,批量替换引入不一致的风险较低。
  • 改动集中在 tearDownClass/_stop_server 等收尾路径,不影响任何测试断言逻辑。

对用户无影响;对 CI 稳定性有明显正面影响。HiCache 测试常启用大容量的钉住主机内存池(如 --hicache-size 100/200、file 后端、direct I/O),连续跑多个用例时,裸 SIGKILL 留下的内核解钉延迟会占用 GPU 显存并导致后续用例失败。改为优雅关闭后,服务器能在用户态完成 KV 池注销,显存及时释放,预计可减少 HiCache/PD 测试队列的偶发失败。对测试维护者而言,本 PR 确立了统一的进程清理惯用法,后续新测试可直接复用 terminate_and_kill_process_tree

测试基础设施变更 多文件机械替换 依赖信号处理助手 无源码主路径变更

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论