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.
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.
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.
Three CSR permutation indexes (SPO / POS / OSP), u32 IDs, no pointer chasing. Flat contiguous vectors keep the data cache-hot and predictable.
Worst-case-optimal joins (leapfrog triejoin) for cyclic patterns; a pipelined, cost-based plan for the rest. Picks the right algorithm per query.
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.
Sub-millisecond entity lookups. Single-pattern WDBench queries answer in ~1 ms median. Full Wikidata queries complete in seconds, not minutes.
INSERT DATA / DELETE DATA via a write-ahead log. fsync'd operations survive a restart without rewriting the immutable snapshot.
Apache 2.0 licensed. Run it on your own infrastructure. Built by 42grad GmbH as a building block for sovereign, sustainable data infrastructure.
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.
N-Triples are parsed; each term is interned to a u32 ID via the dictionary.
Three flat-CSR permutations (SPO, POS, OSP) built in contiguous u32 vectors.
Index and dictionary written to a single file, then memory-mapped for zero-copy serving.
SPARQL parsed, planner picks joins, hybrid engine executes — WCOJ for cycles, pipelined for trees.
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.
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]>.
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.
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.
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.
Result correctness — per-query result counts against published numbers (below 100k must match exactly).
On-disk footprint — store size in bytes per triple, independent of hardware.
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.
Architecture differs — Trillian is in-memory; the others are disk-backed with 64 GB RAM. Much of the latency gap is architectural.
| Class | Match | Diff |
|---|---|---|
| Single BGP | 267/267 | 0 |
| Multiple BGP | 661/661 | 0 |
| Optional | 239/315 | 76 |
| Property Paths | 381/393 | 12 |
| C2RPQ | 232/283 | 51 |
| Engine | Size | B/triple |
|---|---|---|
| Trillian | 49 GB | 39 |
| Blazegraph | 70 GB | 56 |
| Virtuoso | 70 GB | 56 |
| Jena (TDB) | 110 GB | 87 |
| Neo4j | 112 GB | 89 |
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).
"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.
| Class | Trillian | Others | Capped |
|---|---|---|---|
| Single BGP (280) | 267 | 280 | 13 |
| Multiple BGP (681) | 665 | 677 | 16 |
| Optional (498) | 315 | 498 | 183 |
| Property Paths (660) | 397 | 560 | 263 |
| C2RPQ (539) | 308 | 508 | 231 |
| Class | Tril. | Blaz. | Jena | Virt. | Neo4j |
|---|---|---|---|---|---|
| Single BGP | 1 | 69 | 279 | 261 | 642 |
| Multiple BGP | 131 | 1,166 | 2,761 | 8,436 | — |
| Optional | 5,597 | 1,892 | 3,368 | 7,900 | 11,967 |
| Property Paths | 4 | 645 | 416 | 738 | 4,612 |
| C2RPQ | 187 | 1,113 | 632 | 2,755 | — |
Trillian implements a growing subset of SPARQL 1.1. The basic-graph-pattern core is provably correct — 930 WDBench queries, zero result deviations.
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 tutorialTrillian requires Rust 1.85+. Clone the repo, build, and run.
git clone https://github.com/42-grad/trillian.git
cd trillian
cargo build --release --bin server
cargo test# 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 9090curl -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'GET / POST/sparqlSPARQL 1.1 JSON resultsGET / POST/streamNDJSON stream (header + one binding per line)GET / POST/countResult count for SELECT / ASKPOST/updateINSERT DATA / DELETE DATA