Stratum

SQL that branches

Stratum is a columnar SQL engine for the JVM. Tables fork in O(1) through copy-on-write structural sharing, and the server accepts PostgreSQL clients such as psql, JDBC and DBeaver. The project is currently beta.

41/55 queries faster than DuckDB in the published 10M-row, 1T suite
9.4× faster on H2O db-benchmark Q10
O(1) table fork
Pure JVM no native dependencies

Performance

These selected results come from a single-threaded comparison with DuckDB 1.4.4 running in-process through JDBC. The suite uses 10 million rows on an Intel Core Ultra 7 258V with JVM 25 and reports the median of 10 measured runs after five warmups.

Query Stratum DuckDB Ratio
TPC-H Q6 (filter + sum-product) 16.9ms 28.3ms 1.7x faster
Filtered COUNT (NEQ pred) 3.3ms 11.5ms 3.4x faster
H2O Q3 (100K string groups) 67.2ms 364.4ms 5.4x faster
LIKE '%search%' (string scan) 47.2ms 246.4ms 5.2x faster
H2O Q6 (STDDEV group-by) 29.7ms 81.1ms 2.7x faster
H2O Q10 (10M groups, 6 cols) 754.4ms 7110.1ms 9.4x faster
AVG(LENGTH(URL)) 41.3ms 173.4ms 4.2x faster

Stratum wins 41 of 55 queries in the published 10-million-row, single-threaded suite. DuckDB wins on several sparse-selectivity filters, high-cardinality group-bys and global COUNT(DISTINCT). Full methodology and raw results in the benchmark docs.

Branch tables without copying them

st/fork creates an O(1) copy-on-write branch. It records a new root while leaving unchanged chunks shared. st/sync! persists a branch to storage. st/load restores any named branch. Pass column data as a table map to SQL queries, or register live storage-backed tables in the server with register-live-table!.

