Skip to content

Commit c54ce7e

Browse files
connortsui20claude
andauthored
Benchmarks Website V3: Admin and Auto-Deploy (#7849)
## Summary http://ec2-18-219-54-101.us-east-2.compute.amazonaws.com:3000/ Hopefully the Benchmarks V3 finale. The website is one Rust binary + one DuckDB file managed by systemd: `vortex-bench-deploy.timer` polls `origin/develop` every 60s, rebuilds + atomic-swaps the binary on website-touching commits, `/health`-verifies, rolls back on failure. `vortex-bench-backup.timer` fires hourly, calls a new loopback-only `POST /api/admin/snapshot` (per-table Vortex export under one `READ ONLY` transaction), `tar czf`s the result, and uploads to `s3://vortex-benchmark-results-database/v3-backups/<ts>.tar.gz`. Admin endpoints (snapshot + read-only `POST /api/admin/sql`) sit on a separate `ADMIN_BEARER_TOKEN`-gated listener that refuses to start on a non-loopback bind. v2 keeps shipping unchanged via the existing publish workflow until DNS flips. Operator runbook (one-time install on a fresh EC2 host, day-to-day, recovery): [`benchmarks-website/ops/README.md`](benchmarks-website/ops/README.md). On a clean box, `git clone` the repo, edit `INGEST_BEARER_TOKEN` + `ADMIN_BEARER_TOKEN` in `/etc/vortex-bench.env`, `./benchmarks-website/ops/install.sh`. <details> <summary>Admin cheat sheet (generated by claude)</summary> ### Status — "what's the state right now?" ```bash systemctl status vortex-bench-server # main service status + recent log lines systemctl list-timers vortex-bench-deploy.timer vortex-bench-backup.timer # next + last fire of each timer curl -fsS http://127.0.0.1:3000/health | jq # live build_sha, row counts, db path cat /var/lib/vortex-bench/last-deployed-sha # SHA the deploy timer thinks is live readlink /var/lib/vortex-bench/bin/vortex-bench-server # symlink target = versioned binary ``` ### Manual triggers — "do something now" ```bash /var/lib/vortex-bench/ops/restart.sh # restart binary in place, show before/after /var/lib/vortex-bench/ops/force-rebuild.sh # rebuild origin/$DEPLOY_BRANCH + restart sudo systemctl start vortex-bench-deploy.service # run deploy IF origin moved (no-op otherwise) sudo systemctl start vortex-bench-backup.service # snapshot to S3 right now /var/lib/vortex-bench/ops/migrate.sh run --output \ /var/lib/vortex-bench/bench.duckdb # re-run v2→v3 migration /var/lib/vortex-bench/ops/inspect.sh "SELECT COUNT(*) FROM commits;" # read-only SQL ``` ### Logs — "what happened?" ```bash journalctl -fu vortex-bench-server # live server logs journalctl -fu vortex-bench-deploy.service # watch a deploy in progress journalctl -u vortex-bench-deploy.service --since '15 min ago' --no-pager journalctl -u vortex-bench-backup.service --since '4 hours ago' --no-pager journalctl -u vortex-bench-server --since '5 min ago' --no-pager ``` ### Pause / resume the autopilot — "stop fiddling for a minute" ```bash sudo systemctl stop vortex-bench-deploy.timer # pause auto-deploys sudo systemctl start vortex-bench-deploy.timer # resume sudo systemctl stop vortex-bench-backup.timer # pause hourly snapshots sudo systemctl start vortex-bench-backup.timer # resume sudo systemctl stop vortex-bench-server # take the site down sudo systemctl start vortex-bench-server # bring it back ``` ### Config changes — "edit settings" ```bash sudo $EDITOR /etc/vortex-bench.env # tokens, DEPLOY_BRANCH, S3 prefix, etc. sudo systemctl restart vortex-bench-server # pick up server-side env changes # (no rebuild — restart.sh shows new pid) /var/lib/vortex-bench/ops/force-rebuild.sh # pick up build-time env changes ``` ### Recover from S3 — "restore a backup" ```bash aws s3 ls s3://vortex-benchmark-results-database/v3-backups/ | tail LATEST=$(aws s3 ls s3://vortex-benchmark-results-database/v3-backups/ | tail -1 | awk '{print $4}') aws s3 cp "s3://vortex-benchmark-results-database/v3-backups/$LATEST" /tmp/ cd /tmp && tar xzf "$LATEST" ts="${LATEST%.tar.gz}" sudo systemctl stop vortex-bench-server sudo -u ec2-user rm -f /var/lib/vortex-bench/bench.duckdb \ /var/lib/vortex-bench/bench.duckdb.wal duckdb /var/lib/vortex-bench/bench.duckdb <<EOF INSTALL vortex; LOAD vortex; .read /tmp/${ts}/schema.sql INSERT INTO commits SELECT * FROM read_vortex('/tmp/${ts}/commits.vortex'); INSERT INTO query_measurements SELECT * FROM read_vortex('/tmp/${ts}/query_measurements.vortex'); INSERT INTO compression_times SELECT * FROM read_vortex('/tmp/${ts}/compression_times.vortex'); INSERT INTO compression_sizes SELECT * FROM read_vortex('/tmp/${ts}/compression_sizes.vortex'); INSERT INTO random_access_times SELECT * FROM read_vortex('/tmp/${ts}/random_access_times.vortex'); INSERT INTO vector_search_runs SELECT * FROM read_vortex('/tmp/${ts}/vector_search_runs.vortex'); EOF sudo systemctl start vortex-bench-server ``` The 5 commands you'll actually use day-to-day: `restart.sh`, `force-rebuild.sh`, `inspect.sh`, `journalctl -fu …`, `curl /health | jq`. Everything else is rare. </details> --------- Signed-off-by: Claude <noreply@anthropic.com> Signed-off-by: Connor Tsui <connor.tsui20@gmail.com> Signed-off-by: Connor Tsui <connor@spiraldb.com> Co-authored-by: Claude <noreply@anthropic.com>
1 parent f852d72 commit c54ce7e

64 files changed

Lines changed: 6072 additions & 585 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/bench.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,6 @@ jobs:
107107
108108
- name: Ingest results to v3 server
109109
if: vars.V3_INGEST_URL != ''
110-
continue-on-error: true
111110
shell: bash
112111
env:
113112
INGEST_BEARER_TOKEN: ${{ secrets.INGEST_BEARER_TOKEN }}

.github/workflows/ci.yml

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ jobs:
255255
await github.rest.checks.update({
256256
...context.repo,
257257
check_run_id: Number(url.split('/').pop()),
258-
output: { title: `${failed.join(', ')} failing`, summary: '' },
258+
output: { title: `${failed.join(', ')} - failing`, summary: '' },
259259
});
260260
}
261261
core.setFailed(`Lint failed: ${failed.join(', ')}`);
@@ -370,6 +370,19 @@ jobs:
370370
if: matrix.os != 'windows-x64'
371371
run: |
372372
cargo nextest run --cargo-profile ci --locked --workspace --all-features --no-fail-fast --exclude vortex-bench --exclude xtask --exclude vortex-sqllogictest
373+
- name: vortex-bench-server admin snapshot tests (Linux only - network-dependent)
374+
# The /api/admin/snapshot tests INSTALL+LOAD the vortex DuckDB
375+
# core extension from extensions.duckdb.org on first call. They
376+
# are #[ignore]'d by default so `cargo test` works in offline
377+
# environments (sandboxed CI, local dev without network). This
378+
# step runs them explicitly on the Linux runners, which DO have
379+
# outbound network, so the entire backup contract is covered
380+
# in CI before merge. macOS/arm64 also exercises them - same
381+
# extension, same network. Windows skipped (bench-server is
382+
# excluded from Windows test matrix above).
383+
if: matrix.os == 'linux-x64' || matrix.os == 'linux-arm64' || matrix.os == 'macos-arm64'
384+
run: |
385+
cargo nextest run --cargo-profile ci --locked -p vortex-bench-server --test admin --run-ignored only
373386
- uses: ./.github/actions/check-rebuild
374387
if: matrix.os != 'windows-x64'
375388
with:

