docs: record consolidated runner decision
This commit is contained in:
@@ -12,6 +12,8 @@ The artifact boundary remains load-bearing. `publint`, `verify-node-next-types`,
|
||||
|
||||
## Decision
|
||||
|
||||
The production topology below was superseded by [Evidence-based larger hosted runners](2026-07-22-evidence-based-larger-hosted-runners.md). Its shard selectors and former job layout remain available only through manual `suite=sharded-reference`; this note preserves why that earlier topology was implemented.
|
||||
|
||||
[CI](../../../../.github/workflows/ci.yml) treats one minute for non-Windows jobs and three minutes for Windows jobs as observed performance targets, not cancellation deadlines. Hosted-runner variance should leave complete timing evidence and useful failure logs instead of cancelling an otherwise-correct gate. The [serial cross-platform CI reference](2026-07-21-serial-cross-platform-ci-reference.md) independently runs the complete unsharded primary Node aggregate on Linux, macOS, and Windows so the optimized lane inventory is not its own completeness oracle.
|
||||
|
||||
[scripts/run-gates.ts](../../../../scripts/run-gates.ts) remains the common bounded scheduler, but GitHub supplies explicit shard names for the expensive gate families. [scripts/static-shards.ts](../../../../scripts/static-shards.ts) partitions static gates into foundation, documentation-type, API-contract, catalog, prose, documentation-projection, and documentation-build ownership and rejects a missing or duplicate gate assignment; low-cost ownership groups may share one hosted lane to stay below the runner-provisioning concurrency wave. Linux lint uses disjoint A-C, D-M, N-S, and T-Z package-source and package-test lanes, while the longer Windows target uses one complete package-source lane and one complete package-test lane; both topologies include a repository complement that still starts from `.` so a new top-level lint target cannot disappear between shards and owns the single cross-file duplication run. [scripts/coverage-shards.ts](../../../../scripts/coverage-shards.ts) assigns every workspace package to exactly one source-coverage lane; its test expands the live package tree, so a new package makes CI red until it has an owner. Directory filters retain a trailing separator because Vitest positional filters match substrings and would otherwise admit prefix-named sibling packages. Each coverage lane includes only its owned source files, repeats the exhaustive companion topology test, and runs without a preceding build because the complete coverage suite passes from a tree with every generated `lib/` removed.
|
||||
|
||||
+2
-2
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write
|
||||
2026-07-22-evidence-based-larger-hosted-runners.md: 992a622f0d7a2662cc65353d6c4cd1350e1f3f5f
|
||||
2026-07-22-evidence-based-larger-hosted-runners.zh.md: 622911af8400fbe159038b69b440c0e97fb1f2cc
|
||||
2026-07-22-evidence-based-larger-hosted-runners.md: 8d3852c56e2289bd222a260f2685f6a941a0cb19
|
||||
2026-07-22-evidence-based-larger-hosted-runners.zh.md: 4e7f846db1be2d68a080381943012583636ed2c4
|
||||
+41
-13
@@ -6,39 +6,67 @@ English | [中文](2026-07-22-evidence-based-larger-hosted-runners.zh.md)
|
||||
|
||||
## Problem
|
||||
|
||||
The sharded primary CI fits its latency targets on standard GitHub-hosted runners, but the margin depends on cold setup and install variance. Larger runners may add useful headroom, yet their per-minute price rises much faster than these short lanes can use extra cores. Selecting a size from machine specifications or a synthetic benchmark would spend more without proving that repository CI becomes faster.
|
||||
The shard-heavy CI topology met its latency targets by spreading primary Node work across 40 Linux jobs and Windows work across nine jobs. Most gates were shorter than checkout, runner setup, cache restore, and dependency installation, so repeated setup waves created both cost and latency variance. One hosted run finished its slowest Linux job in 49 seconds yet took 231 seconds for a Windows lint shard whose checkout, cache restore, and install alone consumed 158 seconds.
|
||||
|
||||
Larger runners make it possible to pay setup once and parallelize inside the repository scheduler, but the useful size cannot be selected from core counts alone. Critical-lane benchmarks did not scale monotonically, and a whole-repository aggregate exposed different bottlenecks from isolated typecheck or site builds.
|
||||
|
||||
## Decision
|
||||
|
||||
The organization keeps twelve x64 larger-runner pools in the repo-restricted `dsh-larger-ci` group: Ubuntu 24.04 and Windows 2025 at 4, 8, 16, 32, 64, and 96 cores. Public IPs are disabled. Each pool has an autoscaling ceiling of 256, while the repository bounds actual demand through its workflow matrices; an idle ceiling does not allocate machines.
|
||||
The organization keeps twelve x64 larger-runner pools in the repo-restricted `dsh-larger-ci` group: Ubuntu 24.04 and Windows 2025 at 4, 8, 16, 32, 64, and 96 cores. Public IPs are disabled. Each pool has an autoscaling ceiling of 256; the ceiling does not allocate idle machines or remove the need to bound workflow demand.
|
||||
|
||||
The `CI` workflow exposes `suite=larger-runner-benchmark` only through manual dispatch. Its six Linux legs run the critical typecheck lane, and its six Windows legs run the critical production-site lane. Every leg reports the observed CPU and memory, has a 15-minute timeout, and uses the same setup and caching policy as the production lane it represents. Push and pull-request events skip this benchmark.
|
||||
Production CI uses three larger-runner jobs and keeps Node compatibility, Python, and the final aggregator on standard runners:
|
||||
|
||||
The [twelve-size benchmark](https://github.com/deepseek-harness/deepseek-harness/actions/runs/29895295659) used a workflow-only commit on top of the standard-runner [baseline](https://github.com/deepseek-harness/deepseek-harness/actions/runs/29850033610), so the code, lockfile, and critical commands were identical:
|
||||
- `node 24 / complete` uses the 96-core Linux pool. One checkout, setup, cache restore, and install feeds the complete unsharded 40-gate primary inventory. `run-gates` starts up to 32 independent gates, ESLint uses 32 workers, snapshots use up to 32 subprocesses, and coverage uses 16 forks. Build starts beside typecheck; snapshot and artifact consumers still wait for emitted output. Coverage stays below 32 forks because that setting twice caused Node 24's CJS lexer to terminate a Vitest worker and invalidate coverage.
|
||||
- `windows / blocking builds` uses the 16-core Windows pool. Build and the production VitePress site run concurrently after one setup.
|
||||
- `windows node 24 / observational` uses the 32-core Windows pool. Its complete unsharded 37-gate static, lint, and artifact inventory runs with 32 outer scheduler slots and remains non-blocking. ESLint itself stays single-threaded because 16 ESLint worker threads increased full-lint time to 174.54 seconds; with outer concurrency and no ESLint workers, the same full lint took 31.67 seconds.
|
||||
|
||||
The two Windows jobs deliberately use different pools. A first candidate put both on the 16-core pool and GitHub took 93 seconds to provision the second same-label runner despite the configured autoscaling ceiling. Using the 16-core and 32-core pools let every production job begin within two seconds in the final validation run.
|
||||
|
||||
The workflow retains four manual diagnostics. `suite=larger-runner-benchmark` compares isolated critical lanes across every size, `suite=consolidated-runner-benchmark` compares whole aggregates, `suite=sharded-reference` preserves the former production shard topology, and `suite=serial-reference` remains the unsharded cross-platform completeness oracle. `suite=optimized-larger-runners` runs the exact production topology against a branch ref when a pull request cannot form a merge commit.
|
||||
|
||||
The first [twelve-size critical-lane benchmark](https://github.com/deepseek-harness/deepseek-harness/actions/runs/29895295659) used a workflow-only commit on top of the standard-runner [baseline](https://github.com/deepseek-harness/deepseek-harness/actions/runs/29850033610), so the code, lockfile, and commands were identical:
|
||||
|
||||
| Critical job | Standard | 4 cores | 8 cores | 16 cores | 32 cores | 64 cores | 96 cores |
|
||||
|---|---:|---:|---:|---:|---:|---:|---:|
|
||||
| Linux typecheck | 56 s | 38 s | 35 s | 40 s | 35 s | 44 s | 40 s |
|
||||
| Windows production site | 160 s | 117 s | 103 s | 113 s | 75 s | 105 s | 108 s |
|
||||
|
||||
The repository therefore uses the 4-core pools for the primary Node matrix and all Windows jobs. Linux 4-core finished within three seconds of the fastest measured size at the lowest larger-runner rate. Windows 4-core stayed below two minutes; the isolated 32-core result was faster, but adjacent larger sizes regressed and the production-site command itself varied only from 28 to 36 seconds, so setup and install noise—not scalable compute—created most of the spread. Node compatibility, Python, and the final aggregator remain on standard runners because their baseline jobs already finish well below one minute.
|
||||
Those isolated results showed that setup dominated but did not identify the production size. A [whole-aggregate benchmark without native ESLint concurrency](https://github.com/deepseek-harness/deepseek-harness/actions/runs/29897826082) found a 69-second single-threaded Linux lint gate. After enabling native Linux ESLint concurrency, the [second whole-aggregate benchmark](https://github.com/deepseek-harness/deepseek-harness/actions/runs/29898331705) produced these active job times:
|
||||
|
||||
The workflow also exposes `suite=optimized-larger-runners` through manual dispatch. That path runs the production matrices against the branch ref itself, providing an exact-head timing check when a pull request cannot form a merge commit.
|
||||
| Aggregate job | 4 cores | 8 cores | 16 cores | 32 cores | 64 cores | 96 cores |
|
||||
|---|---:|---:|---:|---:|---:|---:|
|
||||
| Linux complete primary | 147 s | 104 s | 95 s | failed at 57 s | 66 s | 60 s |
|
||||
| Windows blocking builds | 137 s | 127 s | 113 s | 107 s | 105 s | 131 s |
|
||||
|
||||
The Linux 32-core failure was the first CJS-lexer worker crash. The 96-core aggregate was the only successful all-size result at the one-minute boundary. Windows gained little above 16 cores, so the blocking job uses 16 cores; the observational job uses a separate 32-core pool to avoid same-label provisioning delay and to start all outer gates together.
|
||||
|
||||
The exact production [validation run](https://github.com/deepseek-harness/deepseek-harness/actions/runs/29899733584) passed every job at the tested branch head:
|
||||
|
||||
| Production job | Active time | Repository work | Result |
|
||||
|---|---:|---:|---:|
|
||||
| Linux complete primary | 50 s | 40 gates in 23.23 s | passed |
|
||||
| Slowest standard non-Windows job | 40 s | Node 26 compatibility | passed |
|
||||
| Windows blocking builds | 91 s | 2 gates in 28.69 s | passed |
|
||||
| Windows observational | 153 s | 37 gates in 31.83 s | passed |
|
||||
|
||||
The Windows observational job spent 57 seconds restoring its pnpm cache, so its remaining margin measures hosted setup variance as well as repository work. The final run still stays below one minute for every non-Windows job and below three minutes for both Windows jobs.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
**Keep every job on standard runners.** This meets the threshold but gives the critical lanes no cold-run margin and leaves the larger-runner suggestion untested.
|
||||
**Keep the former shard topology in production.** The shards can be fast when provisioned together, but 49 larger-runner jobs repeat setup and create more chances for a cold outlier. The 231-second Windows control demonstrated that a short lint shard does not protect the end-to-end job target.
|
||||
|
||||
**Select 8 or 32 cores from the fastest individual result.** The small differences were not monotonic, while billing grows sharply with size. Treating one noisy minimum as scaling evidence would make recurring CI substantially more expensive.
|
||||
**Select a production size from the critical-lane benchmark.** Four cores looked cost-effective for isolated typecheck and site builds, but the full aggregate found repository-wide lint and dependent artifact work that those commands did not represent.
|
||||
|
||||
**Move every job to a larger runner.** Compatibility, Python, and aggregation were already short; paying the larger-runner premium there cannot improve the critical path enough to justify the dependency or cost.
|
||||
**Prebuild before starting the Linux aggregate.** This moved build onto the setup path and produced a 66-second candidate. Starting build eagerly inside `run-gates` preserves artifact dependencies while overlapping it with unrelated checks; the final aggregate completed in 23.23 seconds.
|
||||
|
||||
**Use a synthetic CPU benchmark.** A microbenchmark would not include checkout, action startup, package installation, cache restore, or the repository command mix that dominates these jobs.
|
||||
**Use native ESLint worker concurrency on Windows.** Sixteen workers made lint more than five times slower than the final single-threaded result. Outer gate parallelism uses the 32-core runner without multiplying ESLint's Windows worker startup and TypeScript project loading.
|
||||
|
||||
**Move compatibility, Python, and aggregation to larger runners.** These standard-runner jobs all completed in 40 seconds or less. Paid capacity would not shorten the critical path.
|
||||
|
||||
## Consequences
|
||||
|
||||
The benchmark incurred $2.936 across dedicated larger-runner SKUs, as recorded by organization billing immediately after the run. The existing zero-dollar Actions budget did not block those jobs, so the repo-only runner group, manual benchmark trigger, matrix width, and timeout are the observed cost controls; the budget is not treated as an execution guard.
|
||||
The final production validation consumed one billed 96-core Linux minute, two billed 16-core Windows minutes, and three billed 32-core Windows minutes. At the configured larger-runner rates, its larger-runner cost was $0.902. The all-size critical benchmark cost $2.936. GitHub rounds each larger-runner job up to a whole minute, so reducing paid job count from 49 to three matters as much as shortening repository work.
|
||||
|
||||
Production CI now depends on the organization-owned runner names in this note and in `.github/workflows/ci.yml`. Missing or renamed pools leave jobs queued instead of silently falling back to standard capacity. The 256 autoscaling ceiling permits future concurrency but does not override the bounded matrices in the current workflow.
|
||||
The existing zero-dollar Actions budget did not block larger-runner jobs. The repo-only runner group, bounded workflow topology, manual benchmark triggers, and job timeouts are the observed cost controls; the budget is not treated as an execution guard.
|
||||
|
||||
The selected pools buy latency headroom at a recurring per-job premium. The manual benchmark retains all sizes so a future image, dependency graph, or workload change can be measured before changing the production labels again.
|
||||
Production CI depends on the organization-owned runner names in this note and in `.github/workflows/ci.yml`. Missing or renamed pools leave jobs queued instead of falling back to standard capacity. Manual all-size and former-shard suites remain available so image, dependency, scheduler, or pricing changes can be remeasured before changing production labels.
|
||||
+41
-13
@@ -6,39 +6,67 @@ Status: implemented
|
||||
|
||||
## 问题
|
||||
|
||||
分片后的主 CI 在标准 GitHub 托管运行器上能够达到延迟目标,但余量会受冷启动设置和安装耗时波动影响。大型运行器或许能提供更多有效余量,但随着核心数增加,其每分钟单价增长得远比这些短通道能够利用额外核心的速度快。仅根据机器规格或合成基准测试选择配置,只会增加支出,无法证明仓库 CI 确实变快。
|
||||
高度分片的 CI 拓扑通过把主 Node 工作分散到 40 个 Linux 作业、把 Windows 工作分散到 9 个作业来达到延迟目标。大多数门禁本身的耗时短于代码检出、运行器设置、缓存恢复和依赖安装这些准备阶段,因此反复执行多轮设置既增加成本,也带来延迟波动。一次托管运行中最慢的 Linux 作业用时 49 秒,而一个 Windows lint 分片却耗时 231 秒,其中仅代码检出、缓存恢复和安装就占了 158 秒。
|
||||
|
||||
大型运行器可以让 CI 只承担一次设置开销,再由仓库调度器在内部并行执行,但无法仅凭核心数选出有实际价值的规格。关键通道基准测试的性能提升不呈单调变化,完整仓库聚合流程暴露出的瓶颈也不同于单独运行类型检查或网站构建时的瓶颈。
|
||||
|
||||
## 决策
|
||||
|
||||
组织在仅限本仓库使用的 `dsh-larger-ci` 运行器组中保留 12 个 x64 大型运行器池:Ubuntu 24.04 和 Windows 2025 各设 4、8、16、32、64、96 核规格。公网 IP 已禁用。每个池的自动扩缩容上限为 256,仓库则通过工作流矩阵限制实际需求;空闲状态下的上限不会分配机器。
|
||||
组织在仅限本仓库使用的 `dsh-larger-ci` 运行器组中保留 12 个 x64 大型运行器池:Ubuntu 24.04 和 Windows 2025 各设 4、8、16、32、64、96 核规格。公网 IP 已禁用。每个池的自动扩缩容上限为 256;该上限既不会分配闲置机器,也不能免除限制工作流需求的必要性。
|
||||
|
||||
`CI` 工作流仅在手动触发时提供 `suite=larger-runner-benchmark`。其中 6 个 Linux 矩阵分支运行关键类型检查通道,6 个 Windows 矩阵分支运行生产网站关键通道。每个矩阵分支都会报告实测 CPU 和内存,超时限制为 15 分钟,并采用与其所代表的生产通道相同的设置和缓存策略。push 和拉取请求事件会跳过这项基准测试。
|
||||
生产 CI 使用 3 个大型运行器作业,并让 Node 兼容性、Python 和最终聚合作业继续使用标准运行器:
|
||||
|
||||
这项[涵盖 12 种规格的基准测试](https://github.com/deepseek-harness/deepseek-harness/actions/runs/29895295659)以标准运行器[基线](https://github.com/deepseek-harness/deepseek-harness/actions/runs/29850033610)为基础,只叠加了一个仅修改工作流的提交,因此代码、锁文件和关键命令完全相同:
|
||||
- `node 24 / complete` 使用 96 核 Linux 池。只需执行一次代码检出、设置、缓存恢复和安装,即可供完整且未分片的 40 项主门禁清单使用。`run-gates` 最多同时启动 32 项相互独立的门禁,ESLint 使用 32 个工作线程,快照最多使用 32 个子进程,覆盖率运行使用 16 个 fork。构建与类型检查同时启动;快照和产物消费方仍会等待生成的输出。覆盖率运行的 fork 数保持低于 32,因为将其设为 32 曾两次导致 Node 24 的 CJS 词法分析器终止一个 Vitest 工作进程,使覆盖率结果失效。
|
||||
- `windows / blocking builds` 使用 16 核 Windows 池。一次设置完成后,构建任务与生产版 VitePress 网站任务并发运行。
|
||||
- `windows node 24 / observational` 使用 32 核 Windows 池。其完整且未分片的 37 项静态、lint 和产物门禁清单使用 32 个外层调度器槽位运行,并保持非阻塞。ESLint 本身仍采用单线程,因为启用 16 个 ESLint 工作线程会把完整 lint 的耗时增加至 174.54 秒;在启用外层并发且不使用 ESLint 工作线程时,同一项完整 lint 只需 31.67 秒。
|
||||
|
||||
两个 Windows 作业有意使用不同的运行器池。首个候选方案让二者都使用 16 核池;尽管已配置自动扩缩容上限,GitHub 仍花费 93 秒才预配好第二台同标签运行器。分别使用 16 核池和 32 核池后,最终验证运行中的每个生产作业都在 2 秒内开始运行。
|
||||
|
||||
工作流保留 4 项手动诊断。`suite=larger-runner-benchmark` 比较所有规格下相互独立的关键通道,`suite=consolidated-runner-benchmark` 比较完整聚合流程,`suite=sharded-reference` 保留原生产分片拓扑,`suite=serial-reference` 则继续作为未分片的跨平台完整性判定基准。当拉取请求无法生成合并提交时,`suite=optimized-larger-runners` 会直接针对分支引用运行与生产环境完全相同的拓扑。
|
||||
|
||||
首次[涵盖 12 种规格的关键通道基准测试](https://github.com/deepseek-harness/deepseek-harness/actions/runs/29895295659)以标准运行器[基线](https://github.com/deepseek-harness/deepseek-harness/actions/runs/29850033610)为基础,只叠加了一个仅修改工作流的提交,因此代码、锁文件和命令完全相同:
|
||||
|
||||
| 关键作业 | 标准 | 4 核 | 8 核 | 16 核 | 32 核 | 64 核 | 96 核 |
|
||||
|---|---:|---:|---:|---:|---:|---:|---:|
|
||||
| Linux 类型检查 | 56 秒 | 38 秒 | 35 秒 | 40 秒 | 35 秒 | 44 秒 | 40 秒 |
|
||||
| Windows 生产网站 | 160 秒 | 117 秒 | 103 秒 | 113 秒 | 75 秒 | 105 秒 | 108 秒 |
|
||||
|
||||
因此,仓库让主 Node 矩阵和所有 Windows 作业使用 4 核池。Linux 4 核规格比实测最快规格至多慢 3 秒,同时采用大型运行器中的最低费率。Windows 4 核规格保持在 2 分钟以内;单次测得的 32 核结果虽然更快,但相邻的大型运行器规格反而耗时回升,而生产网站命令本身的耗时仅在 28 至 36 秒之间波动,因此大部分差异来自设置和安装噪声,而非可随核心数扩展的计算负载。Node 兼容性、Python 和最终聚合作业仍使用标准运行器,因为它们的基线耗时本就远低于 1 分钟。
|
||||
这些单项结果表明设置开销占主导地位,却无法确定生产环境应选用的规格。一项[未启用 ESLint 原生并发的完整聚合基准测试](https://github.com/deepseek-harness/deepseek-harness/actions/runs/29897826082)发现,Linux 单线程 lint 门禁耗时 69 秒。启用 Linux ESLint 原生并发后,[第二次完整聚合基准测试](https://github.com/deepseek-harness/deepseek-harness/actions/runs/29898331705)得到了以下作业活动耗时:
|
||||
|
||||
工作流还在手动触发时提供 `suite=optimized-larger-runners`。这一路径直接以分支引用本身运行生产矩阵;当拉取请求无法生成合并提交时,它仍能针对该分支实际的头部提交提供精确计时检查。
|
||||
| 聚合作业 | 4 核 | 8 核 | 16 核 | 32 核 | 64 核 | 96 核 |
|
||||
|---|---:|---:|---:|---:|---:|---:|
|
||||
| Linux 完整主流程 | 147 秒 | 104 秒 | 95 秒 | 57 秒时失败 | 66 秒 | 60 秒 |
|
||||
| Windows 阻塞性构建 | 137 秒 | 127 秒 | 113 秒 | 107 秒 | 105 秒 | 131 秒 |
|
||||
|
||||
Linux 32 核作业的失败是首次发生的 CJS 词法分析器工作进程崩溃。在所有规格的结果中,96 核聚合作业是唯一成功达到 1 分钟边界的结果。Windows 超过 16 核后的收益很小,因此阻塞性作业使用 16 核;观测作业则使用单独的 32 核池,以避免同标签运行器的预配延迟,并让全部外层门禁同时启动。
|
||||
|
||||
生产环境的精确[验证运行](https://github.com/deepseek-harness/deepseek-harness/actions/runs/29899733584)在受测分支头通过了所有作业:
|
||||
|
||||
| 生产作业 | 活动耗时 | 仓库工作 | 结果 |
|
||||
|---|---:|---:|---:|
|
||||
| Linux 完整主流程 | 50 秒 | 40 项门禁耗时 23.23 秒 | 通过 |
|
||||
| 最慢的标准非 Windows 作业 | 40 秒 | Node 26 兼容性 | 通过 |
|
||||
| Windows 阻塞性构建 | 91 秒 | 2 项门禁耗时 28.69 秒 | 通过 |
|
||||
| Windows 观测作业 | 153 秒 | 37 项门禁耗时 31.83 秒 | 通过 |
|
||||
|
||||
Windows 观测作业花费 57 秒恢复 pnpm 缓存,因此其剩余余量既反映托管环境的设置波动,也反映仓库工作耗时。最终运行中每个非 Windows 作业仍低于 1 分钟,两个 Windows 作业也都低于 3 分钟。
|
||||
|
||||
## 曾考虑的替代方案
|
||||
|
||||
**所有作业均保留在标准运行器上。** 此方案能达到时长目标,但关键通道面对冷启动时没有余量,也无法验证大型运行器方案。
|
||||
**在生产环境中保留原分片拓扑。** 各分片在一同完成预配时可以很快,但 49 个大型运行器作业会重复设置,也增加了出现冷启动异常值的机会。耗时 231 秒的 Windows 对照结果表明,短小的 lint 分片并不能保障端到端作业达到时长目标。
|
||||
|
||||
**根据单次最快结果选择 8 核或 32 核。** 各规格间的微小差异并不呈单调变化,计费却随规格急剧增长。把一次带有噪声的最低值当作扩展性证据,会大幅提高 CI 的经常性成本。
|
||||
**根据关键通道基准测试选择生产规格。** 对单独的类型检查和网站构建而言,4 核看起来具备成本效益,但完整聚合流程发现了这些命令未覆盖的全仓库 lint 和存在依赖关系的产物工作。
|
||||
|
||||
**将所有作业迁移到大型运行器。** 兼容性、Python 和聚合作业原本就很短;为这些作业支付大型运行器溢价,无法充分缩短关键路径,因而不足以证明这种依赖或成本合理。
|
||||
**在启动 Linux 聚合流程前预先构建。** 此方案让构建成为设置路径的一部分,并产生了一个耗时 66 秒的候选结果。在 `run-gates` 内尽早启动构建,既能保留产物依赖关系,又能让构建与无关检查重叠执行;最终聚合流程耗时 23.23 秒。
|
||||
|
||||
**使用合成 CPU 基准测试。** 微基准测试不会包含代码检出、操作启动、包安装、缓存恢复或主导这些作业耗时的仓库命令组合。
|
||||
**在 Windows 上使用 ESLint 原生工作线程并发。** 16 个工作线程让 lint 比最终的单线程结果慢 5 倍以上。外层门禁并发能够利用 32 核运行器,同时不会成倍增加 ESLint 在 Windows 上启动工作线程和加载 TypeScript 项目的开销。
|
||||
|
||||
**将兼容性、Python 和聚合作业迁移到大型运行器。** 这些标准运行器作业都在 40 秒以内完成。付费容量不会缩短关键路径。
|
||||
|
||||
## 后果
|
||||
|
||||
这次基准测试使用专用大型运行器 SKU,共产生 $2.936 费用,组织计费系统在运行结束后立即记录了这笔费用。现有的零美元 Actions 预算并未阻止这些作业,因此经实测有效的成本控制机制是仅限本仓库的运行器组、手动基准测试触发、矩阵宽度和超时限制;该预算不被视为执行防护措施。
|
||||
最终生产验证产生的计费时长为:96 核 Linux 1 分钟、16 核 Windows 2 分钟和 32 核 Windows 3 分钟。按已配置的大型运行器费率计算,其大型运行器成本为 $0.902。全规格关键通道基准测试的成本为 $2.936。GitHub 会把每个大型运行器作业向上取整到整分钟计费,因此把付费作业数从 49 个减少到 3 个,与缩短仓库工作耗时同样重要。
|
||||
|
||||
生产 CI 目前依赖本 Agent Note 和 `.github/workflows/ci.yml` 中由组织持有的运行器名称。池缺失或改名会让作业一直排队,不会静默回退到标准容量。256 的自动扩缩容上限允许未来提高并发量,但不会突破当前工作流对矩阵规模的限制。
|
||||
现有的零美元 Actions 预算并未阻止大型运行器作业。仅限本仓库的运行器组、有界的工作流拓扑、手动基准测试触发和作业超时限制才是经实测有效的成本控制机制;该预算不被视为执行防护措施。
|
||||
|
||||
选定的运行器池以每次作业都需支付的溢价换取延迟余量。手动基准测试保留了所有规格,以便未来映像、依赖图或工作负载发生变化时,先完成测量,再次调整生产标签。
|
||||
生产 CI 依赖本 Agent Note 和 `.github/workflows/ci.yml` 中由组织持有的运行器名称。池缺失或改名会让作业一直排队,不会回退到标准容量。手动全规格套件和原分片套件均予以保留,以便在映像、依赖、调度器或定价发生变化后重新测量,再调整生产标签。
|
||||
Reference in New Issue
Block a user