Prhub

#39600 [CI] Fix SWA decode radix cache NIXL import

原始 PR 作者 nvpohanh 合并时间 2026-09-15 21:01 文件变更 1 提交数 1 评论 3 代码增减 +8 / -1

执行摘要

SWA 解码 radix 测试本地定义 NIXL 探测,修复导入崩溃

PR body 明确给出因果链:"define the NIXL availability helper locally in the SWA decode radix cache test — remove the stale cross-file import left after #39544 removed the base NIXL test variant",并进一步说明 "PR #32196 exposed the regression in Extra CI: test_disaggregation_decode_radix_cache_swa.py failed during import because _has_nixl no longer exists in test_disaggregation_decode_radix_cache.py"。即:前序测试裁剪 PR 删除了共享的 NIXL 可用性探测函数,SWA 变体测试仍按旧路径导入,造成模块级 ImportError,整个 SWA decode radix cache 用例无法被收集执行。修复目标是最小成本恢复 CI 绿并保住 SWA + decode 侧 radix cache 的覆盖。

建议只需扫读,无需精读:改动为 8 行级的导入修复,没有值得深入学习的设计决策。阅读时值得停留的两点:一是「共享测试文件被裁剪时,变体测试的隐式依赖会连带断裂」这一测试重构反模式,做测试套件裁剪或重构时应先搜索 _has_nixl 这类私有符号的跨文件引用;二是 is_in_ci() or _has_nixl() 的短路结构导致新代码在 CI 中不可达,属于既有设计,但评估后续测试改动时需要意识到。若后续再出现同类断裂,建议把 NIXL 可用性探测上提到 sglang/test/test_utils.py 统一维护。

讨论亮点

本 PR 的 review 讨论非常轻量:mmangkad 给出 APPROVED 且 body 为空,未留下任何行内 review 评论;实质讨论发生在 Issue 评论区。作者 nvpohanh 先以 /rerun-tests test/registered/disaggregation/test_disaggregation_decode_radix_cache_swa.py 定向重跑,github-actions[bot] 回报 🚀 8-gpu-h200 (1 test): ✅,命令为 cd test/ && python3 registered/disaggregation/test_disaggregation_decode_radix_cache_swa.py,证明导入错误已消除;随后作者 @hnyls2002 请求 review,最终由 ShangmingCai 合并。可提炼的三个要点:一是「用目标用例实跑替代全量 CI 等待」在本例中是最有效的验收手段;二是 reviewer 未对「本地复制 _has_nixl 而非抽到公共测试工具」提出异议,该设计权衡实际上未被讨论过(见风险分析);三是 PR body 顶部的 CI States 快照显示 Latest PR Test / Extra / AMD ROCm 10 三个 run 均为 ❌,但 bot 的定向 rerun 已通过,二者是否同一失败原因在材料中无法确认。

实现拆解

  1. 根因定位(test/registered/disaggregation/test_disaggregation_decode_radix_cache_swa.py):原文件顶部 from test_disaggregation_decode_radix_cache import (DisaggregationDecodeRadixCacheTestMixin, _has_nixl,),_has_nixl 由 PR#39544 随基文件的 NIXL 变体一起删除,导致 import 阶段即抛 ImportError,未进入任何测试逻辑;这是纯粹的名字解析问题,而非 SWA 逻辑缺陷。
  2. 在测试文件内自持探测能力:新增模块级函数 _has_nixl(),内部 import nixl._api 并以 try/except ImportError 返回布尔值,语义与基文件被删版本完全一致,使 SWA 测试不再依赖其他测试文件的私有符号。
  3. 收缩跨文件导入面:从 import 列表中只保留 DisaggregationDecodeRadixCacheTestMixin,把跨文件耦合收窄到「必须复用的 mixin」这一条显式契约上,降低后续再次被套件裁剪误伤的概率。
  4. 保持跳过语义与配置不变:@unittest.skipUnless(is_in_ci() or _has_nixl(), ...) 未改,由于 is_in_ci() 在 CI 中先短路,_has_nixl() 实际只在本地执行;register_cuda_ci(est_time=365, stage="extra-b", runner_config="8-gpu-h200")、SWA_SERVER_ARGS(--page-size 64 --attention-backend triton)、SGLANG_ENABLE_UNIFIED_RADIX_TREE=1 的 prefill/decode 环境变量、gsm8k_min_score = 0.45 等测试契约均保持原样,确保修复不改变覆盖范围。
  5. 验证配套:PR body 给出 pre-commit run --all-files --show-diff-on-failure、python3 -m py_compile test/registered/disaggregation/test_disaggregation_decode_radix_cache_swa.py、git diff --check 三项静态校验;作者随后用 /rerun-tests test/registered/disaggregation/test_disaggregation_decode_radix_cache_swa.py 在 8-gpu-h200 上实跑该文件,bot 回报 1 个测试通过,形成「静态检查 + 目标用例实跑」的闭环。