.github/workflows/rerun-approvals.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ jobs:
3333
"repos/$REPO/actions/workflows/approvals.yml/runs?head_sha=$HEAD_SHA" \
3434
--jq '.workflow_runs[0].id // empty')
3535
if [ -z "$run_id" ]; then
36-
echo "No approvals.yml run for $HEAD_SHA nothing to re-run."
36+
echo "No approvals.yml run for $HEAD_SHA - nothing to re-run."
3737
exit 0
3838
fi
3939
echo "Re-running approvals.yml run $run_id"

.github/workflows/sql-benchmarks.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -503,7 +503,6 @@ jobs:
503503
504504
- name: Ingest results to v3 server
505505
if: inputs.mode == 'develop' && vars.V3_INGEST_URL != ''
506-
continue-on-error: true
507506
shell: bash
508507
env:
509508
INGEST_BEARER_TOKEN: ${{ secrets.INGEST_BEARER_TOKEN }}

.github/workflows/v3-commit-metadata.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ jobs:
2222

2323
- name: Ingest commit metadata to v3 server
2424
if: vars.V3_INGEST_URL != ''
25-
continue-on-error: true
2625
shell: bash
2726
env:
2827
INGEST_BEARER_TOKEN: ${{ secrets.INGEST_BEARER_TOKEN }}

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

