A/B the oximeter collector under synthetic load. Topology per measured run, all on one illumos box:
ch-dev (ClickHouse) <--inserts-- oximeter standalone collector (the arm)
^ HTTP pull
N x oximeter-load-test producers -----------------------+
An arm is a label plus a prebuilt collector binary. Building is separate from measuring, so a build failure can't abort a matrix and a run measures exactly the bytes you built. The loadgen and ch-dev are shared across arms, never variables.
| file | scope |
|---|---|
build.sh |
refs -> oximeter.<arm> + shared tools (TOOLS_REF) + manifest.tsv |
orchestrate.sh |
lifecycle: spawn/teardown (sourced) |
measure.sh |
observation: sample a pid, query ClickHouse, derive metrics (sourced) |
run.sh |
one cell = one arm x one shape x one round, its own process |
ab.sh |
the matrix: shapes x rounds x arms, plus aggregation |
Shell and platform tools only — no runtime to install. clickhouse on PATH
(omicron ships one in out/clickhouse/); mdb and dtrace if you want the
illumos introspection, which degrades to a no-op without them.
# 1. build (tens of minutes; run under nohup on the box)
nohup ./build.sh ~/bench-tree ~/bench/bin \
main=main cache=jmcarp/oximeter-field-cache > ~/bench/build.log 2>&1 &
# 2. run the matrix
export PATH=$HOME/omicron/out/clickhouse:$PATH # for the clickhouse client
ORCH_CHDEV_BIN=~/bench/bin/ch-dev \
ORCH_LOADGEN_BIN=~/bench/bin/oximeter-load-test \
nohup ./ab.sh ~/bench/bin ~/bench/out main cache > ~/bench/driver.log 2>&1 &
# 2b. restart a matrix that died partway (box reboot, ^C) without re-measuring
# the cells that already finished
RESUME=1 ORCH_CHDEV_BIN=... ORCH_LOADGEN_BIN=... \
nohup ./ab.sh ~/bench/bin ~/bench/out main cache >> ~/bench/driver.log 2>&1 &
# or a single cell, with loadgen flags passed through verbatim
ORCH_CHDEV_BIN=... ORCH_LOADGEN_BIN=... OFFERED=60000 \
./run.sh ~/bench/bin/oximeter.cache /tmp/one 60 2 \
--series-cardinality 1000 --interval-seconds 1 \
--field component_kind:String:10 --field component_id:Uuid:1000Outputs per cell: samples.tsv (epoch, rss_kb, cpu_time), counts.tsv,
summary.tsv, meta.tsv, component logs, and mdb/dtrace artifacts. ab.sh
writes rows.tsv plus a mean +/- half-range aggregate.
ab.shneeds the realistic loadgen (--field,--series-cardinality,--batches-per-poll). Older builds use--label-cardinalityand will reject the shape table.run.shforwards whatever you give it, so it works with either.- Field pools must multiply to >=
--series-cardinality, or the loadgen refuses to start. MEASURE_PROFILEis a mode, not a flag.0(default) takes mdb umem/depot snaps;1takes a dtrace CPU fold. Never both: mdb-pand dtrace-pboth grab the target through/proc, and running them together wedges the collector.- ClickHouse readiness IS waited for; producers' is not. The standalone
collector
.unwrap()s its DB connection (bin/oximeter.rs) and panics if ClickHouse isn't up, soorch_start_clickhouseblocks until it's listening. Producers need no wait — each registers with Nexus from a background task that retries with backoff. Startup ramp still lands inside the measurement window, which is why RSS is a p50 over the window's tail (MEASURE_TAIL_FRAC). run.shsetsset -m. Shells start background children with SIGINT ignored, and ch-dev removes its datadir only on SIGINT. Without job control, teardown falls through to-9and leaks the datadir into swap-backed tmpfs.orch_start_clickhousealso reaps/tmp/ch-dev-*on start (guarded on no live server), since runs that die before teardown leak regardless — 59 orphaned dirs cost 14 GB of tmpfs once.- RSS is a max-statistic: its MEAN converges but its SPREAD does not shrink.
It tracks the allocator's high-water arena, set by whichever startup transient
aligned worst — and collection start times are jittered, so that alignment is
random per run. Measured on identical binaries/shape, per-run RSS varies by
+/-270 MB on an unmodified collector while the 3-round mean is stable to within
3% across three separate sessions. So report means, expect wide
+/-, and do not add rounds hoping the spread tightens. (Verified not to be/tmpaccumulation: spread was the same with 14 GB of leaked tmpfs and with none.) CPU and samples/cpu-sec are means and near noise-free (coresrepeated to +/-0.00 across rounds). Trust CPU for small effects; treat RSS deltas under ~20% as unresolved. - A dead collector fails the run — the sampling loop notices the pid is gone, so a bad binary path or a mid-run crash surfaces immediately rather than as quiet zeroes.
rows_per_suses the sampled window (N samples span N-1 seconds), so it reads ~1/dur high; negligible at dur=300.cpu_coresis exact.
Laptop->box sync/poll glue is intentionally not here; keep it in scratch/.