The Embedded
Streaming Database
Sub-microsecond SQL stream processing. No cluster required.
Why LaminarDB
Think DuckDB, but for streaming. Embed a full streaming database directly in your application.
Sub-Microsecond Latency
Zero allocations on the hot path. No GC pauses, no JVM warmup. 325ns measured through a 3-node pipeline.
< 1µs hot path • 325ns measuredEmbedded-First
Single binary, no cluster, no external dependencies. Embed in your application like DuckDB.
cargo add laminar-dbSQL-Native Streaming
Full SQL via DataFusion with streaming extensions: TUMBLE, HOP, SESSION, EMIT, ASOF JOIN, and 30+ aggregates.
DataFusion + streaming SQLExactly-Once Guaranteed
Write-ahead log, incremental checkpointing, two-phase commit, and co-transactional sinks.
WAL + 2PC + epoch trackingWhy DuckDB for Streaming?
DuckDB changed analytics by proving you don't need a cluster to run fast SQL queries — just embed it in your process. No deployment, no network hops, no operational overhead.
Streaming has the same problem today. Running real-time aggregations or CDC pipelines typically means deploying and maintaining a distributed system like Apache Flink or RisingWave — even when your workload fits on a single machine.
LaminarDB takes the DuckDB approach for stream processing:
- Add a dependency, not a cluster.
cargo add laminar-dbgives you windowed aggregations, stream joins, watermarks, and exactly-once semantics inside your application. - No serialization boundary. Data stays in Apache Arrow columnar format in your process. No network encoding/decoding overhead.
- Same SQL you know. Full DataFusion SQL engine with streaming extensions (TUMBLE, HOP, SESSION, EMIT, ASOF JOIN).
- Production semantics. Write-ahead log, incremental checkpointing, and two-phase commit — the same guarantees distributed systems provide, without the distributed part.
If your streaming workload fits on one machine, you don't need a cluster. Most workloads do.
Thread-Per-Core Architecture
Most streaming systems use thread pools with shared state, which means locks, contention, and unpredictable latency spikes from context switching. LaminarDB uses a thread-per-core model instead — the same architecture behind ScyllaDB, Redpanda, and Seastar.
How it works
- One reactor per CPU core. Each core runs its own event loop, pinned with CPU affinity. No thread migration, no cache thrashing.
- Shared-nothing state. Each core owns its partition of the keyspace. No locks anywhere on the hot path.
- SPSC queues between cores. Lock-free single-producer single-consumer queues (~4.8ns per operation) for cross-core communication.
- Zero allocations on hot path. Arena allocators and pre-allocated buffers. A debug-mode allocation detector verifies this in CI.
Why it matters
- Predictable latency. No GC pauses (Rust), no lock contention, no thread pool scheduling jitter. The p99 stays close to the median.
- Linear scaling. Adding cores adds proportional throughput. 4 cores = ~4x throughput, not diminishing returns from lock contention.
- Cache efficiency. Pinned cores keep working data in L1/L2 cache. No cross-core cache invalidation on the hot path.
Measured Performance
Criterion benchmarks on a 3-node DAG pipeline. All numbers from cargo bench.
See It In Action
Streaming SQL, real-time joins, embedded Rust API, and CDC pipelines.
-- Real-time OHLC bars from a trade stream
CREATE MATERIALIZED VIEW ohlc_1m AS
SELECT
symbol,
TUMBLE_START(event_time, INTERVAL '1' MINUTE) AS bar_start,
FIRST_VALUE(price) AS open,
MAX(price) AS high,
MIN(price) AS low,
LAST_VALUE(price) AS close,
SUM(volume) AS volume
FROM trades
GROUP BY
symbol,
TUMBLE(event_time, INTERVAL '1' MINUTE)
EMIT ON WINDOW CLOSE;
-- ASOF JOIN: enrich trades with latest quote
SELECT
t.symbol,
t.price AS trade_price,
t.quantity,
q.bid,
q.ask,
t.price - q.bid AS spread
FROM trades t
ASOF JOIN quotes q
ON t.symbol = q.symbol
AND t.event_time >= q.event_time
TOLERANCE INTERVAL '5' SECOND;
use laminar_db::LaminarDB;
#[tokio::main]
async fn main() -> Result<(), Box> {
let db = LaminarDB::open("./my_stream_db").await?;
// Create a streaming query
db.execute("
CREATE MATERIALIZED VIEW alerts AS
SELECT sensor_id, AVG(temperature) AS avg_temp
FROM sensors
GROUP BY sensor_id, TUMBLE(event_time, INTERVAL '10' SECOND)
HAVING AVG(temperature) > 100.0
EMIT ON WINDOW CLOSE
").await?;
// Ingest events
let source = db.source("sensors").await?;
source.push(sensor_event).await?;
// Query results
let results = db.query("SELECT * FROM alerts").await?;
println!("{:?}", results);
Ok(())
}
-- Stream changes from PostgreSQL to Kafka
CREATE SOURCE orders_cdc
WITH (
connector = 'postgres-cdc',
hostname = 'db.example.com',
port = '5432',
database = 'shop',
table = 'orders',
slot.name = 'laminar_orders'
);
CREATE SINK order_totals_kafka
WITH (
connector = 'kafka',
brokers = 'kafka:9092',
topic = 'order-totals',
format = 'json'
) AS
SELECT
customer_id,
COUNT(*) AS total_orders,
SUM(amount) AS total_spent
FROM orders_cdc
WHERE _op IN ('I', 'U')
GROUP BY customer_id
EMIT CHANGES;
Three-Ring Architecture
Strict isolation between hot path, background I/O, and control plane.
Built For Real Workloads
From financial analytics to IoT edge processing.
Financial Analytics
- OHLC bars with FIRST/LAST aggregates
- ASOF joins for trade-quote enrichment
- Cascading MVs for multi-resolution (1s, 1m, 1h)
- Keyed watermarks for 99%+ accuracy
IoT & Edge Processing
- Embedded deployment, single binary
- Local aggregation before cloud
- Sub-millisecond response times
- Thread-per-core for sensor data
Real-Time Applications
- Gaming leaderboards and scoring
- Fraud detection pipelines
- Dynamic pricing engines
- Event-driven architectures
Data Infrastructure
- CDC from PostgreSQL and MySQL
- Stream-to-lake (Delta Lake, Iceberg)
- Lightweight alternative for single-node workloads
- Kafka source and sink connectors
65 Features And Counting
Production-grade streaming across engine, SQL, durability, and connectors.
How We Compare
Different tools for different needs. LaminarDB is embedded-first and optimized for single-node, low-latency workloads.
| Feature | LaminarDB | Flink | Kafka Streams | RisingWave | Materialize |
|---|---|---|---|---|---|
| Deployment | Embedded | Distributed | Embedded | Distributed | Distributed |
| Latency (in-process) | < 1µs | N/A* | ~1ms | N/A* | N/A* |
| SQL Support | ✓ Full | Limited | ✗ None | ✓ Full | ✓ Full |
| Exactly-Once | ✓ | ✓ | ✓ | ✓ | ✓ |
| Thread-per-Core | ✓ | ✗ | ✗ | ✗ | ✗ |
| Language | Rust | Java | Java | Rust | Rust / C++ |
| Lakehouse | Planned | Limited | ✗ | ✓ | ✗ |
| Embedded Mode | ✓ | ✗ | ✓ | ✗ | ✗ |
| License | MIT | Apache 2.0 | Apache 2.0 | Apache 2.0 | BSL |
* Flink, RisingWave, and Materialize are distributed systems — their latency includes network hops and is not directly comparable to in-process embedded latency. Kafka Streams is the closest comparison as it is also embedded.
Get Started
Up and running in three steps.
Add the dependency
cargo add laminar-db
Create and query
use laminar_db::LaminarDB;
#[tokio::main]
async fn main() -> Result<(), Box> {
let db = LaminarDB::open("./my_db").await?;
db.execute("
CREATE MATERIALIZED VIEW summary AS
SELECT key, COUNT(*) AS total
FROM events
GROUP BY key, TUMBLE(event_time, INTERVAL '1' MINUTE)
EMIT ON WINDOW CLOSE
").await?;
let results = db.query("SELECT * FROM summary").await?;
println!("{:?}", results);
Ok(())
}
Run it
cargo run
Open Source.
MIT.
LaminarDB is open source and free to use. Contributions welcome.