benchmarks-website/AGENTS.md

Lines changed: 54 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ SPDX-License-Identifier: Apache-2.0
33
SPDX-FileCopyrightText: Copyright the Vortex contributors
44
-->
55

6-
# AGENTS.md `benchmarks-website/`
6+
# AGENTS.md - `benchmarks-website/`
77

88
Read [`README.md`](README.md) first for the architecture and the v2/v3
99
side-by-side situation. Then this file. The root [`CLAUDE.md`](../CLAUDE.md)
@@ -14,17 +14,34 @@ covers Rust style, test layout, commit conventions.
1414
Until the cutover PR lands, the top-level v2 files
1515
(`server.js`, `src/`, `index.html`, `vite.config.js`, `package.json`,
1616
`package-lock.json`, `public/`, the top-level `Dockerfile`,
17-
`docker-compose.yml`, `ec2-init.txt`) and the `benchmarks-website` service
18-
in `docker-compose.yml` and the `publish-benchmarks-website.yml` workflow
17+
`docker-compose.yml`) and the `publish-benchmarks-website.yml` workflow
1918
are production. Don't edit them as part of unrelated work.
2019

20+
The v3 deploy lives entirely under `server/`, `migrate/`, and `ops/`.
21+
The operator runbook is [`ops/README.md`](ops/README.md).
22+
2123
## v3 specifics
2224