文件 模块 状态 重要度
test/registered/disaggregation/test_disaggregation_decode_radix_cache_swa.py 拆分测试 modified 4.64

关键符号

_has_nixl

关键源码片段

test/registered/disaggregation/test_disaggregation_decode_radix_cache_swa.py test-coverage

唯一改动文件:移除对基测试文件私有符号 `_has_nixl` 的跨文件导入,并在本文件内本地重定义该探测函数,修复 Extra CI 中模块导入即崩溃的问题;同时保持 SWA(`--page-size 64 --attention-backend triton`)与 unified radix tree 环境变量等测试契约不变。

# test/registered/disaggregation/test_disaggregation_decode_radix_cache_swa.py
"""SWA coverage for decode-side radix cache on gpt-oss-20b."""
​
import unittest
​
# 只保留 mixin 这一条跨文件导入:_has_nixl 不再从基测试文件借,
# 因为 PR#39544 裁剪 8-gpu-h20 套件时删除了基文件里的 NIXL 变体,
# 连带删掉该辅助函数,导致本文件在 import 阶段就抛 ImportError。
from test_disaggregation_decode_radix_cache import (
    DisaggregationDecodeRadixCacheTestMixin,
)
​
from sglang.test.ci.ci_register import register_cuda_ci
from sglang.test.server_fixtures.disaggregation_fixture import (
    PDDisaggregationServerBase,
)
from sglang.test.test_utils import DEFAULT_MODEL_NAME_FOR_TEST_MXFP4_WITH_MOE, is_in_ci
​
register_cuda_ci(est_time=365, stage="extra-b", runner_config="8-gpu-h200")
​
SWA_SERVER_ARGS = ["--page-size", "64", "--attention-backend", "triton"]
​
​
def _has_nixl():
    # 本地自持 NIXL 可用性探测,语义与被基文件删除的版本保持一致,
    # 避免再依赖其他测试文件的私有符号(那种依赖会被套件裁剪无声破坏)。
    try:
        import nixl._api # noqa: F401
    except ImportError:
        return False
    return True
​
​
# 注意短路顺序:CI 中 is_in_ci() 为真,_has_nixl() 不会被真正执行,
# 因此该函数体实际只在本地开发机跑;本地无 NIXL 时整类测试跳过。
@unittest.skipUnless(
    is_in_ci() or _has_nixl(),
    "NIXL is required for decode radix cache disaggregation coverage.",
)
class TestDisaggregationDecodeRadixCacheSWANixl(
    DisaggregationDecodeRadixCacheTestMixin, PDDisaggregationServerBase
):
    transfer_backend_name = "nixl"
    model_name = DEFAULT_MODEL_NAME_FOR_TEST_MXFP4_WITH_MOE
    gsm8k_min_score = 0.45
    # SWA + decode 侧 radix cache 被约束在 unified radix tree 路径上,
    # prefill 与 decode 两侧都要显式打开该开关。
    extra_prefill_env = {"SGLANG_ENABLE_UNIFIED_RADIX_TREE": "1"}
    extra_decode_env = {"SGLANG_ENABLE_UNIFIED_RADIX_TREE": "1"}
    extra_prefill_args = SWA_SERVER_ARGS
    extra_decode_args = [
        "--disaggregation-decode-enable-radix-cache",
        *SWA_SERVER_ARGS,
    ]
