Prhub

#33460 [CI] Build the Rust extensions on the 5090 pool and seed the cache from main

原始 PR 作者 hnyls2002 合并时间 2026-08-04 14:33 文件变更 7 提交数 6 评论 2 代码增减 +72 / -6

执行摘要

Rust 扩展构建迁移至 5090 池,新增 main 缓存种子机制

PR body 说明了问题根源:"A cache entry only reaches every PR when it was written from the default branch's ref, and pr-test.yml has no push trigger - so before this, a merge that moved the key left every PR compiling for itself until the next scheduled run, up to 12h later"。即:只有 default branch 写入的缓存条目才能覆盖所有 PR,而 pr-test.yml 没有 push 触发器,导致缓存键变化后每个 PR 都要等到下一次计划运行才能恢复命中。另一个动机是把构建节点切到 5090 池,与测试阶段保持同一镜像与 ABI 标签。

值得精读。重点看 actions/cache 的 key/压缩工具版本语义、跨 job 共享 CARGO_TARGET_DIR 与磁盘保护、以及 seed 工作流如何用 concurrency 和最小权限控制成本。对维护大型 CI 流水线的团队,这是一个"缓存生命周期管理"的典型案例。

讨论亮点

本 PR 没有人工 review 评论,review_comments 为 0。仅有的两条 issue 评论分别是 Gemini Code Assist bot 的下线声明和作者的 /tag-and-rerun-ci 触发命令,无实质技术讨论。设计权衡全部记录在 PR body 中:缓存必须由 default branch 写入、artifact_name 按调用方加后缀而 cache_key_prefix 共享、zstd 版本差异会导致缓存不可读等。

