Skip to content

Publish benchmark results in the style of the sibling LinksPlatform comparisons - #15

Merged
konard merged 12 commits into
mainfrom
issue-14-59fe569f2a3a
Sep 15, 2026
Merged

konard merged 12 commits into
mainfrom
issue-14-59fe569f2a3a

Conversation

@konard

@konard konard commented Sep 15, 2026 •

Copy link
Copy Markdown
Member

Summary

Brings the benchmarking CI/CD in line with Comparisons.PostgreSQLVSDoublets and Comparisons.Neo4jVSDoublets, so the benchmark results are shown, documented and recalculated automatically — and are readable in the repository without a local benchmark run.

Fixes #14

The benchmark CI was red, so nothing could be published

Rust Benchmark had been failing on every branch, including main, since 2026-04-14. Root cause: the workflow and rust/rust-toolchain.toml both asked for a rolling nightly, and two dependencies rely on unstable APIs that changed underneath the repository:

error[E0512]: cannot transmute between types of different sizes
  --> ethnum-1.5.2/src/error.rs:16

error[E0277]: the trait bound `Flow: Residual<()>` is not satisfied
  --> doublets-patched/dev-deps/data-rs/src/flow.rs:16

(run 35022182087, reproduced locally.)

ethnum relies on the layout of core::num::IntErrorKind; the patched doublets dev dependency implements the nightly-only Try/Residual traits. The toolchain is now pinned to nightly-2026-04-14 — the last nightly the benchmark built with — so it is updated deliberately instead of breaking silently.

Two more CI defects found on the way:

  • cargo bench ... | tee out.txt had no set -o pipefail, so a crashing benchmark produced a green run and an empty out.txt.
  • The benchmark jobs installed the toolchain without rustfmt/clippy, which rust/rust-toolchain.toml requires, so rustup added them lazily mid-job and failed with detected conflict: 'bin/cargo-fmt'.

What the results pipeline now does

rust/out.py was rewritten. It previously knew about only two of the five benchmarked backends — the two NonVolatile Doublets variants never appeared anywhere — and it produced charts but no table, which is why the README results placeholder was never filled in.

It now:

  • parses all 7 operations × 5 backends from criterion bencher output;
  • writes rust/results.md, annotating every Doublets cell against the SpacetimeDB baseline (12.3x faster / 1.2x slower — slower is reported honestly);
  • emits a provenance line (when, which Actions run, at which scale);
  • replaces the README section between <!--BENCHMARK_RESULTS_START--> / <!--BENCHMARK_RESULTS_END-->;
  • generates the linear and logarithmic charts and copies them into Docs/.

The workflow publishes all of that: the full run on main commits Docs/, README.md and rust/results.md as LinksPlatformBencher, exactly like the sibling repositories, and both the PR and full runs write the results table into the GitHub Actions job summary.

Tests

rust/test_out.py — 21 unit tests covering parsing (including thousand separators, missing files and noise lines), speedup formatting, the table, provenance, README replacement (including regex-unsafe table content such as \g<0>) and the CLI entry point.

A new results-pipeline CI job runs them and gates both benchmark jobs, so a 3 hour benchmark can no longer finish only to fail while publishing its results.

cd rust && python3 -m unittest test_out -v
# Ran 21 tests — OK

Verified locally

  • cargo clippy --all-targets and cargo fmt --all -- --check pass on the pinned toolchain
  • the SpacetimeDB WASM module builds and publishes
  • a real benchmark run against a local SpacetimeDB 2.10.1 server produces the table and both charts

