Prhub

#37249 fix(gateway): bump wfaas to 1.0.2 so ContinueNextStep unblocks dependents

原始 PR 作者 Juhyun-Kim-Memphis 合并时间 2026-09-01 02:30 文件变更 1 提交数 1 评论 1 代码增减 +1 / -1

执行摘要

wfaas 钉版升至 1.0.2,修复 ContinueNextStep 依赖死锁

PR body 指出 sgl-model-gateway 将 wfaas 钉在 =1.0.0,而 wfaas 1.0.0/1.0.1 存在调度 bug:ContinueNextStep 失败的步骤被 tracker 记为 skipped,但发给调度器的信号是 StepResult::Failure,调度器只在 Success|Skip 时入队下游步骤,最终走进死锁分支把整个 workflow 置为 Failed。作者在生产环境 fork 中遇到“rolled pod 从未被拾取,路由器一直服务陈旧 worker 集合直到重启”。根因是关联 Issue #19224 为 CI 稳定性把 11 个内部 crate 全部改钉 =1.0.0,恰好发生在 wfaas 1.0.2 发布后一天,把上游修复挡在门外;作者原话称“那是 ContinueNextStep 声明在这个步骤上要达成的效果的反面”。

值得精读,但重点不在代码而在方法:PR body 对 wfaas 调度器 bug 的根因追踪(tracker 记录与调度器信号两个状态源的错位、workflow 形状分析、生产影响还原)是教科书级的依赖 bug 分析;审核人的独立验证和“触发条件比描述更窄”的纠正也展示了良好的协作模式。可关注的设计决策包括:在精确钉版纪律内选择最小修复版本、用判别性测试矩阵双向验证 pin 版本、以及如何为钉版依赖建立定期回访机制(如 Dependabot 或专门巡检)。建议后续跟进把判别测试入库,并评估 1.1.0 与 #32322 的落地。

讨论亮点

审核人 ShangmingCai 给了两次 APPROVED,第二次附带了独立验证笔记,核心交锋如下:

  • 根因独立核验:审核人在 vendored 的 wfaas-1.0.0/src/engine.rs 中确认,失败信号在 ContinueNextStep 分支把步骤映射为 skipped 之前就已按 result 计算为 StepResult::Failure;调度器只在 Success | Skip 时入队依赖步骤,随后走进死锁分支置 WorkflowStatus::Failedcreate_local_worker_workflow()steps/worker/local/mod.rs:120)正是该形状,discover_metadata 失败后 register_workers 不可达。
  • 触发条件修正:PR 描述称“新启动 worker 的 /get_server_info 慢到 10s 超时即可触发”,审核人指出 HTTP 路径中 DiscoverMetadataStep.ok()unwrap_or_else 吞掉了 HTTP/gRPC fetch 错误、返回 Ok(Success) 空标签,普通失败/拒绝的 /server_info 并不会触发;真正触发的是三轮 10s 步超时全部耗尽(冷启动 pod 的挂起连接)或 connection_mode 上下文错误——真实但不罕见,只是比描述更窄。
  • 行为变化面:审核人顺带检查了唯一的行为变化面 create_mcp_registration_workflow,它也是一串 ContinueNextStep 步骤,bump 后其下游步骤的调度行为会从“永不执行”变为“按声明执行”(该段评论在材料中截断,完整结论未呈现)。

实现拆解

  1. 变更入口:唯一改动是 sgl-model-gateway/Cargo.toml 第 87 行的 wfaas 依赖,从 =1.0.0 改为 =1.0.2,保持 #19224 引入的精确钉版纪律不变;没有修改任何 Rust 源码、测试或 CI 配置,仓库也没有提交 Cargo.lock 需要同步。
  2. 版本选择依据:1.0.2 是携带修复的最小版本。作者逐一列出 1.0.0→1.0.2 的公开 delta:src/engine.rs 包含核心修复(ContinueNextStep 分支改为发送 (StepResult::Skip, true))、pending_check 索引去重(避免 depends_on_any 步骤在多个依赖完成时重复启动)以及若干 clippy 属性与内联 format 参数;src/event.rs 仅 clippy 属性;其余为版本号和 README(1.0.1 加入)。没有任何公开 API 变化。1.1.0 是当前最新版本但 delta 更大,作者选择最小修复集。
  3. 验证方式:作者写了三个判别测试,针对未修改的 wfaas 依赖、仅变动 pin 版本运行:上游自带控制用例(无依赖步骤在 ContinueNextStep 后仍运行)、扩展后带 dependent 的判别用例、以及 gateway AddWorker 形状(detect_connection_mode → discover_metadata(ContinueNextStep) → create_worker → register_workers)的用例。结果矩阵显示 1.0.0/1.0.1 在两个判别用例上 FAIL、1.0.2/1.1.0 通过,双向判别有效;cargo check --all-targets 在工具链 1.90 下通过。
  4. 测试与配套:判别测试未入库,作者明确表示可按需补到 sgl-model-gateway/tests/;本变更不涉及模型推理路径,accuracy 与 speed 测试均不适用。
文件 模块 状态 重要度
sgl-model-gateway/Cargo.toml 网关依赖 modified 3.68

关键源码片段

sgl-model-gateway/Cargo.toml configuration

