Skip to content

refactor(benchmark): split the comparative harness from the CI one - #442

Merged
rmanibus merged 2 commits into
refactor/bench-reporting-in-gofrom
refactor/split-benchmark-scripts
Aug 4, 2026
Merged

refactor(benchmark): split the comparative harness from the CI one#442
rmanibus merged 2 commits into
refactor/bench-reporting-in-gofrom
refactor/split-benchmark-scripts

Conversation

@rmanibus

@rmanibus rmanibus commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Summary

Stacked on #435 — base is refactor/bench-reporting-in-go, so review that first. GitHub retargets this to main automatically once #435 merges. Per repo convention a stacked PR gets no CI run.

What CI was actually doing

benchmark.yml invoked run.sh local local cloudstic — the third argument filters to cloudstic — on a runner where restic, borg and duplicacy are not installed. The comparison never happened in CI. It was a 615-line cross-tool harness executing 64 lines of cloudstic-specific code.

Why that matters beyond the dead paths

The dead paths are cosmetic. The structural problem is that the two jobs have conflicting requirements:

  • A comparison needs a dataset that is fair across four tools, and can only measure what all of them expose.
  • Tracking this product over time needs a dataset that stresses this design, and is free to report numbers no other tool has.

Sharing one script meant a change made for cross-tool fairness — adjusting the dataset so borg is not unfairly penalised, say — would silently move the numbers a trend line is built on.

The split

Script Job
compare.sh (was run.sh) Cloudstic vs restic/borg/duplicacy. Manual only.
cloudstic.sh (new) This product on its own terms. What the Benchmark workflow runs.
lib.sh (new) Measurement mechanics both use.

lib.sh holds run_bench, repo-size tracking and the table — the how. The datasets deliberately stay separate, which follows directly from the argument above: sharing them would reintroduce exactly the coupling the split removes.

cloudstic.sh measures things a comparison cannot, and the first run shows why they are worth having:

Operation Time · Peak RSS Repo added
Initial Backup 0.7s · 534.4 MB 344.0 MB
Incremental (1 File Changed) 0.27s · 127.3 MB 12 KB
Incremental (1000 Changed) 0.27s · 144.2 MB 1.3 MB
Deduplicated Backup 0.54s · 343.4 MB 40 KB
Prune 0.13s · 153.1 MB -320 KB

Copying 340 MB of already-stored data adds 40 KB, and a thousand changed files costs the same wall time as one — both properties the design claims and neither previously measured. The script also reports a stored-to-logical ratio (2.18x on this dataset).

Report changes

The table grows a Repo added column for single-tool runs, and the prose no longer says "each tool over the same dataset" when there is one. The column is deliberately withheld across tools, where it would invite comparing byte counts between different repository formats — that is not the same measurement twice.

Verification

  • ./scripts/benchmark/cloudstic.sh — full run, output above
  • ./scripts/benchmark/compare.sh local local cloudstic — still works after the lib.sh extraction, output unchanged
  • /bin/bash -n on all three scripts under bash 3.2.57
  • go test -race ./... passes; golangci-lint run ./... — 0 issues
  • npx markdownlint-cli2 '**/*.md' — 0 issues

Trigger

The Benchmark workflow had fired once since April 2nd, so the split was worth roughly nothing on its own. It now also runs on a pull request carrying the benchmark label, matching Memory scaling.

Same label rather than a second one, deliberately: labelling a PR is the act of asking for performance data, and wanting the memory curve but not the throughput numbers is not a real case. synchronize is included so pushing to an already-labelled PR re-measures without another click, and any other label leaves both jobs skipped.

CI was running `run.sh local local cloudstic` — a 615-line cross-tool harness
filtered down to one tool, on a runner where restic, borg and duplicacy are
not installed. The comparison never happened there.

The dead paths were the smaller problem. The real one is that the two jobs
have conflicting requirements: a comparison needs a dataset that is fair
across four tools and can only measure what all of them expose, while
tracking this product over time needs a dataset that stresses this design and
is free to report numbers no other tool has. Sharing one script meant a change
made for cross-tool fairness would silently move the numbers CI trends on.

- run.sh becomes compare.sh, which is only ever run by hand.
- cloudstic.sh is new and is what the Benchmark workflow runs. It measures
  what a comparison cannot: incremental cost at one changed file and at a
  thousand, deduplication of already-stored data, and the stored-to-logical
  ratio.
- lib.sh holds the measurement mechanics both use. The datasets deliberately
  stay separate — sharing them would reintroduce exactly the coupling this
  split removes.

The report grows a Repo added column for a single-tool run, which is where
deduplication shows up: copying 340 MB of already-stored data adds 40 KB.
That column is withheld across tools, where it would invite comparing byte
counts between different repository formats.

Trigger is unchanged: manual dispatch. Worth noting the workflow has fired
once since April, so the split is only worth what someone runs.
@rmanibus rmanibus added refactor area/ci CI workflows, coverage, and automation labels Aug 4, 2026
…memory

The Benchmark workflow was dispatch-only, and had fired once since April. It
now also runs on a pull request carrying the `benchmark` label, matching what
Memory scaling already does.

Deliberately the same label rather than a second one: labelling a PR is the
act of asking for performance data, and wanting the memory curve but not the
throughput numbers is not a real case. `synchronize` is included so pushing to
an already-labelled PR re-measures without another click, and the job is
skipped for any other label so an unrelated one does not start it.
@rmanibus rmanibus added the benchmark Run the memory-scaling benchmark on this PR label Aug 4, 2026
@rmanibus
rmanibus merged commit 4895ed3 into refactor/bench-reporting-in-go Aug 4, 2026
22 checks passed
@rmanibus
rmanibus deleted the refactor/split-benchmark-scripts branch August 4, 2026 08:25
@codecov

codecov Bot commented Aug 4, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.46154% with 3 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/benchreport/render.go 90.47% 1 Missing and 1 partial ⚠️
internal/benchreport/report.go 80.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

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

Labels

area/ci CI workflows, coverage, and automation benchmark Run the memory-scaling benchmark on this PR refactor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant