Gunnar

nordisk/knut

knut — a knytpunkt over graphs

knut

A pure-Rust columnar ↔ graph fabric — read/write Apache GraphAr, bulk-load FalkorDB, and serve Cypher over Arrow Flight SQL, with Bifröst streaming Iceberg tables into a live graph by CDC (Spark-Connect or Spark-free). One Arrow pipe from the lakehouse to a queryable graph.

⚡ part of the nordisk constellation

Mashup — one Arrow pipe: columnar lake → live graph

knut carries graph data the whole way from a columnar lake to a live, queryable graph in one Arrow pipe (no JVM required) — the rows marked NA are categories the rivals have no concept for:

capabilityknutGraphAr (official)Neo4j / GDSSpark + GraphFrames
Pure-Rust GraphAr — 5 formats (Parquet/CSV/ORC/JSON/Arrow), local or object store✅◐ (C++/Java/Py)✗◐
Cypher over Arrow Flight SQL server — ADBC / Power BI / DataFusion clients, Arrow out✅NA◐✗
Iceberg → graph CDC fabric — snapshot-incremental + changelog (_change_type → MERGE / DETACH DELETE)✅NA✗◐
Spark-free Iceberg ingestion (skade IcebergSource, no JVM)✅NA✗✗
Bulk columnar RecordBatch → live graph DB (FalkorDB UNWIND/CREATE)✅◐✅✅
Mature graph-algorithm library + production graph engine◐✗✅✅

Rows marked NA: the rival has no such category. The measured numbers are below.

Benchmarks

v0.1.3 · oden · 32 cores · 2026-07-23

hostoden · 32 cores
version0.1.3
measured2026-07-23
arms29
borrowed arms10 named nornir.* — measured here but owned by nornir
tests29/29 passed
publishableyes
flagged implausible2

⚠ 2 arms carrying a number that is not credible as real work. Each is marked ⚠ or withheld wherever it appears below — never published as a measurement:

armwhat is not credible
bifrost_fabricrows_per_s = 6.698e9/s derived from a 1.49e-5 s interval — under the 1e-4 s timing floor, so the rate is arithmetic, not measurement
merge_property_groupsrows_per_s = 1.067e10/s derived from a 9.37e-6 s interval — under the 1e-4 s timing floor, so the rate is arithmetic, not measurement

ℹ 10 arms in this run belong to another repo (10 × nornir.*). They are the harness's own suite, pulled in because the bench runner this repo pins predates the arm filter that would have excluded them — so they were measured here without being asked for. They describe nornir, not knut, and are shown separately below rather than mixed into this repo's numbers.

The Arrow pipe — knut's own arms

Storage formats round-tripped through the GraphAr writer/reader, the Bifröst CDC fabric, the FalkorDB bulk path, and the Thund streaming window operators.

v0.1.3 · oden · 32 cores · 2026-07-23

workloadrows_per_sread_rows_per_swrite_rows_per_sincremental_rows_per_srecompute_rows_per_scheckpointed_rows_per_srestore_resume_rows_per_sspeedup_xtelem_cores_busy_avgtelem_elapsed_ms
builder_dump1.42M171.43
cypher_render3.21M138.22
doput_dispatch0.851.604766
falkor_parallel_dispatch29.751801.77
file_source_scan48.55M120.62
format_arrow170.32M77.14M110.89
format_csv8.18M5.44M242.19
format_json1.08M8.44M2113.17
format_orc63.84M47.38M112.46
format_parquet45.57M7.16M224.88
graphar_vertex_load35.85M254.49
ingest_lifecycle36.05M224.69
thund_checkpoint14.11M2157.51
thund_incremental_agg6.13M1.80M1.501467
thund_recovery33.59M1129.08
thund_watermark_drop2.22M3.81M136.46
thund_window_close16.58M31.40M1193.79

⚠ withheld from this table — not credible as real work:

telem_cores_busy_avg is the average number of CPU cores busy while the arm ran — a single-digit value on a 32-core host means the arm is serial by construction, not that the box was idle. Blank cells are metrics that arm does not report.

Borrowed arms — nornir's own suite, measured here by accident

v0.1.3 · oden · 32 cores · 2026-07-23

workloadelapsed_msrows_per_secqueries_per_secsymbols_per_secevents_per_secspeedup_vs_fulltelem_cores_busy_avg
nornir.delta_rescan8.192.700
nornir.dep_graph_build24.073650851
nornir.dep_graph_build_ext31.883446371
nornir.funnel_append128449.841
nornir.funnel_append_batch1.23521370
nornir.knowledge_query16.85237421
nornir.knowledge_scan1.786745820
nornir.plan_dag_assemble107.380
nornir.symbol_append4.592396180
nornir.warehouse_query1.224105610

These arms measure nornir, not knut. knut/xtask pins nornir = "0.5" from crates.io, and that published runner predates the NORNIR_BENCH_ONLY arm filter — so the filter was accepted and silently ignored, and nornir's suite ran alongside knut's. They are kept in their own table so they can never be read as knut's numbers; the fix is a newer runner, not an edit to this file.

SQL for Power BI lives next door (2026-09-09)

knut's Flight SQL server (graphar-flight) speaks Cypher — the graph. Power BI, the Arrow Flight SQL ODBC driver and ADBC speak SQL, and that dialect is skade-flight in the skade repo: DataFusion over the Iceberg warehouse, streamed Arrow, ODBC escapes translated, 129 Power Query statement shapes green. Two dialects, two services, one wire; korp mounts both. The knut bridge — graph tables as DataFusion table providers in that same session — is the next step, not done.

Read the real docs

Files

.github/
.nornir/
assets/
containers/
crates/
demo/
deploy/
docs/
python/
vendor/
xtask/
.gitattributes
.gitignore
Cargo.lock
Cargo.toml
README.md
brokkr.toml
rustfmt.toml

The list is cut short here. Clone the repository for every file.

Newest commits

037d6b448393Merge fix/small-4-2026-10-25 into main: no file addresses our repositories on the old forge (A28 N7-left)2026-10-25
dcaebd10632dGREEN: the README links skade's real home (A28 N7-left; guard 4/0; docs only)2026-10-25
13c7370963e8TEST: no file addresses our repositories on the old forge (A28 N7-left; RED: README.md:124)2026-10-25
900bfd3509f4Merge v2/n7-a28-small-2026-10-25: every repository = names this repository's real home, with a guard test (A28 L17)2026-10-25
532ef5068e34GREEN: every repository = names this repository's home (A28 L17)2026-10-25