Skip to main content
Kora can be embedded directly into your Rust application as a library, eliminating network overhead and providing sub-microsecond dispatch latency. The embedded API wraps the same multi-threaded shard engine that powers kora-server, but routes commands through direct function calls instead of TCP.

How It Works

Each Database instance owns a ShardEngine with a configurable number of shard worker threads. Keys are hashed to a shard, and commands are sent over an mpsc channel to that shard’s worker. The calling thread blocks on a oneshot channel until the worker replies, giving synchronous semantics with lock-free, shared-nothing execution.

Embedded vs. Server Mode

Aspectkora-embeddedkora-server
TransportDirect function callsTCP / Unix socket (RESP2)
LatencySub-microsecond dispatchNetwork round-trip
DeploymentLinked into your binarySeparate process
ProtocolRust types (Command / CommandResponse)Wire-compatible RESP2

Quick Start

Add kora-embedded to your Cargo.toml:
[dependencies]
kora-embedded = "0.1"

Opening a Database

use kora_embedded::{Config, Database};

let db = Database::open(Config::default());
The default configuration sets shard_count to the number of available hardware threads. You can customize it:
let db = Database::open(Config { shard_count: 4 });

String Operations

Basic Get/Set

db.set("user:1", b"Augustus");
let val = db.get("user:1"); // Some(b"Augustus")
assert_eq!(val, Some(b"Augustus".to_vec()));

Set with Expiration

use std::time::Duration;

db.set_ex("session:123", b"token-xyz", Duration::from_secs(3600));

Atomic Counters

db.set("counter", b"0");
db.incr("counter").unwrap(); // 1
db.incr("counter").unwrap(); // 2
db.incrby("counter", 10).unwrap(); // 12
db.decr("counter").unwrap(); // 11

Batch Operations

// Set multiple keys
db.mset(&[
    ("user:1:name", b"Augustus"),
    ("user:1:city", b"Accra"),
]);

// Get multiple keys
let values = db.mget(&["user:1:name", "user:1:city", "missing"]);
// [Some(b"Augustus"), Some(b"Accra"), None]

String Utilities

db.set("greeting", b"hello");
let new_len = db.append("greeting", b" world"); // 11
let len = db.strlen("greeting"); // 11

let old = db.getset("greeting", b"hi"); // Some(b"hello world")

Hash Operations

db.hset("profile:1", "name", b"Augustus");
db.hset("profile:1", "city", b"Accra");
db.hset("profile:1", "age", b"30");

let name = db.hget("profile:1", "name"); // Some(b"Augustus")
let exists = db.hexists("profile:1", "email"); // false
let len = db.hlen("profile:1"); // 3

let all = db.hgetall("profile:1");
// [(b"name", b"Augustus"), (b"city", b"Accra"), (b"age", b"30")]

db.hdel("profile:1", &["age"]); // 1

// Atomic increment
db.hset("stats:1", "views", b"100");
db.hincrby("stats:1", "views", 5).unwrap(); // 105

List Operations

db.lpush("queue", &[b"task-1", b"task-2"]); // 2
db.rpush("queue", &[b"task-3"]); // 3

let tasks = db.lrange("queue", 0, -1);
// [b"task-2", b"task-1", b"task-3"]

let first = db.lpop("queue"); // Some(b"task-2")
let last = db.rpop("queue"); // Some(b"task-3")

let len = db.llen("queue"); // 1
let item = db.lindex("queue", 0); // Some(b"task-1")

Set Operations

db.sadd("tags", &[b"rust", b"cache", b"rust"]); // 2 (deduplicated)

let is_member = db.sismember("tags", b"rust"); // true
let count = db.scard("tags"); // 2

let members = db.smembers("tags");
// [b"rust", b"cache"] (order not guaranteed)

db.srem("tags", &[b"cache"]); // 1

Document Database

Kora includes a JSON-native document database with secondary indexes and query capabilities.

Creating Collections and Inserting Documents

use serde_json::json;

db.doc_create_collection("users", Default::default())?;

db.doc_set("users", "user:1", &json!({
    "name": "Augustus",
    "age": 30,
    "city": "Accra"
}))?;

db.doc_set("users", "user:2", &json!({
    "name": "Kwame",
    "age": 25,
    "city": "Kumasi"
}))?;

Reading Documents

// Get full document
let doc = db.doc_get("users", "user:1", None)?;
// Some({"name": "Augustus", "age": 30, "city": "Accra"})

