Add Rayforce - #1375
Conversation
Rayforce (https://github.com/RayforceDB/rayforce) is a zero-dependency columnar analytics and graph engine in pure C, queried through Rayfall, its Lisp-like language. queries.sql therefore holds 43 Rayfall expressions, one per line, the way jd/bqn hold their own dialects. (Not to be confused with the closed #940, which was RayforceDB's internal 10M-row harness opened against this repo by mistake.) Two measured engine properties shaped the entry: - Text columns are loaded as SYM (dictionary-encoded), not STR. STR pool offsets are uint32_t, so a column's pool is capped at 4 GiB, and at 100M rows URL/Title/Referer/OriginalURL need 5-11 GB each. Opening a splayed table also validates every STR element, and that cost is superlinear: 0.4 s at 2M rows, 199 s at 9.6M rows (99 s for the parted layout). All-SYM opens in 17 s at 9.6M rows and stores the same subset in 4.4 GB instead of 7.5 GB. - It runs as an IPC server, with BENCH_RESTARTABLE=yes. Opening the table is eager (validate every column file, load the dictionary), so a CLI-per-query would pay that 129 times per run; the server pays it once per start and times queries with `timeit` server-side. The restart is not optional either: with the server left up, drop_caches cannot evict pages a live process still maps, and a "cold" query measured 0.029 s against 0.44 s after a real restart, so keeping the process alive would report every run as warm. BENCH_CHECK_TIMEOUT is raised to 1800 s because the readiness probe now waits for that table open. Query notes: HAVING becomes an outer select over the grouped one; LIMIT/OFFSET is take: [m n]; DATE_TRUNC('minute', EventTime) is (xbar EventTime 60000000000); CASE WHEN is the row-wise (if ...); Q29's REGEXP_REPLACE is rebuilt from str-find/substr/if and reproduces the regex exactly on this dataset, fallback rows included. Q25/Q27 project EventTime alongside SearchPhrase because sorting runs after projection, and Q35 projects the constant group key in an outer select. All 43 queries were checked against clickhouse-local on a 2M-row subset of the same CSV: 33 match exactly or modulo row order, and the other 10 differ only in which of several equally-ranked rows a LIMIT over ties returns (the sort-key multisets are identical). install, load, the stop/drop_caches/start/check cold cycle, 43 queries through ./query, 10 concurrent clients and the error paths were all exercised end to end. Two upstream bugs found on the way, neither affecting this entry: RayforceDB/rayforce#404 (take: drops pool-backed STR values) and RayforceDB/rayforce#405 (ungrouped count(distinct) in a select projection returns the row count). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Results for Logs:
|
|
The run of Logs:
|
|
The run of Logs:
|
|
The run of Logs:
|
|
The run of Logs:
|
|
Results for Logs:
|
|
It tends to randomly degrade up to being totally stuck: |
|
But as long as we have two configurations on which the system barely works, it's okay to merge. |
|
But no - I don't think that the results are trustworthy. This needs a more detailed investigation. |
|
Results for Logs:
|
Adds Rayforce — a zero-dependency
columnar analytics and graph engine in pure C (MIT), queried through Rayfall,
its Lisp-like language.
queries.sqltherefore holds 43 Rayfall expressions,one per line, the way
jdandbqnhold their own dialects.(Not the same as the closed #940 — that was RayforceDB's internal 10M-row
harness, opened against this repo by mistake. This is a standard-harness entry
on the full dataset.)
Two measured engine properties shaped the entry
Text columns load as
SYM(dictionary-encoded), notSTR.STRpooloffsets are
uint32_t, so a column's pool is capped at 4 GiB, while at 100Mrows
URL/Title/Referer/OriginalURLhold 5–11 GB of bytes each. Opening asplayed table also validates every
STRelement, and that cost is superlinear:0.4 s at 2M rows, 199 s at 9.6M rows (99 s for the parted layout). All-
SYMopens in 17 s at 9.6M rows and stores the same subset in 4.4 GB instead of
7.5 GB.
It runs as an IPC server, with
BENCH_RESTARTABLE=yes. The table open iseager (validate every column file, load the dictionary), so a CLI-per-query
would pay it 129 times per run; the server pays it once per start and times
queries with
timeitserver-side. The restart is not optional either: with theserver left up,
drop_cachescannot evict pages a live process still maps, anda "cold" query measured 0.029 s versus 0.44 s after a real restart — keeping the
process alive would report every run as warm.
BENCH_CHECK_TIMEOUTis raised to1800 s because the readiness probe now waits for that open.
Query notes
HAVINGbecomes an outerselectover the grouped one;LIMIT n OFFSET mistake: [m n];DATE_TRUNC('minute', EventTime)is(xbar EventTime 60000000000);CASE WHENis the row-wise(if ...). Q29'sREGEXP_REPLACEis rebuilt fromstr-find/substr/ifand reproduces theregex exactly on this dataset, fallback rows included. Q25/Q27 project
EventTimealongsideSearchPhrasebecause sorting runs after projection, andQ35 projects its constant group key in an outer
select.Validation
All 43 queries were diffed against
clickhouse-localon a 2M-row subset of thesame CSV: 33 match exactly or modulo row order, and the other 10 differ only in
which of several equally-ranked rows a
LIMITover ties returns (the sort-keymultisets are identical).
install,load, the stop/drop_caches/start/checkcold cycle, all 43 queries through
./query, 10 concurrent clients, and theerror paths were exercised end to end.
Upstream bugs found on the way
Neither affects this entry, both filed with minimal repros:
selectwithtake:returns empty strings forpool-backed
STRvalues (silently;(select {from: t take: 1})is enough).A further reason the text columns here are
SYM.(count (distinct col))in aselectprojection returns the row count on
STR/SYMand a type error on integers;hence Q5/Q6 use the whole-column reducer
(count (distinct (at hits 'c))).🤖 Generated with Claude Code