Repository navigation
CI 测试分片:新增测试文件可能把三个占固定端口的套件打进同一片,互相踩端口整片红 #3209
Copy link
Copy link
Closed
Labels
Description
来源: 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 自有的固定端口套件:
tests/auto-restart.test.tsperformSelfRestart SIGTERM 升级(autoRestartOnUpdate + occupied target port -> orphan process leak: new instance silently hops to the next port, old instance never exits #3044)tests/claude-native.test.tshook e2e pinned-port attach([bug] claude native proxy outlives all sessions despite README lifecycle contract — watcher-based shutdown did not fire #1322/claude-native hooks must not attach to a lifecycle-less proxy — attach gate + self-managed fallback #1335/[Design] 定死端口 lane(claude 48787 / zcode 48789)端口被占时的 0 配置解法 #1660)+ occupied-stable-porttests/port-zone-live.test.tsupgrade-replacement(autoRestartOnUpdate + occupied target port -> orphan process leak: new instance silently hops to the next port, old instance never exits #3044)tests/plugin-bypass.test.ts(opencode v1: keep opencode-acp legacy sessions working after switching to bili #920,出现过一次,suite 超时 cancelledByParent)
其余所有 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 下同时运行、互踩固定端口。
证据
- 分区模拟如上(同一算法、同一权重规则,仅输入文件集不同)。
- 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 对其工作正常。 - 本 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 自有测试语义,面最大。
Reactions are currently unavailable
Activity
ranxianglei commentedon Oct 14, 2026
(ai: vllm-qwen/qwen3.8-27b)
查重结论:重复 —— 与 #3182 同症状、同机制、同根因(同家族还有 #3184/#3188),且修复已在途并全绿(PR #3197)。按先来后到规则关闭本条,保留先到条目。
独立核验过的证据(不是转述原 issue):
--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)。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,行为零变化;test (os, node, shard)腿。本条的分析质量没有问题(分区模拟、pid 双服务证据都很硬),但它回答的问题 #3182 已先回答、#3197 已先给出答案。打
重复标签并关闭。ranxianglei commentedon Oct 16, 2026
(ai: vllm-qwen/qwen3.8-27b)
关闭说明:本 issue 的根因已被上游修复覆盖。
1a320fb4f(ci: per-file zone-port bands,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)为每个 spawn 进程的测试文件分配独立端口分带(18787+(slot+1)*32,经tests/preload-zone-slot.mjs注入BILI_ZONE_PORT),并配scripts/live-files.mjsSPAWN_RE 绊线强制新 spawn 套件登记——正是本 issue「修法选项 B(按套件端口偏移)」的落地形态,且其 commit message 描述的根因(sharding 把端口家族集中到同片 + concurrency 下同时绑定)与本人取证一致;4e036430f)中,上一轮整片红的 4 个测试分片(ubuntu/windows × node22/24 的 shard 3)全部转绿;