Skip to content

Config-driven benchmark campaign runner for the full-history bench subcommands #896

Closed

Description

@marwen-abid

TL;DR

Add tooling that runs a full benchmark campaign from one config file with one command. The runner builds a pinned binary, prepares every dataset, runs the configured ingest and query loops across datasets, chunks, and repetitions, records provenance, bundles the results, and optionally publishes them. A companion script bootstraps the bench box; another safeguards the bundle to object storage.

Why

The bench subcommands run one leg at a time — one ingest or query pass against one source. A campaign runs many legs across several datasets and chunks, repeated for statistics, and the results only compare if every leg used the identical binary, dataset preparation, and flags. Doing that by hand is slow and error-prone. This tooling makes a campaign reproducible: a config file names the binary, the datasets, and the loops, and the runner does the rest the same way every time.

The runner — campaign.sh

One command runs the whole campaign:

BENCH=/mnt/nvme/bench ./scripts/bench-devbox/campaign.sh my-campaign.cfg [--dry-run]

Steps, in order:

  1. Validate. The config is a sourced bash fragment. The runner rejects any unknown key and validates every value (enums, integers, Go durations, chunk lists, dataset entries) before it builds or runs anything.
  2. Build the binary under test. REF is built into a versioned binary stellar-rpc-<sha>, then the original checkout is restored. REF selects only the binary — nothing else. A dirty work tree is refused when REF is set, so the built commit is exact. The build is skipped if that binary already exists.
  3. Prepare each dataset to a local cold pack root. Four source kinds converge on the same on-disk shape (ledgers/, events/, txhash/):
    • packs-local — a local cold pack root, used in place.
    • packs-gs — a gs:// prefix, fetched once into $BENCH/golden/<name>/ and reused.
    • bsb-s3 — an S3 bucket path; an untimed cold backfill materializes the golden root.
    • fixture — a generated fixture pack plus an untimed cold ingest materialize the golden root.
      Golden datasets are fetched or built once and reused across runs. Removing $BENCH/golden/<name> forces a re-fetch.
  4. Run the configured loops. Per (dataset, chunk), RUNS repetitions, each a fresh process with its own --out directory:
    • INGEST=cold|both → bench-ingest cold (scratch wiped before each run).
    • INGEST=hot|both → bench-ingest hot, optionally paced at CLOSE_INTERVAL. The last run's hot DB is kept for the hot query suite.
    • QUERY=yes → bench-query cold against each frozen pack root, and bench-query hot against the kept hot DB (hot query needs a hot ingest, so it runs only when INGEST is hot or both).
  5. Record provenance and bundle. Results land in $BENCH/results/<run_id>/, where run_id = <NAME>-<sha>-<stamp>, alongside the config, binary.txt, machine-metadata.txt, and metadata.json. The directory is tarred to durable storage so the bundle survives an instance stop.
  6. Publish (optional). When PUBLISH_URI is set, publish.sh uploads the bundle. A publish failure is not a benchmark failure — the data is already safe locally, so it exits with the exact retry command instead of corrupting the "campaign done" signal.

--dry-run prints every command with resolved paths and flags, and builds, downloads, and runs nothing.

Config format

The config sets only documented keys:

Key Default Meaning
NAME — (required) Campaign name; charset [A-Za-z0-9._-]; part of the run_id.
REF current checkout Git ref to benchmark. Built into a versioned binary; checkout restored after.
INGEST — (required) cold | hot | both | none.
QUERY — (required) yes | no. Query-hot needs INGEST hot or both.
CLOSE_INTERVAL 0 Hot pacing interval, e.g. 2s. 0 = unpaced catch-up.
RUNS 5 Repetitions per (dataset, chunk) cell.
QC 1,4,16 Query concurrency sweep (used when QUERY=yes).
COLD_ITERS / HOT_ITERS 100 / 200 Query iteration counts.
WORKERS 1 bench-ingest cold --workers.
HOT_NUM_LEDGERS 0 Cap hot ingest at N ledgers. 0 = whole range.
PUBLISH_URI empty gs:// or s3:// root to publish the bundle to. Empty = keep local.
DATASETS — (required) Array of name|kind|location|chunks entries; chunks is a space-separated chunk-ID list.

Box bootstrap — bootstrap.sh

Idempotent setup for an EC2 box with a local NVMe instance store (m6id class, Ubuntu 24.04). It formats and mounts the instance store, runs an fsync honesty probe and warns if the disk absorbs fsync (hot-commit numbers would be fiction on such a disk — the reason this box class exists), installs the toolchain and native libs, builds the daemon, and race-tests the bench package. It is safe to re-run after a stop/start, which wipes the instance store; the runner re-fetches golden packs.

Publishing — publish.sh

Safeguards a finished bundle to <dest-root>/<run_id>/. It dispatches by scheme (gs:// via gcloud storage rsync, s3:// via aws s3 sync). Published runs are immutable: it refuses a destination that already holds objects unless --force is given. --dry-run prints the cloud commands without running them.

What this delivers

  • One config file plus one command runs a full ingest and query campaign end to end.
  • Strict config validation and a --dry-run that prints the exact command plan.
  • Ref-pinned, versioned binary so results are reproducible and attributable to a commit.
  • Four dataset source kinds behind one interface, with fetch-once golden reuse.
  • Fresh process and output directory per run; scratch and hot DBs reset between runs.
  • Self-describing bundle: config, binary identity, machine metadata (with fsync probe), and a machine-readable metadata.json (run id, config, datasets, hardware).
  • Idempotent box bootstrap and an immutable, scheme-agnostic publish step.

Related

Activity

  1. self-assigned this
    on Jul 23, 2026
  2. moved this from To Do to In Progress in Platform Scrumon Jul 23, 2026
  3. marwen-abid commented on Aug 7, 2026

    @marwen-abid
    ContributorAuthor

    Shipped, closing manually:

  4. moved this from In Progress to Done in Platform Scrumon Aug 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

Relationships

None yet

Development

No branches or pull requests

Issue actions