Skip to content

perf(ci): go-hub-test 的分片划分按实测成本重排——internal/repository 一个包就占 hub-server race 测试成本的 54.4%(227.7s / 418.8s),而 NR % 2 把它塞进 shard 2 ⇒ shard 2 在 14/14 次实测 CI run 里都是慢的那一半(中位 +119s、最大 +177s);改成它独占 shard 1 后两片 load 从 310.5/108.3 收到 227.7/191.1(spread 202.3s → 36.5s),不新增任何 job(#2251,ADR-032) - #2309

Merged
DeliciousBuding merged 2 commits into
masterfrom
perf/go-hub-test-shard-balance
Sep 3, 2026

Conversation

@DeliciousBuding

Copy link
Copy Markdown
Collaborator

这条为什么值得做(Objective D 的量化目标已经达成,这是达成之后实测出来的最长杆)

#2300@agenthub/workbench coverage job 从实测中位 441s 打到 259s(−41.3%)
「≥40% / ≤4m30s」的目标已经过了(自测 15 次 pre-#2300 run 得到的基线,不是沿用 7m31s 口径)。
过了之后极换人了,所以重新量了一遍 43 次 run 的逐 job 时间:

成因是实测的,不是「看代码觉得不均」

用 CI 同款 flag 逐包串行测(go test ./... -count=1 -short -race -p 1 -parallel 1,本机
Kunpeng ARM64 4C8G):

hub-server 包数 / 总成本 52 / 418.81s
internal/repository 一个包 227.67s = 54.4%
次重的三个 internal/middleware 36.59s、internal/service/auth 25.01s、internal/service/agent 16.90s
NR % 2 现状(shard1=偶数 NR / shard2=奇数 NR) shard1 108.3s / shard2 310.5s,spread 202.3s
repositorygo list 位置 第 15 位(奇数)⇒ 落 shard 2
改后(shard1 = 该包独占 / shard2 = 其余 51 个) 227.7s / 191.1s,spread 36.5s

两个方向都被独立证据夹住:shard 2 更慢是 14/14(另一窗口 18/18)次 CI 实测,
shard 2 更重的原因就是这个包是本地逐包实测。

227.7s 同时是硬下限:任何包粒度切分都不可能把最重的一片压到「单个包自己的成本」以下,
所以 LPT-3 的最重片还是 227.7s —— 第 3 个 shard 在这里买不到任何东西,只多烧一个 runner。
这也是为什么选 k=2 而不是 k=3:账号级并发上限实测 ~20,Go+FE run 已经峰值 18–20,
正是历史上 6 次饿死发生的形状。

硬编码一个包路径会不会腐烂?——fail-closed,不会静默

如果 internal/repository 被改名/移动/拆分,shard 1 就收到 0 个包,而既有的守卫
if [ -z "$SHARD_PKGS" ]; then echo "::error::shard N received 0 packages"; exit 1; fi
硬报错。也就是说这个常量腐烂的第一时间 CI 就红,而不是让两片悄悄失衡两个月。
再加 verify-ci-gates.py 三条断言(必须 pin 该包 / 不得退回 NR % 2 / 必须保留 0-package
守卫)与 verify-ci-gates.Tests.py 两发变异自测(case 30/31),自测 39 → 41 全绿
test_unmutated_workflow_passes 证明未变异副本仍 rc=0(无假红)。

go-edge-test 刻意不动(同样实测过,不是没看)

同一套 flag 测 edge:35 包 / 143.96s,最重 internal/lifecycle 34.57s = 24.0%
没有支配包可 pin。现状 spread 29.1s load(shard1 偶数 NR = 86.5s vs 57.4s,
这正好解释 CI 上「edge shard 2 在 14/14 次里更快、中位 −46.5s」),LPT-2 能省 14.1s load
且包数天然 18/18 平衡——go-edge-test 从来不是 backend-required 的极
(2m39s vs go-hub-test 4m51s),省下的时间到不了 PR 墙钟。所以本轮不改,
触发条件(edge-test 成为 backend 极)与全部数字写进 checks.yml 就地注释 + ADR-032(b)。

方法学教训(写进 ADR-032 ⑤,防下次重推)

不带 -race 测出来的成本分布与 CI 完全不同:同一台机器上
go test -short -count=1 -p 1(无 race)给出的是 internal/middleware 34.83s = 31%、
internal/repository 只占 11.11s = 9.9%,据此会得出「shard 1 更重」的相反结论。
加上 -race 后 repository 涨到 227.67s(20.5×,race 检测器对这个包的放大远超其它包),
方向才与 28/28 次 CI 观察一致。⇒ 任何 shard 重排都必须用 CI 同款 flag 逐包测。

同批落地的裁决

docs/decisions.md 新增 ADR-032(CI 反馈回路):① 目标已达成的自测口径与机制证据;
② stretch ≤3m 不做(单 runner 下限已 255s,再快只能删测试工作量);③ workbench 分片
DEFERRED + 可证伪的触发条件;④ 换人之后的四个极按优先级((a) 本 PR 实施 /
(b) edge 保持轮转 / (c) windows-frontend ~75s/leg 环境准备税 DEFERRED,需一次真实 CI 实验,
禁止用砍掉 Windows leg 的 Production build 换 47–50s——那是每个 PR 唯一证明 desktop
tsc && vite build 能过的地方 / (d) 两个前端杠杆严格互补、单独都不划算);⑤ 方法学。
docs/governance/verifier-map.md 的 ci-gates 行补上「Go 分片划分也是政策」。

Co-authored-by: Cursor cursor@vectorcontrol.tech

…ver race 测试成本的 54.4%(227.7s / 418.8s),而 NR % 2 把它塞进 shard 2 ⇒ shard 2 在 14/14 次实测 CI run 里都是慢的那一半(中位 +119s、最大 +177s);改成它独占 shard 1 后两片 load 从 310.5/108.3 收到 227.7/191.1(spread 202.3s → 36.5s),不新增任何 job(#2251,ADR-032)

## 这条为什么值得做(Objective D 的量化目标已经达成,这是达成之后实测出来的**新**最长杆)

#2300 把 `@agenthub/workbench` coverage job 从实测中位 **441s** 打到 **259s(−41.3%)**,
「≥40% / ≤4m30s」的目标已经过了(自测 15 次 pre-#2300 run 得到的基线,不是沿用 7m31s 口径)。
过了之后**极换人了**,所以重新量了一遍 43 次 run 的逐 job 时间:

* FE-only PR 已经变成 `frontend-required`(5/9) 与 `windows-frontend`(4/9) 的**双极平局**
  (中位差 20s)⇒ workbench coverage 再分片只能把 PR 墙钟中位从 289s 推到 275s(**−4.8%**,
  9 次里 4 次收益恰好为 0),却要把叶子数 15→19、两个重叠 FE-only PR 30→38 去撞实测 ~20 的
  账号级并发上限。**分片 DEFERRED**,触发条件写进 ADR-032(该 job 墙钟中位 ≥ ~5m10s)。
* Go-touching PR 的极是 `backend-required`,而它被 `go-hub-test` 的分片失衡顶着:
  **shard 2 在 14/14 次实测 run 里都更慢**(post-#2300 窗口 18/18),中位 **+119s**(另一个口径
  中位 |Δ| 2m02s)、最大 +177s / 3m20s(`33807478121`:6m03s vs 2m43s,把 `backend-required`
  推到 +6m52s)。这是当前**唯一「零 runner 成本、每个 Go PR 都拿得到」**的杠杆。

## 成因是实测的,不是「看代码觉得不均」

用 CI 同款 flag 逐包串行测(`go test ./... -count=1 -short -race -p 1 -parallel 1`,本机
Kunpeng ARM64 4C8G):

| | 值 |
|---|---|
| hub-server 包数 / 总成本 | 52 / **418.81s** |
| `internal/repository` 一个包 | **227.67s = 54.4%** |
| 次重的三个 | `internal/middleware` 36.59s、`internal/service/auth` 25.01s、`internal/service/agent` 16.90s |
| `NR % 2` 现状(shard1=偶数 NR / shard2=奇数 NR) | shard1 **108.3s** / shard2 **310.5s**,spread **202.3s** |
| `repository` 的 `go list` 位置 | **第 15 位(奇数)⇒ 落 shard 2** |
| 改后(shard1 = 该包独占 / shard2 = 其余 51 个) | **227.7s / 191.1s**,spread **36.5s** |

两个方向都被独立证据夹住:**shard 2 更慢**是 14/14(另一窗口 18/18)次 CI 实测,
**shard 2 更重的原因就是这个包**是本地逐包实测。

**227.7s 同时是硬下限**:任何包粒度切分都不可能把最重的一片压到「单个包自己的成本」以下,
所以 LPT-3 的最重片还是 227.7s —— 第 3 个 shard 在这里买不到任何东西,只多烧一个 runner。
这也是为什么选 k=2 而不是 k=3:账号级并发上限实测 ~20,Go+FE run 已经峰值 18–20,
正是历史上 6 次饿死发生的形状。

## 硬编码一个包路径会不会腐烂?——fail-closed,不会静默

如果 `internal/repository` 被改名/移动/拆分,shard 1 就收到 **0 个包**,而既有的守卫
`if [ -z "$SHARD_PKGS" ]; then echo "::error::shard N received 0 packages"; exit 1; fi`
会**硬报错**。也就是说这个常量腐烂的第一时间 CI 就红,而不是让两片悄悄失衡两个月。
再加 `verify-ci-gates.py` 三条断言(必须 pin 该包 / 不得退回 `NR % 2` / 必须保留 0-package
守卫)与 `verify-ci-gates.Tests.py` 两发变异自测(case 30/31),自测 39 → **41 全绿**,
且 `test_unmutated_workflow_passes` 证明未变异副本仍 rc=0(无假红)。

## `go-edge-test` 刻意**不动**(同样实测过,不是没看)

同一套 flag 测 edge:35 包 / **143.96s**,最重 `internal/lifecycle` 34.57s = **24.0%**
⇒ **没有支配包可 pin**。现状 spread 29.1s load(shard1 偶数 NR = 86.5s vs 57.4s,
这正好解释 CI 上「edge shard 2 在 14/14 次里更快、中位 −46.5s」),LPT-2 能省 14.1s load
且包数天然 18/18 平衡——**但 `go-edge-test` 从来不是 `backend-required` 的极**
(2m39s vs `go-hub-test` 4m51s),省下的时间到不了 PR 墙钟。所以本轮不改,
触发条件(edge-test 成为 backend 极)与全部数字写进 checks.yml 就地注释 + ADR-032(b)。

## 方法学教训(写进 ADR-032 ⑤,防下次重推)

**不带 `-race` 测出来的成本分布与 CI 完全不同**:同一台机器上
`go test -short -count=1 -p 1`(无 race)给出的是 `internal/middleware` 34.83s = 31%、
`internal/repository` 只占 11.11s = **9.9%**,据此会得出「shard 1 更重」的**相反**结论。
加上 `-race` 后 repository 涨到 227.67s(**20.5×**,race 检测器对这个包的放大远超其它包),
方向才与 28/28 次 CI 观察一致。⇒ 任何 shard 重排都必须用 CI 同款 flag 逐包测。

## 同批落地的裁决

`docs/decisions.md` 新增 **ADR-032**(CI 反馈回路):① 目标已达成的自测口径与机制证据;
② stretch ≤3m **不做**(单 runner 下限已 255s,再快只能删测试工作量);③ workbench 分片
**DEFERRED** + 可证伪的触发条件;④ 换人之后的四个极按优先级((a) 本 PR 实施 /
(b) edge 保持轮转 / (c) `windows-frontend` ~75s/leg 环境准备税 DEFERRED,需一次真实 CI 实验,
并**禁止**用砍掉 Windows leg 的 `Production build` 换 47–50s——那是每个 PR 唯一证明 desktop
`tsc && vite build` 能过的地方 / (d) 两个前端杠杆严格互补、单独都不划算);⑤ 方法学。
`docs/governance/verifier-map.md` 的 ci-gates 行补上「Go 分片划分也是政策」。

Co-authored-by: Cursor <cursor@vectorcontrol.tech>
@coderabbitai

coderabbitai Bot commented Sep 3, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Team

Run ID: 8506bab9-5f58-4e1d-8a7d-a29ed8a0b5a7

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

…3m49s/3m28s(spread 21s,对照基线中位 +119s)⇒ 矩阵跨度 −62s,落在预测 −40…−70s 区间;含 1 个包的那片反而较慢 21s,说明 link 成本随包数增长确实存在但比 load 失衡小一个数量级;同 run 的 go-edge-test 未改且仍非 backend 极,再次支持 ADR-032(b) 的「不动」

Co-authored-by: Cursor <cursor@vectorcontrol.tech>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant