Embedded HTAP
One process, two engines. Writes go to SQLite (OLTP); analytical SQL automatically routes to DuckDB or DataFusion (OLAP). No external servers, no replication clusters.
Pair SQLite for OLTP with DuckDB or DataFusion for OLAP. Trigger-based CDC keeps them in sync. Zero servers, zero clusters, embedded.
use yoda::{HtapConfig, HtapEngine, OlapBackendType, TableSchema};
use arrow::datatypes::{DataType, Field, Schema};
use std::sync::Arc;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut engine = HtapEngine::new(HtapConfig {
oltp_path: "./yoda.db".into(),
olap_in_memory: true,
olap_backend: OlapBackendType::DataFusion,
..Default::default()
}).await?;
// Register a table — schema replicates to OLAP, CDC triggers wire up automatically.
let schema = Arc::new(Schema::new(vec![
Field::new("id", DataType::Int64, false),
Field::new("name", DataType::Utf8, true),
]));
engine.register_table(TableSchema::new("users", schema, vec!["id".into()])).await?;
// Writes go to OLTP.
engine.execute("INSERT INTO users VALUES (1, 'Ada'), (2, 'Grace')", &[]).await?;
// Analytical queries route to OLAP automatically.
let batches = engine.query("SELECT COUNT(*) FROM users").await?;
println!("{} batch(es) returned", batches.len());
engine.shutdown().await;
Ok(())
}Same idea in Python, or yd query from the command line.
| Mode | What it does | Use when |
|---|---|---|
| Standard HTAP | Local SQLite + local OLAP, kept in sync by triggers | Embedded apps that need analytics over their own writes |
| Sidecar | Pull data from external SQLite/Postgres by updated_at, no source changes | You don't own (or can't modify) the upstream DB |
| Temporal (SCD Type 2) | Append-only OLAP with _yoda_valid_from / _yoda_valid_to | Audit history, point-in-time queries, slowly changing dimensions |
| FlightSQL server | Expose OLAP over gRPC for clients (DBeaver, ADBC, …) | Multi-client analytical access |