Read README.md first for the architecture and the v3/v4
side-by-side situation. Then this file. The root CLAUDE.md
covers Rust style, test layout, commit conventions.
The v2 Vite/React site is still live at bench.vortex.dev but is deployed from
elsewhere — its source no longer lives in this repo. The migrator still reads
the v2 S3 dump as its historical source (see migrate/README.md).
The v3 deploy lives entirely under server/, migrate/, and ops/.
The operator runbook is ops/README.md.
- Wire shapes are a coordinated change.
server/src/records.rs,vortex-bench/src/v3.rs, and (until cutover)migrate/src/classifier.rsmust agree. Bumping a shape means changing all three plus the snapshot fixtures in one commit.SCHEMA_VERSIONis the version literal coupled acrossserver/src/schema.rs(in-repo source of truth),web/lib/schema-version.ts(the in-repo web mirror), andscripts/post-ingest.py(the monorepo CI ingest wrapper, which hardcodes it as a Python literal); a bump also coordinates with the monorepo producer wire shape invortex-bench/src/v3.rs. Bump in lockstep or every CI ingest run 400s/409s (400 if the server is ahead, 409 if the emitter is ahead). The server-side validation inrecords.rs+ingest.rsand the echo in/healthall consume the constant throughcrate::schema. The full versioned contract lives inCONTRACT.md. - Numeric
?n=is clamped to 1000;?n=allis the uncapped escape hatch. HTML routes hydrate from the materialized latest-100 shard artifact by default;?n=allis an explicit opt-in (chart-init.js's full-history zoom-out hop uses it once, and curl power users can request it). The numeric?n=path is bounded byMAX_NUMERIC_COMMIT_WINDOWinserver/src/api/window.rsas a DoS-protection floor againstcurl ...?n=99999999. If you need full history, use?n=all. Do NOT raise the numeric cap or remove it without thinking about the DoS surface. measurement_idis server-internal. Never put it on the wire. It is a deterministic hash overcommit_shaplus the dim tuple, computed inserver/src/db.rsand reused by the migrator via the same crate.- Don't write a server-side classifier for live ingest. The emitter produces v3-shape records directly; the migrator's classifier only exists to translate v2 records once and goes away after cutover.
- Don't reach for WASM. SSR + a thin hydration script in
server/static/chart-init.jsis the whole client. - v3 ingest is no longer best-effort in CI. The
Ingest results to v3 serverstep inbench.yml,sql-benchmarks.yml, andv3-commit-metadata.ymlno longer carriescontinue-on-error: true. A v3-server outage on a develop push now fails the bench workflow and triggers the existingincident.ioalert. The gate isvars.V3_INGEST_URL != ''so forks and unconfigured environments are unaffected. - Don't re-introduce a server-side commit cap on
?n=all.?n=allis the uncapped escape hatch (chart-init.js fetches it once for the zoom-out path); visual downsampling happens client-side via LTTB on the visible commit range only. Numeric?n=is clamped per the bullet above. Default fetches from chart-init.js use the materialized latest-100 shard artifact, not?n=all. - Don't refetch on every scope change. Once a chart's payload is in
memory, pan/zoom/slider/range-strip all rebuild in place via the
in-memory LTTB pass on the cached payload. The single exception is the
latest-100 to full-history zoom-out path: charts initially hydrate from
the materialized latest-100 group shard artifact (served from
/api/artifacts/{generation}/groups/{slug}/shards/{i}); when the user zooms past that window for the first time,chart-init.jslazy-fetches?n=allonce and replaces the latest-100 payload in place.
- Reverse predecessor walk in the tooltip.
payload.commits[]is sorted oldest-first by SQL -commits[0]is the oldest,commits[N-1]is the newest. For per-row delta the predecessor ofcommits[idx]is atidx - 1. We caught a regression where a "fix" flipped this toidx + 1; the original walk-backward direction is right. pointer-events: autoon the tooltip host. The tooltip is positioned at the cursor; making it pointer-interactive causes a flicker loop. Keep itpointer-events: noneand offset viatransform: translate(12px, 12px).changeevents on the slider. Useinputevents with a small throttle;changeonly fires on release and feels broken.
# Public-only run (read API + ingest only, admin routes 404):
INGEST_BEARER_TOKEN=dev cargo run -p vortex-bench-server
# With admin endpoints mounted on a separate loopback listener:
INGEST_BEARER_TOKEN=dev ADMIN_BEARER_TOKEN=dev \
cargo run -p vortex-bench-server
cargo nextest run -p vortex-bench-server -p vortex-bench-migrate
INSTA_UPDATE=auto cargo nextest run -p vortex-bench-server # update snapshotsFor the full env-var contract (admin bind, snapshot dir, extension dir,
logging spec, PaaS PORT fallback) see ops/config/vortex-bench.env.example
and the lib-level //! doc on server/src/main.rs.
For the migrator end-to-end against the real S3 dump:
cargo run -p vortex-bench-migrate -- run --output ./bench.duckdb
VORTEX_BENCH_DB=./bench.duckdb INGEST_BEARER_TOKEN=dev \
cargo run -p vortex-bench-server