Also

  • removed rust/rust_out, a 4.2 MB compiled binary committed by accident
  • dropped the stale top-level spacetime-module/** path filter (the module lives in rust/spacetime-module)
  • README: reference-style Results section with both charts served from Docs/ via permanent raw links, plus a Conclusion explaining the measured gap

Adding .gitkeep for PR creation (default mode).
This file will be removed when the task is complete.

Issue: #14
@konard konard self-assigned this Sep 15, 2026
The Rust Benchmark workflow has been failing since 2026-04-14 on every
branch, including main. Two independent compile errors on current rolling
nightly:

    error[E0512]: cannot transmute between types of different sizes
      --> ethnum-1.5.2/src/error.rs:16

    error[E0277]: the trait bound `Flow: Residual<()>` is not satisfied
      --> doublets-patched/dev-deps/data-rs/src/flow.rs:16

Both come from unstable APIs that changed under us: `ethnum` relies on the
layout of `core::num::IntErrorKind`, and the patched `doublets` dev
dependency implements the nightly-only `Try`/`Residual` traits.

Pin the toolchain to nightly-2026-04-14 (the last nightly the benchmark
built with) in rust/rust-toolchain.toml, so it is updated deliberately
instead of silently breaking. Verified locally: `cargo clippy --all-targets`
and `cargo fmt --all -- --check` pass, and the SpacetimeDB WASM module
builds.
rust/out.py only knew about two of the five benchmarked backends, so the
two NonVolatile Doublets variants never appeared anywhere, and it produced
charts but no table, which is why the README results placeholder was never
filled in.

Rewrite it in the style of the sibling benchmarks
(Comparisons.Neo4jVSDoublets, Comparisons.PostgreSQLVSDoublets):

- parse all 7 operations x 5 backends from criterion bencher output
- emit results.md with each Doublets cell annotated relative to the
  SpacetimeDB baseline ("12.3x faster" / "1.2x slower")
- emit a provenance line (when, which Actions run, at which scale)
- replace the README section between the benchmark result markers
- generate linear and logarithmic charts and copy them into Docs/

Add rust/test_out.py: 21 unit tests covering parsing, speedup formatting,
the table, provenance, README replacement (including regex-unsafe content)
and the CLI entry point.
rust/rust_out is a 4.2 MB ELF executable that is not referenced by any
script, workflow or document.
Brings the benchmarking CI in line with Comparisons.Neo4jVSDoublets and
Comparisons.PostgreSQLVSDoublets, so the numbers are visible in the
repository without a local benchmark run (issue #14):

- pin the toolchain via `env.toolchain`, matching rust/rust-toolchain.toml
- `set -o pipefail` before `cargo bench ... | tee out.txt`; without it a
  crashing benchmark still produced a green run and an empty out.txt
- new `results-pipeline` job runs the rust/out.py unit tests and gates both
  benchmark jobs, so a 3 hour benchmark cannot fail only while publishing
- the full run regenerates results.md, copies both charts into Docs/,
  rewrites the README results section and commits it as LinksPlatformBencher
- both runs write the results table into the job summary, and upload
  results.md alongside out.txt and the charts

README gets a reference-style Results section: linear and logarithmic
charts served from Docs/ via permanent raw links, an auto-updated table
between result markers, and a Conclusion explaining the measured gap.
The module lives in rust/spacetime-module, which rust/** already covers.
rust/rust-toolchain.toml requires both components, so rustup tried to add
them lazily on the first cargo invocation in the benchmark jobs and failed:

    error: failed to install component: 'rustfmt-preview-x86_64-unknown-linux-gnu',
    detected conflict: 'bin/cargo-fmt'

(run 35026687311, "Benchmark (PR validation)" / "Build SpacetimeDB module
(WASM)"). Install them together with the toolchain instead, as the test job
already does.
@konard konard changed the title [WIP] Benchmarking CI/CD must be fully done in the same style as in our previous benchmarks Publish benchmark results in the style of the sibling LinksPlatform comparisons Sep 15, 2026
The copies in rust/ are build output; they stay as workflow artifacts
instead of being committed twice.
Criterion prints its own errors to stdout between `test <id> ... ` and
`bench: ...` (criterion-0.3.6/src/report.rs:740,756 and
macros_private.rs:36). A partially restored target/criterion/<id>/<size>/base
directory is enough to splice "Criterion.rs ERROR: ..." into the middle of a
record and push `bench:` onto the next line, which made out.py parse zero
measurements and fail the run with "No benchmark data found in out.txt"
(CI run 35028280108).

- out.py parses the whole file with a pattern that tolerates anything between
  the two halves of a record, without borrowing the next benchmark's number
- the benchmark steps wipe target/criterion first, so no stale comparison
  state can pollute stdout in the first place
- artifacts are uploaded even when the results step fails, and out.py prints
  the tail of the unparsable input
- regression tests in test_out.py plus a standalone reproduction script
The repository already has a lowercase `docs/` directory, so a second
top-level `Docs/` would collide on case-insensitive filesystems (the macOS
job checks the repository out on one). Publish the charts under the existing
directory instead.
@konard

konard commented Sep 15, 2026

Copy link
Copy Markdown
Member Author

Working session summary

Root cause of the last CI failure found and fixed. Status while the long-running jobs finish:

What broke run 35028280108: Criterion prints its own errors to stdout (criterion-0.3.6/src/macros_private.rs:36), and it does so between the two halves of a bencher record (print!("test {} ... ") at src/report.rs:740, println!("bench: ...") at :756). A partially restored target/criterion/<id>/<size>/base directory made every record look like:

test create/SpacetimeDB/10 ... Criterion.rs ERROR: error: Failed to access file ".../base/sample.json": No such file or directory (os error 2)
bench:    24992765 ns/iter (+/- 1790323177)

so the single-line regex in out.py matched nothing → No benchmark data found in out.txt → exit 1. Verified by replaying the CI text through the old parser ({}) and the new one (both values recovered): experiments/reproduce-criterion-error-interleaving.py.

Fixes pushed (6af08a0, a50fe91, c9dacb0):

  • rust/out.py parses the whole file with a pattern that tolerates interleaved output, without borrowing the next benchmark's number; prints the tail of unparsable input on failure.
  • Both benchmark steps `rm -rf target/cri
Rest of the working session summary (586 characters)
terion` first, so stale comparison state can't pollute stdout.
- Artifacts upload with `if: always()`, so a failed results step still preserves `out.txt`.
- 3 new regression tests (26 total, all passing locally).
- Charts moved from `Docs/` to `docs/benchmarks/` — the repo already has a lowercase `docs/`, and the two would collide on the case-insensitive filesystem the macOS job checks out on.

Still running: CI on the new commits, and the full-scale local benchmark (5/35 measurements; the SpacetimeDB groups need ~16 min each) that will seed the committed charts and README table.

This summary was automatically extracted from the AI working session output.

@konard
konard marked this pull request as ready for review September 15, 2026 22:08
@konard

konard commented Sep 15, 2026

Copy link
Copy Markdown
Member Author

🤖 Solution Draft Log

This log file contains the complete execution trace of the AI solution draft process.

💰 Cost: $12.549547

📊 Context and tokens usage:

Claude Opus 5: (3 sub-sessions)

  1. 117.0K / 1M (12%) input tokens, 29.8K / 128K (23%) output tokens
  2. 115.0K / 1M (12%) input tokens, 34.4K / 128K (27%) output tokens
  3. 92.3K / 1M (9%) input tokens, 24.6K / 128K (19%) output tokens

Total: (5.9K new + 293.5K cache writes + 13.7M cache reads) input tokens, 109.5K output tokens, $12.549547 cost

🤖 Models used:

  • Tool: Anthropic Claude Code
  • Requested: opus (claude-opus-5)
  • Thinking level: high (~23999 tokens)
  • Model: Claude Opus 5 (claude-opus-5)

📎 Log file uploaded as Gist (5652KB)


Now working session is ended, feel free to review and add any feedback on the solution draft.

@konard
konard merged commit 972b177 into main Sep 15, 2026
16 checks passed
@konard

konard commented Sep 15, 2026

Copy link
Copy Markdown
Member Author

🎉 Auto-merged

This pull request has been automatically merged by hive-mind.

  • All CI checks have passed

Auto-merged by hive-mind with --auto-merge flag

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.

Benchmarking CI/CD must be fully done in the same style as in our previous benchmarks

1 participant