​
​
if __name__ == "__main__":
    unittest.main()

评论区精华

跨测试文件私有符号导入导致 CI 模块级 ImportError 正确性

PR body 指出:`_has_nixl` 在 PR#39544 删除基 NIXL 变体时被一并移除,`test_disaggregation_decode_radix_cache_swa.py` 仍从 `test_disaggregation_decode_radix_cache` 导入该符号,导致 Extra CI 在 import 阶段失败(附失败 job 链接)。

结论:在 SWA 测试文件内本地定义 `_has_nixl()`,并只保留 `DisaggregationDecodeRadixCacheTestMixin` 的跨文件导入,解除对基文件私有符号的依赖。 · 已解决

用定向 rerun 验证目标用例 测试

作者 `nvpohanh` 执行 `/rerun-tests test/registered/disaggregation/test_disaggregation_decode_radix_cache_swa.py`,`github-actions[bot]` 回报 8-gpu-h200 上 1 个测试通过(✅),命令为 `cd test/ && python3 registered/disaggregation/test_disaggregation_decode_radix_cache_swa.py`。

结论:目标文件已能被正常导入并执行,导入崩溃确认消除;相比等待全量 CI,定向重跑是更快的验收手段。 · 已解决

轻量 review:单次批准、无行内意见 other

作者 `@hnyls2002` 请求 review 后,`mmangkad` 给出 APPROVED 且未留下任何文字或行内评论;随后由 `ShangmingCai` 合并。

结论:因改动面极小且属于 CI 修复,review 以快速放行为主,「复制 `_has_nixl` 还是抽公共工具」这一设计取舍实际上未被讨论。 · 已解决

风险与影响

  1. 测试间私有符号耦合的隐患未根治(test/registered/disaggregation/test_disaggregation_decode_radix_cache_swa.py):修复方式是把 _has_nixl 复制到 SWA 文件本地,而非抽到 sglang/test/test_utils.py 之类的公共位置;一旦 NIXL 探测逻辑需要演进(比如区分 nixl._api 与新版模块名、或需要版本下限判断),各测试文件的多份副本会出现语义漂移。
  2. is_in_ci() 短路使新代码在 CI 中实际未被覆盖:is_in_ci() or _has_nixl() 中前者为真即短路,因此 CI 只验证了模块能被导入,_has_nixl() 的函数体只在本地开发机执行;若未来该函数内部写错,CI 不会给出信号。
  3. 覆盖收缩风险:该文件是 SWA + decode 侧 radix cache 在 gpt-oss-20b(DEFAULT_MODEL_NAME_FOR_TEST_MXFP4_WITH_MOE)上的主要覆盖,注册在 extra-b / 8-gpu-h200,属于非必跑 stage;修复虽恢复了可执行性,但若该 stage 长期被跳过,SWA decode radix cache 的回归保护仍然薄弱,且 gsm8k_min_score = 0.45 的阈值本就依赖两遍非回归对比来兜底。
  4. 无 runtime 风险:改动不涉及 python/sglang 下的任何源码、配置或数据契约,对推理正确性、性能、显存无直接影响;唯一的外部性来自 CI 资源占用(该用例 est_time 365s,跑在 8 卡 H200)。

对用户与线上服务:零影响,纯测试文件改动。对 CI 与团队:恢复了 test/registered/disaggregation/test_disaggregation_decode_radix_cache_swa.py 的模块导入,使 Extra CI 中该文件能重新被收集执行,避免一个持续报 ImportError 的用例长期占据失败位并干扰其他 PR 的 CI 判读;由于 Extra CI 是所有 PR 共享的流水线,此类失败会让作者不断排查与他人改动无关的红灯,修复直接改善开发者体验与提效。对后续维护者:本 PR 明确了「测试文件之间的私有符号导入是脆弱的」这一信号,但未建立统一约定,后续新增变体测试时仍需自觉避免复用跨文件私有函数。

测试间私有符号耦合 CI 与本地执行路径不一致(is_in_ci 短路) 无源码主路径变更 SWA 覆盖仅依赖 extra-b 单一套件

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论