2325
- **Wire shapes are a coordinated change.** [`server/src/records.rs`](server/src/records.rs),
2426
[`vortex-bench/src/v3.rs`](../vortex-bench/src/v3.rs), and (until cutover)
2527
[`migrate/src/classifier.rs`](migrate/src/classifier.rs) must agree.
2628
Bumping a shape means changing all three plus the snapshot fixtures in
27-
one commit.
29+
one commit. `SCHEMA_VERSION` is the version literal coupled across two
30+
named sites: [`server/src/schema.rs`](server/src/schema.rs) (source of
31+
truth) and [`scripts/post-ingest.py`](../scripts/post-ingest.py) (the
32+
CI ingest wrapper, which hardcodes it as a Python literal). Bump in
33+
lockstep or every CI ingest run 400s. The server-side validation in
34+
`records.rs` + `ingest.rs` and the echo in `/health` all consume the
35+
constant through `crate::schema`.
36+
- **Numeric `?n=` is clamped to 1000; `?n=all` is the uncapped escape
37+
hatch.** HTML routes hydrate from the materialized latest-100 shard
38+
artifact by default; `?n=all` is an explicit opt-in
39+
(chart-init.js's full-history zoom-out hop uses it once, and curl
40+
power users can request it). The numeric `?n=` path is bounded by
41+
`MAX_NUMERIC_COMMIT_WINDOW` in [`server/src/api/window.rs`](server/src/api/window.rs)
42+
as a DoS-protection floor against `curl ...?n=99999999`. If you need
43+
full history, use `?n=all`. Do NOT raise the numeric cap or remove it
44+
without thinking about the DoS surface.
2845
- **`measurement_id` is server-internal.** Never put it on the wire. It is
2946
a deterministic hash over `commit_sha` plus the dim tuple, computed in
3047
[`server/src/db.rs`](server/src/db.rs) and reused by the migrator via
@@ -35,20 +52,33 @@ are production. Don't edit them as part of unrelated work.
3552
- **Don't reach for WASM.** SSR + a thin hydration script in
3653
[`server/static/chart-init.js`](server/static/chart-init.js) is the
3754
whole client.
38-
- **Don't re-introduce a server-side commit cap.** `?n=all` is the default
39-
for HTML routes; visual downsampling happens client-side via LTTB on the
40-
visible commit range only.
41-
- **Don't refetch on every scope change.** The chart fetches its full
42-
history once. Pan, zoom, slider, and the range strip rebuild in place
43-
via the in-memory LTTB pass on the cached payload. The single exception
44-
is the inline-payload zoom-out path: when the user zooms past the first
45-
group's inlined `LANDING_INLINE_N` window for the first time,
46-
`chart-init.js` lazy-fetches `?n=all` once and replaces the payload.
55+
- **v3 ingest is no longer best-effort in CI.** The `Ingest results to v3
56+
server` step in [`bench.yml`](../.github/workflows/bench.yml),
57+
[`sql-benchmarks.yml`](../.github/workflows/sql-benchmarks.yml), and
58+
[`v3-commit-metadata.yml`](../.github/workflows/v3-commit-metadata.yml)
59+
no longer carries `continue-on-error: true`. A v3-server outage on a
60+
develop push now fails the bench workflow and triggers the existing
61+
`incident.io` alert. The gate is `vars.V3_INGEST_URL != ''` so forks
62+
and unconfigured environments are unaffected.
63+
- **Don't re-introduce a server-side commit cap on `?n=all`.** `?n=all`
64+
is the uncapped escape hatch (chart-init.js fetches it once for the
65+
zoom-out path); visual downsampling happens client-side via LTTB on
66+
the visible commit range only. Numeric `?n=` is clamped per the bullet
67+
above. Default fetches from chart-init.js use the materialized
68+
latest-100 shard artifact, not `?n=all`.
69+
- **Don't refetch on every scope change.** Once a chart's payload is in
70+
memory, pan/zoom/slider/range-strip all rebuild in place via the
71+
in-memory LTTB pass on the cached payload. The single exception is the
72+
latest-100 to full-history zoom-out path: charts initially hydrate from
73+
the materialized latest-100 group shard artifact (served from
74+
`/api/artifacts/{generation}/groups/{slug}/shards/{i}`); when the user
75+
zooms past that window for the first time, `chart-init.js` lazy-fetches
76+
`?n=all` once and replaces the latest-100 payload in place.
4777

4878
## Footguns we have already hit
4979

5080
- **Reverse predecessor walk in the tooltip.** `payload.commits[]` is
51-
sorted oldest-first by SQL `commits[0]` is the oldest, `commits[N-1]`
81+
sorted oldest-first by SQL - `commits[0]` is the oldest, `commits[N-1]`
5282
is the newest. For per-row delta the predecessor of `commits[idx]` is
5383
at `idx - 1`. We caught a regression where a "fix" flipped this to
5484
`idx + 1`; the original walk-backward direction is right.
@@ -62,11 +92,21 @@ are production. Don't edit them as part of unrelated work.
6292
## Local dev
6393

6494
```bash
95+
# Public-only run (read API + ingest only, admin routes 404):
6596
INGEST_BEARER_TOKEN=dev cargo run -p vortex-bench-server
97+
98+
# With admin endpoints mounted on a separate loopback listener:
99+
INGEST_BEARER_TOKEN=dev ADMIN_BEARER_TOKEN=dev \
100+
cargo run -p vortex-bench-server
101+
66102
cargo nextest run -p vortex-bench-server -p vortex-bench-migrate
67103
INSTA_UPDATE=auto cargo nextest run -p vortex-bench-server # update snapshots
68104
```
69105