// Get with field projection
let doc = db.doc_get("users", "user:1", Some(&["name", "city"]))?;
// Some({"name": "Augustus", "city": "Accra"})

// Batch get
let docs = db.doc_mget("users", &["user:1", "user:2", "missing"])?;
// [Some(...), Some(...), None]

Creating Indexes and Querying

// Create indexes
db.doc_create_index("users", "city", "hash")?;
db.doc_create_index("users", "age", "sorted")?;

// Query with WHERE clause
let results = db.doc_find(
    "users",
    "city = \"Accra\"",
    None,      // projection
    Some(10),  // limit
    0,         // offset
    None,      // order_by
    false      // order_desc
)?;

// Complex queries
let results = db.doc_find(
    "users",
    "age > 20 AND city = \"Accra\"",
    Some(&["name", "age"]),
    Some(10),
    0,
    Some("age"),
    true  // descending
)?;

// Count matching documents
let count = db.doc_count("users", "age >= 25")?;

Document Updates

use kora_embedded::DocUpdateMutation;

db.doc_update(
    "users",
    "user:1",
    &[
        DocUpdateMutation::Set {
            path: "city".to_string(),
            value: json!("London"),
        },
        DocUpdateMutation::Incr {
            path: "age".to_string(),
            delta: 1.0,
        },
    ]
)?;
// Insert vectors
let v1 = vec![1.0f32, 0.0, 0.0, 0.0];
let v2 = vec![0.0f32, 1.0, 0.0, 0.0];
let v3 = vec![1.0f32, 1.0, 0.0, 0.0];

let id1 = db.vector_set("embeddings", 4, &v1)?;
let id2 = db.vector_set("embeddings", 4, &v2)?;
let id3 = db.vector_set("embeddings", 4, &v3)?;

// Search for nearest neighbors
let query = vec![0.9f32, 0.1, 0.0, 0.0];
let results = db.vector_search("embeddings", &query, 3)?;
// [(id1, 0.0141), (id3, 0.2928), (id2, 1.2728)]

// Delete index
db.vector_del("embeddings")?;

Key Management

// Check existence
let exists = db.exists("user:1"); // true

// Delete keys
let deleted = db.del("user:1"); // true

// TTL management
db.expire("session:123", 3600); // expires in 1 hour
let ttl = db.ttl("session:123"); // Some(3600)
db.persist("session:123"); // remove TTL

// Get key type
let typ = db.key_type("profile:1"); // "hash"

// Pattern matching
let keys = db.keys("user:*");

// Database stats
let size = db.db_size(); // total key count
db.flush_db(); // delete all keys

Hybrid Mode

With the server feature enabled, you can embed the database AND expose a TCP listener for external clients:
[dependencies]
kora-embedded = { version = "0.1", features = ["server"] }
use kora_embedded::{Config, Database};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let db = Database::open(Config { shard_count: 4 });
    
    // Start TCP listener (non-blocking)
    let (server_handle, shutdown_tx) = db.start_listener("127.0.0.1:6379")?;
    
    // Use embedded API directly
    db.set("key", b"works from both paths");
    
    // External clients can connect via redis-cli or any Redis client
    
    // Shutdown when done
    shutdown_tx.send(true)?;
    server_handle.await?;
    
    Ok(())
}
In hybrid mode, both the embedded API and external TCP clients can access the same data through the shared ShardEngine.

Thread Safety

All Database methods are safe to call from multiple threads. Wrap the database in an Arc for shared access:
use std::sync::Arc;
use std::thread;

let db = Arc::new(Database::open(Config { shard_count: 4 }));

let handles: Vec<_> = (0..4).map(|t| {
    let db = db.clone();
    thread::spawn(move || {
        for i in 0..100 {
            let key = format!("t{}:k{}", t, i);
            db.set(&key, b"value");
            assert!(db.get(&key).is_some());
        }
    })
}).collect();

for h in handles {
    h.join().unwrap();
}

Performance Considerations

  • Shard count: Set to the number of CPU cores for optimal throughput. Each shard runs on its own OS thread.
  • Key distribution: Kora uses a consistent hash to distribute keys across shards. Hot keys on the same shard won’t benefit from parallelism.
  • Blocking calls: All operations block the calling thread until the shard worker responds. For async code, spawn blocking tasks or use Tokio’s spawn_blocking.
  • Memory: Each shard maintains independent storage. Total memory usage scales with shard count and key count.

API Reference

For the complete API documentation, run:
cargo doc --open -p kora-embedded
Or explore the source at kora-embedded/src/lib.rs.

Build docs developers (and LLMs) love