Skip to content

Update the ravel entry: v0.27.0, s3s-fs store, larger sorted objects - #2804

Open
pmoust wants to merge 7 commits into
ClickHouse:mainfrom
pmoust:ravel-s3sfs-store
Open

pmoust wants to merge 7 commits into
ClickHouse:mainfrom
pmoust:ravel-s3sfs-store

Conversation

@pmoust

@pmoust pmoust commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Updates the ravel entry in four steps: pins it to Ravel v0.27.0, runs its object store on s3s-fs instead of RustFS, writes larger single-row-group objects on machines with 30 GB or more, and sorts rows by a clustering key with bloom filters only on text columns. ./start, queries.sql and template.json are unchanged, the server gets no performance flags, and the result stays "tuned": "no". This PR replaces #2822 and #2823, which carried the last two steps separately.

Why the store changes. Ravel holds no data on local disk; a cold statement is a set of ranged GETs against the store, which reads them from the machine's disk. Both stores are single-node S3 servers over that disk, but they differ in what a ranged read costs. On a single drive RustFS reads the whole 1 MiB erasure block around every ranged GET: about 1.5 MB of disk per 10 KB range, measured on c6a.4xlarge with one request at a time against a quiet box. s3s-fs serves a range with one file read. So on RustFS the cold column measured the store's read amplification more than the database. The README's "The object store" section states this beside the figures.

What ./install does now. It downloads the s3s-fs binary for the host architecture from a release on our fork (NOFireAI/s3s s3s-fs-0.17.0-ravel.1, built from commit 9353c1f1b) and verifies it against that release's SHA256SUMS before moving it into place, the way Ravel's own binaries are installed. When the release is unreachable it builds the same commit with cargo install --locked. The commit is the v0.17.0 release plus two changes offered upstream, s3s-project/s3s#1013 (listings return each object's stored ETag, which Ravel's one-time store qualification requires) and s3s-project/s3s#1014 (the access and secret key are read from the environment, so no key sits in a process argument list). The source-build fallback installs a minimal Rust toolchain when the machine has none (measured at about 3 minutes and 1.1 GB of RSS on c6a.large, 30 s on c8g.4xlarge). RustFS is no longer downloaded. rustfs-start is store-start, the store directory is store/, and the generated credentials file is store/credentials.env, still readable by the current user only.

What ./load does now. On machines whose MemTotal is 30,000,000 kB or more, it writes objects with one row group each: --target-bytes 3700000000, --group-target-blocks 512, a 60 s age trigger, pipeline depth 56 and a 14,000,000,000-byte load budget. On c6a.4xlarge that gives about 222 objects of about 46 MB instead of about 420 of 24 MB. Machines below 30 GB keep the previous geometry. Before the load, on every machine, it sets two tenant settings:

  • bloom-scope set --scope text: bloom filters only on text columns (log bodies and severity). hits has none, so no column is covered and each block keeps only the minimum filter. No statement in queries.sql has a predicate a bloom filter would answer.
  • clustering-key set --column EventDate --column UserID --bucket-width 1d: rows inside each object sort by counter, day, EventDate, UserID and time. That is the order ClickHouse's own entry gives its table with PRIMARY KEY (CounterID, EventDate, UserID, EventTime, WatchID); the counter is already Ravel's stream, so it is not repeated.

Both are table-level layout settings applied once before the load, like a primary key in a CREATE TABLE.

Measurement. Every column is the entry run through its own benchmark.sh on a fresh c6a.4xlarge with this repository's Ubuntu 24.04 image, on published release binaries (SHA256SUMS verified by ./install), one box per run. Each column adds one step to the one before it.

figure v0.26.0, RustFS (results/20261025, current) v0.26.0, s3s-fs v0.27.0, s3s-fs + 30 GB tier (mean of 4 runs) + clustering key and text bloom scope (this PR, results/20261026)
load 866 s 878 s 784 s 898 s 647 s
data size 10.21 GB 10.21 GB 10.20 GB 10.31 GB 8.33 GB
cold sum 365.7 s 172.8 s 163.1 s 117.5 s 108.0 s
hot sum (better of runs 2 and 3) 57.2 s 57.0 s 51.6 s 52.3 s 50.2 s
concurrent QPS 0.720 0.733 0.792 0.802 0.830
concurrent error ratio 0.061 0.041 0.054 0.054 0.053
statements answered 43 of 43 43 of 43 43 of 43 43 of 43 43 of 43

A second run of this PR's entry measured 107.6 s cold, 50.4 s hot, QPS 0.843 and an error ratio of 0.070. The concurrent errors are memory refusals under concurrency in every column; the four 30 GB-tier runs alone spread from 0.046 to 0.059.

The store swap moves the cold column; the hot column moves with the release (v0.27.0 pushes LIKE '%literal%' on a typed string column into the scan and keeps GROUP BY keys in their string form until the rows a statement returns). The 30 GB tier and the two settings move cold again and shrink the load and the stored data. The tier makes the load about 110 s longer; the settings then make it about 250 s shorter.

Answers are equal across all runs on 41 of the 43 statements. q18 (LIMIT without ORDER BY) and q25 (orders by EventTime but returns only SearchPhrase) return different rows between the tries of any single run, on every version and layout above. The statements that order by time and stop at a limit (q24, q25, q27) did not slow down with the clustering key, though rows inside an object are no longer in time order.

The 30 GB tier applies to every machine whose MemTotal is 30,000,000 kB or more; it was measured only on c6a.4xlarge, and the other sizes are left to the usual re-run of the matrix. The RustFS results stay under their own dates.

