Don't panic · Apache 2.0 · Built by 42grad

A lean, fast
RDF triple store
in Rust

Trillian keeps the whole graph in memory with flat, cache-friendly indexes andu32 term IDs, serves SPARQL over HTTP, and persists to a zero-copy memory-mapped snapshot. Worst-case-optimal joins for cyclic patterns.

1.26B
triples loaded
~1ms
median BGP query
35B
per triple in RAM
930
queries, 0 deviations

Everything you need
in a triple store

Trillian is built around a few deliberate choices: flat, cache-friendly indexes, u32 term IDs, zero-copy memory-mapped persistence, and a hybrid query engine.

In-memory, cache-friendly

Three CSR permutation indexes (SPO / POS / OSP), u32 IDs, no pointer chasing. Flat contiguous vectors keep the data cache-hot and predictable.

Hybrid query engine

Worst-case-optimal joins (leapfrog triejoin) for cyclic patterns; a pipelined, cost-based plan for the rest. Picks the right algorithm per query.

Compact footprint

On the full Wikidata graph (1.26B triples): ~44 GB RAM (~35 B/triple) and ~49 GB on-disk snapshot — the most compact store in its class.

Fast lookups

Sub-millisecond entity lookups. Single-pattern WDBench queries answer in ~1 ms median. Full Wikidata queries complete in seconds, not minutes.

Durable updates

INSERT DATA / DELETE DATA via a write-ahead log. fsync'd operations survive a restart without rewriting the immutable snapshot.

Open & sovereign

Apache 2.0 licensed. Run it on your own infrastructure. Built by 42grad GmbH as a building block for sovereign, sustainable data infrastructure.

Architecture at a glance

Three CSR permutation indexes, a dual-mode mmap dictionary, and a hybrid query engine that routes cyclic patterns to WCOJ and acyclic to a pipelined cost-based plan.

01

Ingest

N-Triples are parsed; each term is interned to a u32 ID via the dictionary.

02

Index

Three flat-CSR permutations (SPO, POS, OSP) built in contiguous u32 vectors.

03

Persist

Index and dictionary written to a single file, then memory-mapped for zero-copy serving.

04

Query

SPARQL parsed, planner picks joins, hybrid engine executes — WCOJ for cycles, pipelined for trees.

Term Dictionary

Every IRI, literal, and blank node is interned to a u32 ID. Type is encoded in the key prefix — no separate type table. Namespace folding compresses repeated Wikidata prefixes to 2-byte escapes.

CSR Indexes

Three permutations (SPO, POS, OSP) mean any access pattern with at least one bound term answers from a sorted slice. A base + delta overlay keeps updates incremental; reads return Cow<[u32]>.

Hybrid Engine

Cyclic patterns (triangles) route to leapfrog triejoin. Acyclic patterns (stars, paths) use a cost-based, left-deep plan with a pipelined DFS executor. A configurable row cap prevents OOM.

Persistence

Snapshots are a single versioned file; load memory-maps it, so indexes and dictionary are served zero-copy. A WAL appends INSERT / DELETE DATA and replays on restart.

Benchmarked against
the field

Measured on the full WDBench Wikidata graph — 1.26 billion triples — against published numbers for Blazegraph, Jena, Virtuoso, and Neo4j. Same query sets, 60 s timeout, 100k output cap.

What is and isn't comparable
Directly comparable

Result correctness — per-query result counts against published numbers (below 100k must match exactly).

Directly comparable

On-disk footprint — store size in bytes per triple, independent of hardware.

Indicative only

Latency — different hardware (AWS r6i vs. the WDBench paper's Xeon Silver 4110). Treat absolute ms as a rough signal, not a controlled head-to-head.

Indicative only

Architecture differs — Trillian is in-memory; the others are disk-backed with 64 GB RAM. Much of the latency gap is architectural.

Result correctness vs. published

ClassMatchDiff
Single BGP267/2670
Multiple BGP661/6610
Optional239/31576
Property Paths381/39312
C2RPQ232/28351

On-disk footprint directly comparable

EngineSizeB/triple
Trillian49 GB39
Blazegraph70 GB56
Virtuoso70 GB56
Jena (TDB)110 GB87
Neo4j112 GB89
Correctness & footprint

BGP is provably correct — 930 queries, zero deviations in result counts. Trillian's snapshot is the most compact store of the field at 39 B/triple; in memory it holds the entire graph resident (~15 GB RSS post-load, growing toward 49 GB as the working set is touched).

Coverage & latency caveats

"Capped" = Trillian's result-row cap fired on a degenerate query (a clean error, not a crash). Latency numbers are indicative: Trillian ran on AWS r6i; published numbers from the WDBench paper's Xeon Silver 4110, 128 GB RAM. A fast median only covers completed queries — read it together with coverage.

Coverage completed within 60 s

ClassTrillianOthersCapped
Single BGP (280)26728013
Multiple BGP (681)66567716
Optional (498)315498183
Property Paths (660)397560263
C2RPQ (539)308508231

Median latency ms, completed

Indicative
ClassTril.Blaz.JenaVirt.Neo4j
Single BGP169279261642
Multiple BGP1311,1662,7618,436
Optional5,5971,8923,3687,90011,967
Property Paths46454167384,612
C2RPQ1871,1136322,755

Supported query language

Trillian implements a growing subset of SPARQL 1.1. The basic-graph-pattern core is provably correct — 930 WDBench queries, zero result deviations.

Currently supported

  • SELECT and ASK; projection, DISTINCT, LIMIT, OFFSET
  • Basic graph patterns, OPTIONAL (left joins), UNION
  • FILTER — 3-valued logic: comparisons, && / || / !, BOUND, arithmetic, IN, IF
  • String functions: STR, LANG, DATATYPE, STRLEN, UCASE, LCASE, CONTAINS, STRSTARTS, STRENDS
  • isIRI / isLiteral / isNumeric / isBlank type checks
  • ORDER BY (type-aware: numeric before lexical), with LIMIT / DISTINCT
  • Property paths: / ^ | * + ? and negated property sets
  • IRIs, typed / @lang literals, blank nodes
  • INSERT DATA / DELETE DATA via WAL

On the roadmap

  • BIND, sub-SELECT, and nested/multiple OPTIONAL patterns
  • Aggregation: GROUP BY with COUNT / SUM / MIN / MAX / AVG
  • REGEX in FILTER
  • Turtle (.ttl) input
  • WAL checkpointing and snapshot rotation

GraphRAG with
Trillian + Mistral AI

Use Trillian as the retrieval layer in a GraphRAG pipeline. SPARQL fetches a connected, multi-hop subgraph to ground an answer generated by Mistral AI — with just the Python standard library (no API key required for retrieval).

View the tutorial

From zero to SPARQL
in two commands

Trillian requires Rust 1.85+. Clone the repo, build, and run.

bash
git clone https://github.com/42-grad/trillian.git
cd trillian

cargo build --release --bin server
cargo test
bash
# Build index from N-Triples and persist
./target/release/server build data.nt /tmp/data.bin

# Serve the snapshot on port 9090
./target/release/server load /tmp/data.bin 9090
bash
curl -G 'http://localhost:9090/sparql' \
  --data-urlencode \
  'query=SELECT ?s ?o WHERE { ?s <http://example.org/knows> ?o } LIMIT 10' \
  -H 'Accept: application/sparql-results+json'

HTTP endpoints

GET / POST/sparqlSPARQL 1.1 JSON results
GET / POST/streamNDJSON stream (header + one binding per line)
GET / POST/countResult count for SELECT / ASK
POST/updateINSERT DATA / DELETE DATA