Skip to content

Commit bbbd439

Browse files
antiguruclaude
andcommitted
ci: shard cargo-bench by package onto small dedicated agents
Splits the nightly cargo-bench step into 9 parallel Buildkite jobs, each sharded by package rather than by target so a shard builds and runs one package's full dependency closure at the ancestor and at HEAD. This lets the step run on small dedicated 8cpu/32gb machines instead of a single 48-core box, and shards without an assigned package exit as no-ops. The mzcompose allowlist in the plugin hook now matches the label by prefix since parallel jobs append an index suffix to BUILDKITE_LABEL. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
1 parent d53f143 commit bbbd439

3 files changed

Lines changed: 21 additions & 7 deletions

File tree

ci/nightly/pipeline.template.yml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,7 @@ steps:
304304
# Annotation-only while the noise floor on this hardware is being
305305
# calibrated. Remove after 2026-09-18 so regressions fail the build.
306306
soft_fail: true
307+
parallelism: 9
307308
inputs:
308309
- Cargo.lock
309310
- Cargo.toml
@@ -314,9 +315,10 @@ steps:
314315
composition: cargo-bench
315316
ci-builder: stable
316317
agents:
317-
# Dedicated hardware for stable timings. 48 cores because the step
318-
# builds the bench targets twice in the bench profile.
319-
queue: hetzner-x86-64-dedi-48cpu-192gb
318+
# Dedicated hardware for stable timings, one shard per package with
319+
# bench targets, so `parallelism` should track that package count.
320+
# Extra shards exit as no-ops, fewer shards double up packages.
321+
queue: hetzner-x86-64-dedi-8cpu-32gb
320322

321323
- id: workload-replay
322324
topics: [iceberg, kafka, mysql, postgres, sql-server, ssh-tunnel]

ci/plugins/mzcompose/hooks/command

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -400,7 +400,6 @@ cleanup() {
400400

401401
if [ ! -s services.log ] \
402402
&& [ "$BUILDKITE_LABEL" != ":rust: cargo-fuzz" ] \
403-
&& [ "$BUILDKITE_LABEL" != ":rust: Cargo bench against ancestor" ] \
404403
&& [ "$BUILDKITE_LABEL" != "Maelstrom coverage of persist" ] \
405404
&& [ "$BUILDKITE_LABEL" != "Long single-node Maelstrom coverage of persist" ] \
406405
&& [ "$BUILDKITE_LABEL" != "Maelstrom coverage of txn-wal" ] \
@@ -413,7 +412,8 @@ cleanup() {
413412
&& [ "$BUILDKITE_LABEL" != ":rust: Miri test (full)" ] \
414413
&& [[ ! "$BUILDKITE_LABEL" =~ Terraform\ .* ]] \
415414
&& [[ ! "$BUILDKITE_LABEL" =~ Orchestratord\ .* ]] \
416-
&& [[ ! "$BUILDKITE_LABEL" =~ Cluster\ spec\ sheet.* ]]; then
415+
&& [[ ! "$BUILDKITE_LABEL" =~ Cluster\ spec\ sheet.* ]] \
416+
&& [[ ! "$BUILDKITE_LABEL" =~ ^:rust:\ Cargo\ bench\ against\ ancestor ]]; then
417417
echo "+++ services.log is empty, failing"
418418
if [[ $CI_ANNOTATE_ERRORS_RESULT -ne 0 ]]; then
419419
# Keep the retryable code (128 for GHCR/DockerHub trouble, 199 for a

test/cargo-bench/mzcompose.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,16 @@ def workflow_default(c: Composition, parser: WorkflowArgumentParser) -> None:
297297
if not head_targets:
298298
raise RuntimeError("no bench targets found")
299299

300+
packages = sorted({t.package for t in head_targets})
301+
# Sharding by package rather than by target keeps each shard to one
302+
# dependency closure per checkout.
303+
shard_packages = buildkite.shard_list(packages, lambda p: p)
304+
if not shard_packages:
305+
print("--- No bench packages assigned to this shard")
306+
return
307+
print(f"--- Bench packages in this shard: {', '.join(shard_packages)}")
308+
head_targets = [t for t in head_targets if t.package in shard_packages]
309+
300310
criterion_home = target_dir() / "criterion-compare"
301311
# Stale baselines from an earlier run would silently become the
302312
# comparison target, so start from an empty directory every time.
@@ -351,7 +361,7 @@ def workflow_default(c: Composition, parser: WorkflowArgumentParser) -> None:
351361
env, CARGO_TARGET_DIR=str(target_dir() / "cargo-bench-ancestor")
352362
)
353363
ancestor_targets, ancestor_manifests = load_targets(
354-
worktree, args.package, ancestor_env
364+
worktree, shard_packages, ancestor_env
355365
)
356366
# Building ancestor before HEAD lets both build phases run at
357367
# full core parallelism back to back, then the run phase below
@@ -397,7 +407,9 @@ def workflow_default(c: Composition, parser: WorkflowArgumentParser) -> None:
397407
failed = report.has_regressions or bool(current_failures)
398408
if buildkite.is_in_buildkite():
399409
buildkite.add_annotation(
400-
"error" if failed else "info", "Cargo bench results", markdown
410+
"error" if failed else "info",
411+
f"Cargo bench results ({', '.join(shard_packages)})",
412+
markdown,
401413
)
402414
try:
403415
spawn.runv(

0 commit comments

Comments
 (0)