106+
For the full env-var contract (admin bind, snapshot dir, extension dir,
107+
logging spec, PaaS `PORT` fallback) see [`ops/config/vortex-bench.env.example`](ops/config/vortex-bench.env.example)
108+
and the lib-level `//!` doc on [`server/src/main.rs`](server/src/main.rs).
109+
70110
For the migrator end-to-end against the real S3 dump:
71111

72112
```bash

benchmarks-website/README.md

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,16 @@ The website behind `bench.vortex.dev`. The directory currently houses **two
99
implementations side by side**, run together until the v3 cutover lands:
1010

1111
- **v2** (top-level files: `server.js`, `src/`, `index.html`, `vite.config.js`,
12-
`package.json`, `Dockerfile`, `docker-compose.yml`, `ec2-init.txt`,
13-
`public/`). The Node + React stack that has shipped to production for the
14-
life of the site. Built and published by
12+
`package.json`, `Dockerfile`, `docker-compose.yml`, `public/`). The Node +
13+
React stack that has shipped to production for the life of the site. Built
14+
and published by
1515
[`.github/workflows/publish-benchmarks-website.yml`](../.github/workflows/publish-benchmarks-website.yml).
16-
- **v3** (`server/` + `migrate/`). A single Rust binary
17-
[`vortex-bench-server`](server/) that owns a DuckDB file on local disk,
16+
- **v3** (`server/` + `migrate/` + `ops/`). A single Rust binary -
17+
[`vortex-bench-server`](server/) - that owns a DuckDB file on local disk,
1818
serves the API, and renders the HTML. Compiles all static assets
1919
(`chart.umd.js`, `chart-init.js`, `style.css`) into the binary so deploys
20-
are one file plus a database. Container image at
21-
`ghcr.io/vortex-data/vortex/vortex-bench-server:latest`.
20+
are one file plus a database. Built directly on the EC2 host by
21+
[`ops/deploy.sh`](ops/deploy.sh) - see [`ops/README.md`](ops/README.md).
2222
[`migrate/`](migrate/) is a one-shot tool that loads v2's S3 dataset into a
2323
v3 DuckDB; it is throwaway and goes away after cutover.
2424

@@ -34,7 +34,7 @@ S3 bucket; v3 via `--gh-json-v3` POSTed to `/api/ingest`).
3434
`axum` (HTTP) + `maud` (compile-time HTML) + embedded `duckdb-rs` over a single
3535
local DB file. Five fact tables (`query_measurements`, `compression_times`,
3636
`compression_sizes`, `random_access_times`, `vector_search_runs`) plus a
37-
`commits` dim table see [`server/src/schema.rs`](server/src/schema.rs) for
37+
`commits` dim table - see [`server/src/schema.rs`](server/src/schema.rs) for
3838
the column contracts. Three HTML routes (`/`, `/chart/{slug}`,
3939
`/group/{slug}`) and four stable JSON routes (`GET /api/groups`,
4040
`GET /api/chart/{slug}`, `GET /api/group/{slug}`, `GET /health`), plus
@@ -80,12 +80,23 @@ npm run dev
8080

8181
## Deployment
8282

83-
`docker-compose.yml` runs both stacks side by side: v2 on `:80` and v3 on
84-
`:3001`. `watchtower` polls GHCR every 60s so a fresh image push lands
85-
automatically. v3 reads `INGEST_BEARER_TOKEN` from
86-
`/etc/vortex-bench/secrets.env`, persists DuckDB to
87-
`/opt/benchmarks-website/data/bench.duckdb`, and binds `0.0.0.0:3000` so the
88-
container's `:3001` host port forwards through.
83+
v3 runs as a systemd service on a single EC2 host. The full operator
84+
runbook (first-time install, day-to-day, failure modes) is in
85+
[`ops/README.md`](ops/README.md). Summary:
86+
87+
- A `vortex-bench-deploy.timer` polls `origin/develop` every 60s. If commits
88+
in the range touch `benchmarks-website/server/`, `benchmarks-website/migrate/`,
89+
`Cargo.toml`, or `Cargo.lock`, it builds and atomically swaps the binary,
90+
then verifies `/health`. Otherwise it fast-forwards the working tree and
91+
exits silently.
92+
- A `vortex-bench-backup.timer` fires hourly: it asks the server to write a
93+
per-table Vortex snapshot (`schema.sql` plus one `<table>.vortex` file per
94+
table) via the bearer-gated `/api/admin/snapshot` endpoint, `tar czf`s the
95+
snapshot directory into `<UTC ts>.tar.gz`, uploads it to
96+
`s3://vortex-benchmark-results-database/v3-backups/`, and deletes the local
97+
copies.
98+
- For ad-hoc reads against the live DB, `ops/inspect.sh` calls a
99+
bearer-gated `/api/admin/sql` endpoint - no server stop required.
89100