实现拆解

  1. 切换构建节点:在 .github/workflows/pr-test.yml 与 pr-test-extra.yml 中,将 Rust 扩展构建 job 的 runs_on 从 x64-kernel-build-node 改为 1-gpu-5090。pr-test-extra.yml 的 artifact_name 使用 rust-ext-x86_64-extra 后缀,避免两个调用方的产物名冲突,而 cache_key_prefix 保持共享,让两次运行复用同一份构建。

  2. 磁盘压力保护:在 _pr-test-rust-ext-build.yml 中,为 CARGO_TARGET_DIR 增加 85% 使用率检查。因为 5090 池的 runner 容器只编译不清理,CARGO_TARGET_DIR 无人修剪,直接复用 ci_install_dependency.sh 中已有的保护逻辑:超过阈值就删除重建目录。

  3. 新增缓存种子工作流:新增 seed-rust-ext-cache.yml,在 main 分支推送且变更路径命中 rust/** 或 python/setup.py 时触发(与缓存键哈希的路径一致),复用 _pr-test-rust-ext-build.yml 执行构建并保存缓存。concurrency 设为 cancel-in-progress: true,只保留最新合并的 seed 任务;permissions 声明 contents: read、issues: read,最小化权限。

  4. zstd 兼容性保障:由于 actions/cache 的条目由 key 与压缩工具版本共同标识,缺少 zstd 的 runner 保存的缓存无法被恢复任务读取。构建 job 新增"确保 zstd"步骤,在非 root 时通过 sudo 安装,失败只打 warning 不阻断(冷构建仍可工作)。

  5. 模块加载断言:在 scripts/ci/cuda/ci_install_dependency.sh 中,用 importlib.import_module 依次导入 sglang.srt.server._core、sglang.srt.grpc._core、sglang.srt.multimodal._core。注释说明不用 find_spec 的原因:finder 只定位而不 dlopen,无法加载的 .so 会通过检查、直到某个测试套件运行时才崩溃。

  6. 清理冗余属性:在 rust/sglang-server/src/utils/regex.rs 删除两个 #[allow(dead_code)](其中一个在 pattern() getter 上重复标注),在 rust/sglang-server/src/fsm.rs 删除 RequestState 枚举上的一个。作者用"把所有 allow 换成 expect"的方式验证,剩余八个 allow 仍承担作用,且其中一个只在 --lib 构建中满足,单查 --all-targets 会误删。

文件 模块 状态 重要度
.github/workflows/seed-rust-ext-cache.yml 缓存种子 added 4.65
.github/workflows/_pr-test-rust-ext-build.yml 构建工作流 modified 4.49
scripts/ci/cuda/ci_install_dependency.sh 安装脚本 modified 4.38
rust/sglang-server/src/utils/regex.rs 正则模块 modified 4.08
rust/sglang-server/src/fsm.rs 状态机 modified 3.86
.github/workflows/pr-test.yml 主流水线 modified 2.95
.github/workflows/pr-test-extra.yml 扩展流水线 modified 2.95

关键源码片段

.github/workflows/_pr-test-rust-ext-build.yml infrastructure

构建 job 的关键改动都在这里:磁盘压力保护、zstd 检查、构建产物验证。

# 与 ci_install_dependency.sh 共用同一条缓存路径,
# 在同时跑测试阶段的 runner 上保持一份热缓存。
export CARGO_TARGET_DIR="${HOME}/.cache/sglang-cargo-target"
mkdir -p "${CARGO_TARGET_DIR}"# 5090 池的 runner 容器只编译不清理,CARGO_TARGET_DIR 无人修剪,
# 因此重复 85% 磁盘压力保护,防止磁盘写满。
used="$(df --output=pcent "${CARGO_TARGET_DIR}" 2>/dev/null | tr -dc '0-9')"
if [ "${used:-0}" -ge 85 ]; then
  echo "cargo target dir filesystem at ${used}%; dropping ${CARGO_TARGET_DIR}"
  rm -rf "${CARGO_TARGET_DIR}"
  mkdir -p "${CARGO_TARGET_DIR}"
fi
scripts/ci/cuda/ci_install_dependency.sh infrastructure

增加模块加载断言,确保 Rust 扩展 .so 能实际被 import。

# 用 importlib 导入,而非 find_spec。
# finder 只定位扩展而不 dlopen,无法加载的 .so 会通过 find_spec,
# 直到某个测试套件运行时才崩溃。
import importlibfor mod in ("server", "grpc", "multimodal"):
    name = f"sglang.srt.{mod}._core"
    try:
        importlib.import_module(name)
    except Exception as exc:
        raise SystemExit(f"{name} is present but does not load: {exc!r}")
    print(f"{name} loads")

评论区精华

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

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

风险与影响

风险:

  • 缓存污染:若某个 merge 在验证前就把坏产物写入缓存,后续 PR 会静默复用。缓解:保存步骤在 verify 之后执行,该 PR 自己也验证了 glibc <= 2.34 与编译成功。
  • zstd 缺失:安装步骤失败只打 warning,缓存保存仍继续,但恢复任务读不到条目,所有 PR 退回冷编译。
  • 5090 池可用性:1-gpu-5090 同时服务 sgl-kernel 与 docker 构建,队列繁忙时 Rust 扩展构建可能排队;本 PR 实测排队 2 秒。
  • 加载断言误报:若 _core 扩展在无 GPU 环境导入失败,会把安装阶段误判为失败;当前断言只 import 扩展本体,不依赖设备上下文,风险较低。

对开发者:PR 的 CI 从"缓存键变化后冷编译 12 小时"收敛为缓存命中后的秒级恢复,反馈循环显著缩短。对 CI 系统:构建节点与测试阶段共用同一镜像,减少环境漂移;新增的 seed 工作流会消耗 main 分支推送后的额外 GPU runner 分钟数。对代码质量:模块加载断言为构建产物增加了一道"能真正加载"的健康检查。

缓存键变化触发冷编译 zstd 缺失导致缓存不可恢复 加载断言可能误报 5090 池排队延迟

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论