Skip to content

AI WIP | End-to-end sync benchmark (evidence for #3584 page-size change) - #3656

Draft
markovejnovic wants to merge 10 commits into
mainfrom
perf/sync-page-size
Draft

AI WIP | End-to-end sync benchmark (evidence for #3584 page-size change)#3656
markovejnovic wants to merge 10 commits into
mainfrom
perf/sync-page-size

Conversation

@markovejnovic

Copy link
Copy Markdown
Contributor

What this is

An end-to-end benchmark for record sync throughput. A real atuin-client drives diffoperationssync_remote against a real in-process atuin-server (temp-file SQLite) over a real loopback TCP socket. It sweeps page size (100 vs 1000) × injected RTT (0 / 20 / 100ms) over a 10,000-record corpus, in both directions.

This builds the evidence for #3584 (raise the sync page size 100 → 1000, cutting HTTP round-trips 10×). It does not itself change the page sizecrates/atuin-client/src/record/sync.rs is untouched.

Why the injected latency

Loopback RTT is ~zero, and RTT is exactly the cost a larger page eliminates. So the server's axum router is wrapped in middleware that sleeps a configurable RTT per request. Without it the benchmark measures SQLite and serialization instead of the thing under test.

The headline result proves the point:

RTT page=100 page=1000
0ms 213.4ms 196.1ms ~8% — noise
20ms 2.628s 407.1ms ~6×
100ms 10.8s 1.345s ~8×

(upload; download is comparable — see the results doc). At rtt=0 the two page sizes are within noise. A pure-localhost benchmark would have reported #3584's change as a rounding error.

The finding that should gate the change

axum's default 2 MB request body limit bounds page size. handlers::v0::record::post extracts Json<Vec<Record<EncryptedData>>> and the server never overrides DefaultBodyLimit; max_record_size is per-record (defaults to 1 GB), so nothing else bounds the request. At ~600 bytes/record page=1000 is ~600 KB and safe — but a user with 1–2 KB commands would send 1–2 MB and could hit a 413. Before raising the page size, bound the request by bytes, not record count (or raise the server limit deliberately).

Shape of the change

  • crates/atuin-client/benches/sync.rs — the upload/download benchmarks (divan).
  • crates/atuin-client/benches/_util/{server,record}.rsBenchServer (in-process server + latency middleware) and shared record generation.
  • crates/atuin-client/tests/bench_harness.rs — real tests for the harness (a benchmark on a broken harness produces confident wrong numbers).
  • crates/atuin-server/src/lib.rs — the one production change: make_router is now pub so a caller can layer middleware onto the router. Narrowest available seam (mod router is private).
  • crates/atuin-server/tests/make_router.rs — covers that.
  • docs/superpowers/plans/2026-07-16-e2e-sync-benchmark*.md — the plan and the full results write-up (caveats, follow-ups, reproduction command).

atuin-client gains atuin-server* + axum as path-only dev-dependencies (no version key, so they're stripped from the published manifest and don't affect the release publish order or what ships to users). Not a cycle — atuin-server doesn't depend on atuin-client.

How to run

cargo bench -p atuin-client --bench benchmarks -- sync 2>/tmp/atuin-bench.stderr

Redirecting stderr is not optional: sync_upload/sync_download draw an indicatif progress bar that indicatif suppresses only when stderr is not a TTY, which would otherwise add page-count-proportional work to exactly the comparison being made.

Status

Draft / WIP. Full caveats (SQLite vs production Postgres, RTT-only latency model, synthetic payloads) and follow-ups are in the results doc.

The zero_latency_does_not_delay test asserted a hard 50ms wall-clock upper
bound on a request that received Duration::ZERO latency injection. This bound
flakes under loaded machines. Furthermore, tokio::time::sleep(Duration::ZERO)
returns immediately by design, so this test never actually validated the
is_zero() short-circuit guard in the latency middleware — removing that guard
would not cause this test to fail.

Delete the test, keeping the correct (but untested) guard in the middleware.
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