90101
The v3 server is throwaway-friendly: every request runs against the local
91102
DuckDB file, and a fresh boot reapplies the schema DDL idempotently. The
@@ -97,13 +108,12 @@ re-running `vortex-bench-migrate run --output ...` is safe.
97108
The work to flip `bench.vortex.dev` from v2 to v3 is tracked outside this
98109
repo. The relevant code-side bits:
99110

100-
- v3 runs alongside v2 on the same EC2 host today (v2 on `:80`, v3 on
101-
`:3001`) and is fed by CI's dual-write `--gh-json-v3` path.
111+
- v3 runs alongside v2 on the same EC2 host today and is fed by CI's
112+
dual-write `--gh-json-v3` path.
102113
- v2 keeps shipping unchanged until DNS flips. **Do not touch the top-level
103114
v2 files unless you are doing the cleanup PR opened post-flip.**
104115
- The v2 cleanup PR removes everything top-level under `benchmarks-website/`
105116
that belongs to v2 (`server.js`, `src/`, `index.html`, `vite.config.js`,
106117
`package.json`, `package-lock.json`, `public/`, the top-level `Dockerfile`,
107-
`docker-compose.yml`, `ec2-init.txt`, and the
108-
`publish-benchmarks-website.yml` workflow). The v3 tree under `server/` and
109-
`migrate/` is untouched.
118+
`docker-compose.yml`, and the `publish-benchmarks-website.yml` workflow).
119+
The v3 tree under `server/`, `migrate/`, and `ops/` is untouched.

benchmarks-website/ec2-init.txt

Lines changed: 0 additions & 70 deletions
This file was deleted.

benchmarks-website/migrate/src/main.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,15 @@ enum Command {
4242
/// `data.json.gz`, `commits.json`, and `file-sizes-*.json.gz`.
4343
#[arg(long, required_if_eq("source", "local"))]
4444
source_dir: Option<PathBuf>,
45+
/// Continue past per-`file-sizes-*.json.gz` failures rather than
46+
/// failing the migration. By default a single failed
47+
/// `file-sizes-*` source is an error, because a "successful"
48+
/// migrated DB with missing compression-size history is a worse
49+
/// outcome than a loud failure that the operator can retry. Pass
50+
/// this flag when you genuinely want partial coverage (e.g. one
51+
/// known-bad source file you want to skip).
52+
#[arg(long, default_value_t = false)]
53+
allow_missing_file_sizes: bool,
4554
},
4655
/// Diff a migrated DuckDB against the live v2 `/api/metadata`
4756
/// endpoint. Exits 0 if every v2 group is present in v3, 1
@@ -83,6 +92,7 @@ fn run() -> Result<()> {
8392
output,
8493
source,
8594
source_dir,
95+
allow_missing_file_sizes,
8696
} => {
8797
let source = match source {
8898
SourceKind::PublicS3 => Source::PublicS3,
@@ -100,6 +110,13 @@ fn run() -> Result<()> {
100110
100.0 * summary.uncategorized_fraction()
101111
);
102112
}
113+
if summary.file_sizes_failed > 0 && !allow_missing_file_sizes {
114+
anyhow::bail!(
115+
"{} file-sizes-*.json.gz source file(s) failed (see warnings above); \
116+
re-run with --allow-missing-file-sizes if partial coverage is intended",
117+
summary.file_sizes_failed
118+
);
119+
}
103120
Ok(())
104121
}
105122
Command::Verify { against, duckdb } => {

0 commit comments

Comments
 (0)