Ravel reads cold statements as ranged GETs. On a single drive RustFS
serves each ranged GET by reading the whole 1 MiB erasure block around
it, about 1.5 MB of disk per 10 KB range measured on c6a.4xlarge, so
the cold figures measured the store's read amplification more than the
database. s3s-fs serves a range with one file read. Both are
single-node S3 servers over the machine's disk; the entry stays a
local-disk entry with nothing required from the operator.

./install downloads the s3s-fs binary for the host architecture from a
release on the NOFireAI/s3s fork (s3s-fs-0.17.0-ravel.1: the v0.17.0
release plus listing ETags, s3s-project/s3s#1013, which Ravel's store
qualification requires, and keys read from the environment,
s3s-project/s3s#1014, so no key sits in a process argument list) and
verifies it against that release's SHA256SUMS before moving it into
place, the way Ravel's own binaries are installed. When the release is
unreachable it builds the same pinned commit with cargo install
--locked, so what runs is fixed by content either way; the commit
installed is recorded beside the binary. RustFS is gone from the entry:
nothing downloads it. rustfs-start becomes store-start, the store
directory is store/ and the generated keys are STORE_ACCESS_KEY and
STORE_SECRET_KEY.

The README gains a section on the store that states the swap and the
measured reason for it.
One run of the entry as it stands (s3s-fs from the fork release,
verified against its SHA256SUMS; the published v0.26.0 binaries
verified against the release's SHA256SUMS) on a fresh c6a.4xlarge with
this repository's Ubuntu 24.04 image, through the entry's own
benchmark.sh. Cold sum 172.8 s against 365.7 s for the same binaries on
RustFS (results/20261025); hot 57.0 s, QPS 0.733, error ratio 0.041,
43 of 43 answered, answers equal to the RustFS run's. The RustFS results
stay under their own date.
@github-actions

github-actions Bot commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Install the v0.27.0 release by default, and replace the c6a.4xlarge
results file with a run of the entry as it stands on that release (s3s-fs
from the fork release, the published v0.27.0 binaries verified against the
release's SHA256SUMS): cold sum 163.1 s, hot 51.6 s, concurrent QPS 0.792,
error ratio 0.054, 43 of 43 answered. On v0.26.0 the same entry measured
172.8 s cold and 57.0 s hot. The README's resolved-defaults sentence and
the cold-sum history carry the v0.27.0 figures.
@pmoust
pmoust force-pushed the ravel-s3sfs-store branch from d257fa6 to d418972 Compare October 26, 2026 13:42
@pmoust pmoust changed the title Run the ravel entry on s3s-fs instead of RustFS Pin the ravel entry to v0.27.0 and run its store on s3s-fs Oct 26, 2026
Add a MemTotal tier at 30 GB to ./load: target 3,700,000,000, one row group
per object (--group-target-blocks 512), a 60 s age trigger, pipeline depth 56
and a 14,000,000,000-byte load budget. On c6a.4xlarge with v0.27.0 that
writes 223 objects of about 46 MB instead of 422 of about 24 MB, and each
column of an object is one contiguous range. Machines below 30 GB keep the
existing geometry. RAVEL_GROUP_TARGET_BLOCKS overrides the group setting; an
empty value keeps the loader's default.

Signed-off-by: Panagiotis Moustafellos <pmoust@nofire.ai>
Replace results/20261026/c6a.4xlarge.json with a run of the entry with the
30 GB tier, on the published v0.27.0 binaries and s3s-fs from the fork
release: 222 objects, cold sum 117.7 s, hot 51.5 s, concurrent QPS 0.810,
error ratio 0.051, load 875 s, 43 of 43 answered. The same release with the
previous geometry measured 163.1 s cold and 51.6 s hot. The README's
cold-sum sentence carries both figures.

Signed-off-by: Panagiotis Moustafellos <pmoust@nofire.ai>
Before the load, set the tenant's bloom scope to text and its clustering
key to (EventDate, UserID) with a one-day bucket. hits has no text columns,
so no string column gets a bloom filter, and rows inside each object sort
by counter, day, EventDate, UserID and time, the order ClickHouse's entry
gives its table with its primary key. On c6a.4xlarge with v0.27.0, bloom
scope text alone took the cold sum from 117.5 s to 112.1 s and clustering
alone to 114.6 s, each over three runs, with answers equal and the stored
data 4.6% and 14.7% smaller.

Signed-off-by: Panagiotis Moustafellos <pmoust@nofire.ai>
Replace results/20261026/c6a.4xlarge.json with a run of the entry with both
tenant settings, on the published v0.27.0 binaries and s3s-fs from the fork
release: cold sum 108.0 s, hot 50.2 s, concurrent QPS 0.830, error ratio
0.053, load 647 s, data 8.33 GB, 43 of 43 answered. A second run measured
107.6 s cold and 50.4 s hot. Four runs without the settings averaged 117.5 s
cold, 52.3 s hot and 898 s of load. The README describes both settings and
carries the new cold figure.

Signed-off-by: Panagiotis Moustafellos <pmoust@nofire.ai>
@pmoust
pmoust requested a deployment to benchmark-approval October 26, 2026 18:32 — with GitHub Actions Waiting
@pmoust pmoust changed the title Pin the ravel entry to v0.27.0 and run its store on s3s-fs Update the ravel entry: v0.27.0, s3s-fs store, larger sorted objects Oct 26, 2026

This branch is waiting to be deployed

1 waiting deployment
benchmark-approval — 3d4c9386 Waiting Oct 26, 2026 by pmoust via launch #910
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