What is this syntax?
require('[stratum.api :as st],
  '[konserve.file-store :as fs],
  '[clojure.core.async :refer [<!!]])

;; Open storage and load the orders dataset (10M rows)
def store: <!!(fs/new-fs-store("/data/stratum"))
def orders: <!!(st/load(store, "orders"))

;; Fork in O(1); unchanged chunks remain shared
def experiment: st/fork(orders)

;; Persist the fork as a named branch that shares chunks with main
<!!(st/sync!(experiment, store, "experiment"))

;; Query both branches via SQL by passing column data as a table map
st/q("SELECT SUM(price * qty) FROM t", {"t" st/columns(orders)})
;; => {:SUM(price * qty) 4821903.40}   ← main branch unchanged

st/q("SELECT SUM(price * qty) FROM t", {"t" st/columns(experiment)})
;; => {:SUM(price * qty) 4401238.66}   ← experiment branch

;; Time-travel: load any historical branch from storage
def baseline: <!!(st/load(store, "orders-baseline"))
st/q("SELECT COUNT(*) FROM t", {"t" st/columns(baseline)})
;; => {:COUNT(*) 9847233}
(require '[stratum.api :as st]
         '[konserve.file-store :as fs]
         '[clojure.core.async :refer [<!!]])

;; Open storage and load the orders dataset (10M rows)
(def store  (<!! (fs/new-fs-store "/data/stratum")))
(def orders (<!! (st/load store "orders")))

;; Fork in O(1); unchanged chunks remain shared
(def experiment (st/fork orders))

;; Persist the fork as a named branch that shares chunks with main
(<!! (st/sync! experiment store "experiment"))

;; Query both branches via SQL by passing column data as a table map
(st/q "SELECT SUM(price * qty) FROM t" {"t" (st/columns orders)})
;; => {:SUM(price * qty) 4821903.40}   ← main branch unchanged

(st/q "SELECT SUM(price * qty) FROM t" {"t" (st/columns experiment)})
;; => {:SUM(price * qty) 4401238.66}   ← experiment branch

;; Time-travel: load any historical branch from storage
(def baseline (<!! (st/load store "orders-baseline")))
(st/q "SELECT COUNT(*) FROM t" {"t" (st/columns baseline)})
;; => {:COUNT(*) 9847233}

Yggdrasil coordinates branches across Datahike, Stratum and Proximum so an application can refer to a consistent set of SQL, Datalog and vector snapshots. Yggdrasil → · Dataset API docs →

Where Stratum fits

Datahike is the system of record for immutable transactions, Datalog and time travel. Stratum provides a columnar layout for SQL scans and analytics, including group-bys, joins and window functions.

Fast

SIMD acceleration through the Java Vector API, fused execution, dense group-by indexing and zone-map pruning.

Branchable

Fork a table in O(1) through structural sharing, persist named branches and load historical snapshots.

Pure JVM

No JNI or native compilation. The same library and standalone server run on a supported JVM.

Broad SQL surface

PostgreSQL wire protocol, DML, CTEs, window functions, aggregates and FROM read_csv / read_parquet. Connect with psql, JDBC, DBeaver or psycopg2.

Clojure-native

Datasets implement IEditableCollection, ILookup, IPersistentCollection. tablecloth and tech.ml.dataset work directly. DSL or SQL strings.

Ecosystem

Branch Datahike, Stratum, and Proximum together via Yggdrasil. Consistent snapshots across SQL, Datalog, and vector search.

Quick start

Standalone server

# Java 22+, no Clojure needed
java --add-modules jdk.incubator.vector \
     -jar stratum-standalone.jar \
     --index /data/orders.csv

# Or try the built-in demo tables
java --add-modules jdk.incubator.vector \
     -jar stratum-standalone.jar --demo

# Connect with any PostgreSQL client
psql -h localhost -p 5432 -U stratum

Clojure API

What is this syntax?
require('[stratum.api :as st])

;; Query with DSL
st/q(
  {:from {:price prices, :qty quantities},
   :where [[:> :price 100]],
   :group [:region],
   :agg [[:sum [:* :price :qty]] [:count]]})

;; Or with SQL
st/q(
  "SELECT region, SUM(price * qty), COUNT(*)\n       FROM orders WHERE price > 100 GROUP BY region",
  {"orders" {:price prices, :qty quantities, :region regions}})
(require '[stratum.api :as st])

;; Query with DSL
(st/q {:from {:price prices :qty quantities}
       :where [[:> :price 100]]
       :group [:region]
       :agg   [[:sum [:* :price :qty]]
               [:count]]})

;; Or with SQL
(st/q "SELECT region, SUM(price * qty), COUNT(*)
       FROM orders WHERE price > 100 GROUP BY region"
      {"orders" {:price prices :qty quantities
                 :region regions}})

How it works

Execution

  • Fused SIMD: predicate evaluation + aggregation in a single pass, no intermediate arrays
  • Dense group-by: direct array indexing for low-cardinality groups, hash tables for high-cardinality
  • Zone map pruning: skip entire chunks based on per-chunk min/max statistics
  • Parallel execution: cache-friendly work partitioning across all cores

Storage

  • Chunked B-tree: copy-on-write chunks with structural sharing across snapshots
  • Konserve backend: pluggable storage: filesystem, S3, or custom
  • Lazy loading: only accessed chunks are loaded from disk on demand
  • Mark-and-sweep GC: prune unreachable snapshots without manual cleanup

SQL capabilities

  • DML: SELECT, INSERT, UPDATE, DELETE, UPSERT (INSERT ON CONFLICT), UPDATE FROM, CREATE TABLE, DROP TABLE
  • Aggregates: SUM, COUNT, AVG, MIN, MAX, STDDEV, VARIANCE, CORR, MEDIAN, PERCENTILE_CONT, APPROX_QUANTILE, COUNT(DISTINCT), FILTER clause
  • Group-by: any number of columns, string and numeric, with HAVING
  • Joins: INNER, LEFT, RIGHT, FULL with multi-column keys
  • Window functions: ROW_NUMBER, RANK, DENSE_RANK, NTILE, PERCENT_RANK, CUME_DIST, LAG, LEAD, SUM/COUNT/AVG/MIN/MAX OVER with frame clauses
  • Composition: CTEs (WITH), subqueries, IN/EXISTS, UNION/INTERSECT/EXCEPT
  • Expressions: CASE WHEN, COALESCE, NULLIF, GREATEST, LEAST, CAST
  • Date/time: DATE_TRUNC, EXTRACT, DATE_ADD, DATE_DIFF
  • String: LIKE/ILIKE, UPPER/LOWER, LENGTH, SUBSTR
  • Files: FROM read_csv('file.csv'), FROM read_parquet('file.parquet')
  • Analytics: ANOMALY_SCORE, ANOMALY_PREDICT, ANOMALY_PROBA, ANOMALY_CONFIDENCE (isolation forest via SQL; online rotation for concept drift, docs)
  • Other: EXPLAIN, SELECT DISTINCT, IS NULL/IS NOT NULL, LIMIT/OFFSET

Work with us

If you need help getting Stratum into production, we can help with integration, custom development, and support contracts.

Installation

What is this syntax?
; deps.edn (Clojure CLI)
; check https://clojars.org/org.replikativ/stratum for the latest version
{:deps {org.replikativ/stratum {:mvn/version "0.3.72"}}}

; JVM flags required (add to :jvm-opts or alias)
:jvm-opts

["--add-modules=jdk.incubator.vector" "--enable-native-access=ALL-UNNAMED"]

;; Leiningen, project.clj
;; [org.replikativ/stratum "0.3.72"]
; deps.edn (Clojure CLI)
; check https://clojars.org/org.replikativ/stratum for the latest version
{:deps {org.replikativ/stratum {:mvn/version "0.3.72"}}}

; JVM flags required (add to :jvm-opts or alias)
:jvm-opts ["--add-modules=jdk.incubator.vector"
           "--enable-native-access=ALL-UNNAMED"]

;; Leiningen, project.clj
;; [org.replikativ/stratum "0.3.72"]

Requires JDK 22+. Clojure 1.12+. Apache 2.0 license. Latest version on Clojars.