# PR #24461 完整报告

- 仓库：`sgl-project/sglang`
- 标题：Revert "[fix] /pause_generation and /continue_generation wrong for --tokenizer-worker-num > 1"
- 合并时间：2026-05-06 07:44
- 原文链接：http://prhub.com.cn/sgl-project/sglang/pull/24461

---

# 执行摘要

- 一句话：回退多 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:
70. I will revoke @ByronHsu 's bypass permission for 2 days"。原始修复绕过 CI 合并后导致流水线失败，因此回退修复以确保主线可用。

# 实现拆解

1. **移除数据结构**：在 `python/sglang/srt/managers/io_struct.py` 中删除了 `TokenizerWorkerRegisterReq` 和 `PauseContinueBroadcast` 两个 dataclass，这两个类原本用于 worker 注册和广播状态。
2. **删除导入与广播逻辑**：在 `python/sglang/srt/managers/multi_tokenizer_mixin.py` 中，移除了对上述 dataclass 的导入，并完全删除 `router_worker_obj` 中的 worker 注册收集和 pause/continue 广播分发代码。
3. **恢复简单路由**：`MultiTokenizerRouter` 的文档字符串从“在 tokenizer manager 与 scheduler/detokenizer 之间路由，并广播 pause/continue”恢复为“A router to receive requests from TokenizerWorker”，其 `router_worker_obj` 不再拦截 `PauseGenerationReqInput`/`ContinueGenerationReqInput` 进行广播，而直接转发到 scheduler，回到原始的单 worker 行为。
4. **保留同步处理 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
# 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，但回退后该问题再次打开。