Skip to content

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

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

pmoust wants to merge 9 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 five steps:

  1. pins it to Ravel v0.28.0;
  2. runs its object store on s3s-fs instead of RustFS;
  3. writes larger single-row-group objects on machines with 30 GB or more;
  4. sorts rows by a clustering key, with bloom filters only on text columns;
  5. turns on Ravel's decoded column-block cache with an 8 GB cap on machines with 30 GB or more.

queries.sql and template.json are unchanged, and the result stays "tuned": "no". The cache cap is the entry's fixed configuration, set by ./start from the machine's memory like the load tier, not tuned per run. This PR replaces #2822 and #2823, which carried steps 3 and 4 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.

What ./start does now. Ravel 0.28.0 adds a decoded column-block cache, off by default. It keeps decoded columns between statements, so a repeated statement skips their decode. On machines whose MemTotal is 30,000,000 kB or more, ./start passes --decoded-cache-max-bytes 8000000000; below that the cache stays off. Its entries are charged to the server's memory budget, and memory pressure evicts them before a query is refused. The cache does not change the cold column: the driver restarts the server before every cold try, so the cache starts empty. The 8 GB cap was chosen on c6a.4xlarge from 4, 8 and 12 GB. At 4 GB the cache churned and two statements ran slower than without it, and 8 GB came within 0.2 % of 12 GB's warm-run geomean. RAVEL_DECODED_CACHE_BYTES overrides the cap, and 0 turns the cache off.

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 (results/20261026) v0.28.0 + decoded cache (this PR, results/20261030)
load 866 s 878 s 784 s 898 s 647 s 633 s
data size 10.21 GB 10.21 GB 10.20 GB 10.31 GB 8.33 GB 8.33 GB
cold sum 365.7 s 172.8 s 163.1 s 117.5 s 108.0 s 106.2 s
hot sum (better of runs 2 and 3) 57.2 s 57.0 s 51.6 s 52.3 s 50.2 s 37.9 s
concurrent QPS 0.720 0.733 0.792 0.802 0.830 0.920
concurrent error ratio 0.061 0.041 0.054 0.054 0.053 0.055
statements answered 43 of 43 43 of 43 43 of 43 43 of 43 43 of 43 43 of 43

A second run of the clustering-key column 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) and with the decoded cache. 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. v0.28.0 resolves the same server defaults as v0.27.0 on c6a.4xlarge: fetch cache 7.1 GB, per-query SQL pool 16.5 GB, fetch concurrency 32.

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 and the cache cap apply to every machine whose MemTotal is 30,000,000 kB or more; both were 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 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
Ravel 0.28.0 adds a decoded column-block cache that keeps decoded columns
between statements, off by default. ./start now turns it on with an 8 GB cap
on machines with 30 GB of MemTotal or more, the tier ./load already uses,
and leaves it off below, where the cap was not measured.
RAVEL_DECODED_CACHE_BYTES overrides the cap, and 0 turns it off.
A run of the entry on the published v0.28.0 binaries, with ./start setting
the 8 GB decoded cache cap and s3s-fs as the store: cold sum 106.2 s, hot
sum 37.9 s (50.2 s on v0.27.0 without the cache), concurrent QPS 0.920,
error ratio 0.055, load 633 s, data 8.33 GB, 43 of 43 answered.
@pmoust pmoust changed the title Update the ravel entry: v0.27.0, s3s-fs store, larger sorted objects Update the ravel entry: v0.28.0, s3s-fs store, larger sorted objects, decoded cache Oct 30, 2026
@alexey-milovidov alexey-milovidov added the machine:all PR benchmark on every machine type label Oct 30, 2026
@alexey-milovidov
alexey-milovidov deployed to benchmark-approval October 30, 2026 12:34 — with GitHub Actions Active

This branch was successfully deployed

1 active deployment
benchmark-approval — 637aaa80 Deployed Oct 30, 2026 by alexey-milovidov via launch #926
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

machine:all PR benchmark on every machine type

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants