Prhub

#2774 feat(session): configure session server workers explicitly

原始 PR 作者 guapisolo 合并时间 2026-08-28 06:09 文件变更 17 提交数 6 评论 9 代码增减 +64 / -53

执行摘要

端口与并发数解耦,新增 workers 参数

PR body 明确说明:把实例数量编码为 --session-server-port START END 的终点会让一个 flag 重载两个独立设置。分离起始端口与 worker 数输入,让操作员改并发度时无需重新计算端口区间端点;同时刻意拒绝旧的双值形式,让起始端口和实例数量各有一个唯一属主。

值得精读。这是一个有清晰设计意图的破坏性 CLI 重构:默认 32 workers 的取舍、拒绝旧语法而非兼容、TODO(#1837) 标记临时端口分配,都体现了对“配置属主唯一”和“迁移可见性”的坚持。建议重点关注 _resolve_session_server_ports 的实现与 review 中未解决的连续端口占用问题,后续很可能被 #1837 的统一端口分配重构取代。

讨论亮点

围绕默认值发生了本轮最有价值的交锋:claude[bot] 指出默认 32 会静默改变所有只传 --session-server-port 的启动脚本(旧语义下单端口 = 单实例);guapisolo 回应 32 是刻意的,单个 session-server 进程固定开销太大,默认 1 会让操作员被迫手动选择高效配置,且所有仓库内 launcher 已显式声明 32,迁移可见。另一条未解决的是 auto-port 路径只探测 1 个端口再派生 31 个连续端口,没有校验占用,claude[bot] 建议复用 get_free_port(start_port, consecutive),作者仅加 TODO(#1837)。Shi-Dong 的 README 同步请求已在 0cdda4e87 完成。

实现拆解

  1. CLI 参数定义变更(miles/utils/arguments.py):--session-server-port 从 nargs="+" 改为单值 type=int、default=None,语义从“端口或区间”收敛为“起始端口,不传则自动分配”;新增 --session-server-workers,默认 32。这是默认行为变化的源头,也是 review 争议所在。

  2. 端口解析函数重写(miles/ray/rollout/router_manager.py):_resolve_session_server_ports(start, workers) 保留“None 时自动分配”路径,删除 start >= end 空区间校验和“超过两个值报错”分支,新增 workers < 1 校验并在 spawn 任何进程之前失败;调用点 start_session_server 改为传入 args.session_server_port 与 args.session_server_workers。该改动直接决定并发规模,且移除了旧端口的兼容路径。

  3. 测试配套:tests/fast/ray/rollout/test_router_manager.py 用 test_one_worker_uses_the_starting_port、test_workers_expand_from_the_starting_port、test_non_positive_workers_raise 替换旧的区间用例;tests/fast/utils/test_arguments.py 新增 TestSessionServerScalingArguments,锁定默认 32、显式覆盖,以及旧 end-port 形式被 argparse 拒绝。

  4. 示例与文档联动:所有仓库内 session-server 启动脚本显式追加 --session-server-workers 32(nemo-gym、openenv、terminus-compaction、swe-agent-harbor-docker 等),并同步 README 网络图到端口 30000-30031 + router 31000,最后通过 sync-example-docs 重新生成文档页,保证 CI 漂移检查一致。

文件 模块 状态 重要度
miles/ray/rollout/router_manager.py 会话服务 modified 7.12
miles/utils/arguments.py 参数配置 modified 5.94
tests/fast/ray/rollout/test_router_manager.py 路由管理 modified 6.23
tests/fast/utils/test_arguments.py 参数配置 modified 6.02
examples/experimental/nemo-gym/run.py 示例脚本 modified 3.78
examples/swe-agent-harbor-docker/run.py 示例脚本 modified 2.64

关键符号

_resolve_session_server_ports start_session_server add_session_arguments

关键源码片段

miles/ray/rollout/router_manager.py core-logic

核心逻辑变更:_resolve_session_server_ports 从 [start, end) 区间语义改为 start + workers 展开,破坏性移除旧双值语法;是后续会话服务器并发调控的入口。

# miles/ray/rollout/router_manager.py
def _resolve_session_server_ports(start: int | None, workers: int) -> list[int]:
    """Return the requested number of consecutive ports from the configured or auto-selected start."""
    if workers < 1:
        # 在生成任何进程之前失败,避免出现 0 个或“负数量”端口列表。
        raise ValueError("--session-server-workers must be at least 1.")
    # TODO(#1837): Refactor IP/port allocation; keep this naive for now.
    if start is None:
        # 仅探测单个可用端口,后续连续端口不逐一检查占用(review 已指出)。
        start = find_available_port(random.randint(5000, 6000))
    return list(range(start, start + workers))
# miles/ray/rollout/router_manager.py —— start_session_server 中的调用点
    ip = args.session_server_ip
    ports = _resolve_session_server_ports(args.session_server_port, args.session_server_workers)
    for port in ports:
        if not is_port_available(port):
            raise RuntimeError(
                f"Port {port} is already in use — a stale session server may still be running. "
                f"Run 'pkill -9 python' to kill it, then retry."
            )
    # The canonical driver-side value; rollout code picks from this list.
    args.session_server_ports = ports
miles/utils/arguments.py core-logic

CLI 参数定义变更:--session-server-port 从 nargs='+' 改为单值,新增 --session-server-workers 默认 32;是默认行为变化的源头,直接命中 review 中的静默迁移争议。

# miles/utils/arguments.py —— add_session_arguments 中的参数定义
            parser.add_argument(
                "--session-server-port",
                type=int,
                default=None,
                help="Starting port for standalone session servers. Auto-allocated if not set.",
            )
            parser.add_argument(
                "--session-server-workers",
                type=int,
                default=32, # 旧语义下单端口 = 1 个服务器,新默认 32 是刻意的并发选择。
                help="Number of standalone session servers to launch on consecutive ports.",
            )

评论区精华

默认 32 workers 的静默行为变更 设计

claude[bot] 指出旧 nargs='+' 下单个端口值意味着 1 个服务器,默认 32 会让所有只传 --session-server-port 的启动脚本静默扩展到 32 个实例;guapisolo 回应 32 是刻意默认:单进程固定开销大,默认 1 会让操作员被迫手动选高并发配置,且已在 e9ea5e8 中给全部仓库内 launcher 显式声明 --session-server-workers 32。

结论:默认保持 32;仓库内启动器全部显式声明以避免静默继承,仓库外调用者视为有意的默认采纳。 · 已解决

自动端口分配未探测连续端口块 正确性

claude[bot] 指出省略 --session-server-port 时只通过 find_available_port 探测 1 个端口,再直接派生 32 个连续端口不检查占用,与代码库已有 get_free_port(start_port, consecutive)(miles/utils/misc.py:122)不一致;无作者回复,实现仅加 TODO(#1837)。

结论:未解决;当前保留朴素实现,连续端口冲突风险由 is_port_available 逐端口前置检查兜底,但仍可能全部不可用导致启动失败。 · 待处理

示例 README 与启动器端口同步 documentation

Shi-Dong 要求 terminus-compaction 与 swe-agent-harbor-docker 的 README 同步最新端口;guapisolo 在 0cdda4e87 更新两个 README,写明 32 个 session-server worker 占用端口 30000-30031、SGLang router 用 31000。

结论:已同步并通过 sync-example-docs 校验。 · 已解决

风险与影响

  • CLI 破坏性变更:移除 --session-server-port A B 旧形式,旧脚本会 argparse 报错退出;仓库内已全部迁移,外部脚本需同步。
  • 默认并发从 1 升到 32:省略 --session-server-workers 的仓库外调用方会静默拉起 32 个进程,端口与内存资源消耗陡增;start_session_server 虽有逐端口 is_port_available 前置检查做快速失败,但首个端口可用时仍可能因后续端口冲突在中途抛错。
  • 自动端口分配只探测单端口(router_manager.py 的 find_available_port 分支):派生连续端口不检查占用,与代码库已有的 get_free_port(start_port, consecutive) 不一致;该问题在 review 中提出但未闭合。
  • 32 个 spawn 子进程各自承担约 10s 的 transformers import,启动墙钟时间和内存占用成倍增长。

对操作员是直接的 CLI 语义变更:需要理解 --session-server-port 现在是“起始端口”、并发数由 --session-server-workers 控制,仓库外脚本必须迁移。对系统资源,默认 32 个 session-server 进程意味着更高的常驻内存与端口占用,小规模实验需显式调小。对团队,所有示例与生成文档已同步,CI 有测试锁定默认值和破坏性行为,回归风险主要在外部调用方。

CLI 破坏性变更 默认并发 1 -> 32 auto-port 未校验连续端口 仓库外脚本需迁移

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论