执行摘要
- 一句话:回退多 tokenizer worker 下 pause/continue 修复,恢复旧 bug 状态
- 推荐动作:值得关注此 PR,因为它反映了 CI 门禁和绕过权限的治理问题。后续应跟踪 #24445 的修正版重新提交,并补充分布式场景的集成测试。建议阅读 #24445 的设计讨论了解原始方案的权衡。
功能与动机
PR body 明确说明:"It failed the first CI https://github.com/sgl-project/sglang/actions/runs/25408313988/job/74524211822?pr=24458#step:7:
- I will revoke @ByronHsu 's bypass permission for 2 days"。原始修复绕过 CI 合并后导致流水线失败,因此回退修复以确保主线可用。
实现拆解
- 移除数据结构:在
python/sglang/srt/managers/io_struct.py 中删除了 TokenizerWorkerRegisterReq 和 PauseContinueBroadcast 两个 dataclass,这两个类原本用于 worker 注册和广播状态。
- 删除导入与广播逻辑:在
python/sglang/srt/managers/multi_tokenizer_mixin.py 中,移除了对上述 dataclass 的导入,并完全删除 router_worker_obj 中的 worker 注册收集和 pause/continue 广播分发代码。
- 恢复简单路由:
MultiTokenizerRouter 的文档字符串从“在 tokenizer manager 与 scheduler/detokenizer 之间路由,并广播 pause/continue”恢复为“A router to receive requests from TokenizerWorker”,其 router_worker_obj 不再拦截 PauseGenerationReqInput/ContinueGenerationReqInput 进行广播,而直接转发到 scheduler,回到原始的单 worker 行为。
- 保留同步处理 loop:
handle_loop 后续逻辑保持不变,因为该部分不涉及 pause/continue 广播。
关键文件:
python/sglang/srt/managers/multi_tokenizer_mixin.py(模块 请求路由;类别 source;类型 core-logic;符号 pause_generation, continue_generation, _handle_pause_continue_broadcast, _apply_pause_continue_broadcast): 核心变更:移除广播逻辑和 worker 注册,恢复简单路由。该文件是暂停/继续功能在多 tokenizer worker 下的分发入口,回退直接影响了所有依赖此功能的用户。
python/sglang/srt/managers/io_struct.py(模块 数据结构;类别 source;类型 core-logic;符号 TokenizerWorkerRegisterReq, PauseContinueBroadcast): 移除 TokenizerWorkerRegisterReq 和 PauseContinueBroadcast 两个 dataclass,它们是广播方案的核心数据结构,删除后路由不再具备广播能力。
关键符号:pause_generation, continue_generation, _handle_pause_continue_broadcast, _apply_pause_continue_broadcast, TokenizerWorkerRegisterReq, PauseContinueBroadcast
关键源码片段
python/sglang/srt/managers/multi_tokenizer_mixin.py
核心变更:移除广播逻辑和 worker 注册,恢复简单路由。该文件是暂停/继续功能在多 tokenizer worker 下的分发入口,回退直接影响了所有依赖此功能的用户。
# python/sglang/srt/managers/multi_tokenizer_mixin.py
# === 恢复后的 MultiTokenizerRouter(移除了 pause/continue 广播)===
class MultiTokenizerRouter:
"""A router to receive requests from TokenizerWorker"""
def __init__(
self,
server_args: ServerArgs,
port_args: PortArgs,
):
self.server_args = server_args
context = zmq.asyncio.Context(3)
self.recv_from_detokenizer = get_zmq_socket(
context, zmq.PULL, port_args.tokenizer_ipc_name, True
)
self.send_to_scheduler = get_zmq_socket(
context, zmq.PUSH, port_args.scheduler_input_ipc_name, True
)
self.receive_from_worker = get_zmq_socket(
context, zmq.PULL, port_args.tokenizer_worker_ipc_name, True
)
self._loop = asyncio.new_event_loop()
self._thread = threading.Thread(target=self._run_loop, daemon=True)
self._thread.start()
self._task = asyncio.run_coroutine_threadsafe(
self.router_worker_obj(), self._loop
)
# Start handle_loop simultaneously
self._handle_task = asyncio.run_coroutine_threadsafe(
print_exception_wrapper(self.handle_loop), self._loop
)
self.disaggregation_bootstrap_server = start_disagg_service(self.server_args)
def _run_loop(self):
self._loop.run_forever()
async def router_worker_obj(self):
"""Forward path: workers → scheduler, no pause/continue broadcast."""
while True:
recv_obj = await self.receive_from_worker.recv_pyobj()
# 不再检查 TokenizerWorkerRegisterReq / PauseGenerationReqInput / ContinueGenerationReqInput
await self.send_to_scheduler.send_pyobj(recv_obj)
(代码中已移除广播逻辑,所有请求直接转发到 scheduler)
评论区精华
PR body 中作者表示将根据 MAINTAINER.md 撤销 @ByronHsu 绕过权限两天。@ByronHsu 在关联 issue 中道歉:"Sorry :(. I was too rush to unblock our RL case. Will be careful next time!" 核心讨论点在于绕过 CI 合并的风险,以及 PR 质量门禁的重要性。
- CI 失败与绕过权限 (other): 接受回退合并,并执行权限撤销措施,确保将来绕过 CI 需要更谨慎。
风险与影响
- 风险:回退后,
--tokenizer-worker-num > 1 场景下 /pause_generation 和 /continue_generation 再次无法正确同步所有 worker,导致请求挂起(复现 #21235)。在 PD 分离部署中影响更广(15/64 请求可能挂起)。虽然回退让主线通过 CI,但功能回归是已知风险。
- 影响:影响所有使用多 tokenizer worker 并依赖 pause/continue 的用户(如 RL 训练场景)。这些用户的请求在 pause 后可能永久挂起,必须重启服务恢复。团队需尽快发布修正版本并增加 CI 覆盖。
- 风险标记:核心路径变更, 回归风险, 缺少测试覆盖
关联脉络
- PR #24445 [fix] /pause_generation and /continue_generation wrong for --tokenizer-worker-num > 1: 被回退的原始修复 PR,其合并导致了 CI 失败,进而触发本回退。
- PR #21235 原始 pause/continue 多 worker 挂起 issue: 该 PR 试图修复 #21235,但回退后该问题再次打开。
参与讨论