唯一变更文件:将 wfaas 精确钉版从 =1.0.0 提升到 =1.0.2,解锁上游 ContinueNextStep 调度死锁修复,同时保持 #19224 引入的精确钉版纪律。

# ===== sgl-model-gateway/Cargo.toml · 内部 crate 精确钉版段(节选) =====# 内部 crate 一律使用「精确钉版 =」而非「兼容范围 ~」:
# 这是 #19224 的决定,因为 Cargo.lock 被 gitignore,CI 每次全新解析,
# 宽松范围会拉到带破坏性 API 变化的 patch 版本(如 smg-wasm 的
# WasmModuleManager::new() 签名变更),导致 Docker CI 编译失败。reasoning-parser = "=1.0.0"
openai-protocol = { version = "=1.0.0", features = ["axum"] }
tool-parser = "=1.0.0"
llm-tokenizer = "=1.3.2"
smg-auth = "=1.0.0"# wfaas:1.0.0 是 #19224 当时钉住的版本,但 1.0.0/1.0.1 存在调度死锁——
# 失败动作声明为 ContinueNextStep 的步骤,tracker 记为 skipped,
# 发往调度器的信号却是 StepResult::Failure,导致 depends_on 它的步骤
# 永不入队,最终整条 AddWorker 工作流被死锁分支打成 Failed,
# worker 永远无法注册。
# 1.0.2 (本 PR 目标版本)把该分支改为发送 (StepResult::Skip, true),
# 依赖步骤得以被调度;该升级无任何公开 API 变化,兼容风险极低。wfaas = "=1.0.2"data-connector = "=1.0.0"
smg-mcp = "=1.0.0"
smg-wasm = "=1.0.0"
smg-mesh = "=1.0.0"

评论区精华

ContinueNextStep 死锁根因的独立源码核验 正确性

ShangmingCai 在 vendored 的 wfaas-1.0.0/src/engine.rs 中确认:失败信号在 ContinueNextStep 分支把步骤记为 skipped 之前就已按 result 计算为 StepResult::Failure,调度器只在 Success|Skip 时入队依赖步骤,随后走进死锁分支置 WorkflowStatus::Failed;并确认 create_local_worker_workflow(steps/worker/local/mod.rs:120)正是该形状,discover_metadata 失败后 register_workers 不可达。

结论:认可 PR 的根因判断,1.0.2 的修复((StepResult::Skip, true))确实解决问题,bump 方向正确。 · 已解决

触发条件比 PR 描述更窄:HTTP fetch 错误其实被吞掉 正确性

审核人指出 HTTP 路径中 DiscoverMetadataStep 用 .ok() 加 unwrap_or_else 吞掉 HTTP/gRPC fetch 错误并返回 Ok(Success) 空标签,普通失败 / 拒绝的 /server_info 不会触发;真正触发的是三轮 10s 步超时全部耗尽(冷启动 pod 的挂起连接)或 connection_mode 上下文错误。

结论:故障真实但不罕见,比“一次抖动 HTTP 调用”更窄;不影响 bump 正确性,仅修正描述框架。 · 已解决

bump 的行为变化面:create_mcp_registration_workflow 设计

审核人顺带检查了唯一的行为变化面:create_mcp_registration_workflow 是一串 ContinueNextStep 步骤,其下游在旧版本同样可能被卡住,bump 后会开始按声明调度。该段评论在提供的材料中截断,完整结论未呈现。

结论:未完整呈现,作为后续观察项;建议升级后对该 workflow 做一次端到端验证。 · 待处理

风险与影响

兼容性风险低:审核人验证 1.0.0→1.0.2 的依赖要求字节级一致,[lints] workspace = true 这类 lint 配置对 registry 依赖编译没有影响。行为变化面主要有三点:一是 1.0.2 对 pending_check 索引的去重会减少 depends_on_any 步骤的重复启动次数,若 gateway 中有步骤隐式依赖旧的重复启动行为需要观察;二是 create_mcp_registration_workflow 这条 ContinueNextStep 链的下游步骤将开始真正被调度,属于正向解锁但未充分验证;三是本 PR 暴露的工程风险——精确钉版 + 被 gitignore 的 Cargo.lock + 无依赖回访机制,会让上游 patch 级修复被静默挡住一整年,1.1.0 是否值得跟进也尚未评估。另外,作者编制的判别性回归测试没有入库,后续再次升级或回退 wfaas 时没有自动防线。

影响范围集中在 sgl-model-gateway 的 worker 注册路径:AddWorker workflow 不再因可选的 discover_metadata 超时整体失败,滚动发布或冷启动的 worker 能被可靠注册,直接消除生产环境服务陈旧 worker 集合的问题;create_mcp_registration_workflow 的行为也会随之解锁。对 SGLang 推理内核、调度器、模型精度均无影响。对团队而言,该案例提醒维护者:精确钉版策略需要配套依赖回访或自动化升级机制,否则上游修复会被长期阻断;#32322 落地后,恢复机制会更完整。总体影响程度中等,但直接命中一个真实生产故障。

精确钉版阻隔上游补丁修复 周边 workflow 行为变化未评估 无 in-tree 回归测试

关联 Issue

#19224 fix(gateway): pin internal crate dependencies to exact versions

完整报告

参与讨论