Skip to content

CI 测试分片:新增测试文件可能把三个占固定端口的套件打进同一片,互相踩端口整片红 #3209

Closed

Description

@ranxianglei

来源: PR #3176(codex 一键安装)分析该 PR 的 CI 红灯时发现

查重: 无(检索 open+closed,关键词覆盖 shard/concurrency/EADDRINUSE/端口冲突/flake/LPT/partition)

症状

#3176 的 CI 在两次完整运行 + 一次 rerun-failed-jobs 里,同样的四个分片每次全红:ubuntu/windows × node 22/24 的 shard 3。失败测试全是 master 自有的固定端口套件:

其余所有 lane 全绿(含全部 e2e/native/registry/image/build-artifact)。rerun 后失败集合完全复现——不是偶发 flake。

根因(机制)

scripts/run-ci-tests.mjs(#2816/#3143)用按行数的确定性 LPT 装箱把测试文件分成 3 片,每片内 --test-concurrency=4 并发。上述三个套件的隔离在 master 上是偶然的(装箱副作用),不是调度器保证的:

用与 runner 完全相同的算法对两棵树做分区模拟(git ls-tree 行数权重):

shard 1 shard 2 shard 3
master auto-restart, plugin-bypass port-zone-live claude-native
#3176 HEAD plugin-bypass (无) claude-native + auto-restart + port-zone-live

本 PR 新增约 1500 行测试文件改变了装箱结果,三个占固定端口的套件被集中到 shard 3,concurrency=4 下同时运行、互踩固定端口。

证据

  1. 分区模拟如上(同一算法、同一权重规则,仅输入文件集不同)。
  2. CI 日志:port-zone-live 以 EADDRINUSE 127.0.0.1:18787 失败,紧随其后的日志显示同一个 pid(7726)同时服务 18787 和 18788(bili: attaching to running proxy …pid 7726)——是同片内另一个套件拉起的代理;claude-native 报 port 34787 is held by a lifecycle-less bili proxy (pid 4585)——pinned 端口被外部监听者占用,claude-native hooks must not attach to a lifecycle-less proxy — attach gate + self-managed fallback #1335 attach gate 对其工作正常。
  3. 本 PR diff 不触碰这些路径(无 server/update/端口绑定/restart/hook-attach 行为变更);失败全部落在「端口占用 / 守护生命周期 / spawn 时序」同一域。

影响

任何未来改变测试总行数的 PR(增删测试文件都可能)都会重新洗牌分片,随机触发同类红灯;当前 #3176 因此无法全绿。这是调度器的潜在隐患而非代码回归:runner 假设「live 套件可在 concurrency=4 下共存」(#2816 注释),但从未建模它们之间的成对资源互斥;至今的分离全靠 LPT 运气。

建议修法(供 owner 拍板)

  • A(推荐,治本):run-ci-tests.mjs 增加「互斥组」机制——注册占固定端口的套件为互斥,同片内串行执行(或每片至多一个)。与 LIVE_FILES tripwire 的初衷一致(注册即审查并发假设),且对所有未来 PR 生效。
  • B:ci.yml 按分片索引给端口偏移——claude-native 已有 BILI_CLAUDE_NATIVE_PORT,其余支持 XDG_STATE_HOME 重定位;改动小但依赖每个套件都有 override 旋钮,且 zone 注册表按 lane 名共享,覆盖不全。
  • C:三个套件改动态端口(listen(0))——最彻底但动 master 自有测试语义,面最大。

注:本问题非 #3176 代码变更引起,#3176 只是把它暴露出来。

Activity

  1. ranxianglei commented on Oct 14, 2026

    @ranxianglei
    OwnerAuthor

    🤖 Powered by ework · qwen3.8-27b

    (ai: vllm-qwen/qwen3.8-27b)

    查重结论:重复 —— 与 #3182 同症状、同机制、同根因(同家族还有 #3184/#3188),且修复已在途并全绿(PR #3197)。按先来后到规则关闭本条,保留先到条目。

    独立核验过的证据(不是转述原 issue):

    1. 同一问题更早已被立项。 CI flake storm: concurrent fixed-port e2e files (port-zone-live / claude-native / auto-restart) collide on the 18787 zone under --test-concurrency 4 #3182(2026-10-14 13:49 UTC 开单,早于本条)的定性与本条完全一致:ci: shard the test suite across the matrix to cut wall time ~3x (#3143) #3145 引入 LPT 分片把固定端口的 live 套件集中进同片,诊断为什么个别ci要跑10分钟以上,是否可以优化 #2816 的 --test-concurrency=4 让它们在一片内同时 bind,互踩 18787 区与临时端口。本点名的三个失败套件(port-zone-live / claude-native / auto-restart)与 CI flake storm: concurrent fixed-port e2e files (port-zone-live / claude-native / auto-restart) collide on the 18787 zone under --test-concurrency 4 #3182 的红车道清单逐一对应;PR CI 测试车道间歇性红灯:固定端口与自重启测试在并发分片下互相干扰(#3055 合入后出现) #3184(源自 fix: per-session ingest watermark — storage saves cost O(new messages), not O(full log) (#3133) #3166 分析)、CI 端口类 e2e 测试自 #3055 合入后批量抽风:claude-native attach 测试与 #3044 端口测试随机失败,挡住多个在途 PR #3188(源自 kernel: pair orphaned tool halves by order and multiplicity, not id-set membership (#3046) #3047 分析)是同一故障域从不同 PR 侧的重复观察,三条均已关闭(forensics 完成,修复统一挂 ci: per-file zone-port bands for live suites + bounded TOCTOU retries (#3182/#3184/#3188) #3197)。
    2. 治本修复已在途且全绿。 PR ci: per-file zone-port bands for live suites + bounded TOCTOU retries (#3182/#3184/#3188) #3197(head 1a320fb4f,分支 fix/3182-zone-slot-bands)正文声明 Closes CI flake storm: concurrent fixed-port e2e files (port-zone-live / claude-native / auto-restart) collide on the 18787 zone under --test-concurrency 4 #3182/PR CI 测试车道间歇性红灯:固定端口与自重启测试在并发分片下互相干扰(#3055 合入后出现) #3184/CI 端口类 e2e 测试自 #3055 合入后批量抽风:claude-native attach 测试与 #3044 端口测试随机失败,挡住多个在途 PR #3188,机制我逐文件读过:
      • scripts/live-files.mjs:LIVE_FILES 表从 run-ci-tests.mjs 抽出共享 + zonePortForSlot(slot) = 18787 + (slot+1)*32 每文件端口带(带宽 32 > 实测最大 ladder 漂移 +2);首带刻意避开裸 18787——忘继承 env 的套件会在自己套件里大声失败,而不是毒害别人;
      • tests/preload-zone-slot.mjs:CI-only preload 按 process.argv 识别当前测试文件、注入 BILI_ZONE_PORT(经 src/config.ts 既有产品支持传到子进程 daemon 与断言);本地 npm test 不加载此 preload,行为零变化;
      • 三个点名套件各自加固:port-zone-live(band-aware base + waitForBandFree + 完整 XDG 隔离)、claude-native(band-aware helper + freePort TOCTOU 有界重试)、auto-restart(EADDRINUSE stub exit 91 + 场景有界重试,SIGTERM 升级测试限 POSIX);外加 ensureDistBuilt 防陈旧 dist。
      • 该 head 的 39 个 CI check run 全部 success,含 12 条 test (os, node, shard) 腿。
    3. 本条「建议修法」与已实施方案的关系。 选项 A(互斥组/同片串行)与已实施的「按文件端口带」同属「注册即隔离」一族;端口带严格强于串行——保留并发度、消除碰撞,还覆盖 ladder 漂移场景。选项 B 担心的「zone 注册表覆盖不全」由共享 LIVE_FILES 表 + SPAWN_RE tripwire 兜住(新 spawning 套件不登记即挂 CI)。无需另行实现 A。
    4. 本条唯一增量信息:plugin-bypass.test.ts 的一次性 suite 超时(cancelledByParent)。它不在 LIVE_FILES(无 spawn),属同片资源争抢下的连带时序损伤,非独立机制;ci: per-file zone-port bands for live suites + bounded TOCTOU retries (#3182/#3184/#3188) #3197 合入后若复现,在 ci: per-file zone-port bands for live suites + bounded TOCTOU retries (#3182/#3184/#3188) #3197 下跟进即可。

    本条的分析质量没有问题(分区模拟、pid 双服务证据都很硬),但它回答的问题 #3182 已先回答、#3197 已先给出答案。打 重复 标签并关闭。

  2. ranxianglei commented on Oct 16, 2026

    @ranxianglei
    OwnerAuthor

    🤖 Powered by ework · qwen3.8-27b

    (ai: vllm-qwen/qwen3.8-27b)

    关闭说明:本 issue 的根因已被上游修复覆盖。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions