| Crates.io | ferrokv |
| lib.rs | ferrokv |
| version | 1.0.31 |
| created_at | 2025-11-30 13:54:59.142892+00 |
| updated_at | 2025-12-09 20:43:36.331853+00 |
| description | The embedded, async, Redis-inspired key-value store for Rust. |
| homepage | |
| repository | https://github.com/xosnrdev/ferrokv |
| max_upload_size | |
| id | 1958245 |
| size | 207,450 |
The embedded, async, Redis-inspired key-value store for Rust.
Ferrokv is designed to fill a specific gap in the ecosystem: a persistent database that offers the ergonomic semantics of Redis (TTL, atomic increments, lists) but runs embedded within your application binary, fully integrated with the tokio async runtime.
We believe you shouldn't have to choose between data safety (ACID) and system performance (Non-blocking I/O).
If you need a persistent store but don't want the operational overhead of managing an external Redis server sidecar, Ferrokv is built for you. It respects the filesystem as the source of truth and the executor as the source of concurrency.
Ferrokv is built on six non-negotiable pillars:
tokio. I/O operations utilize io_uring (on supported Linux) or non-blocking thread pools. db.set().await never blocks your executor.fsync guarantees before acknowledgment.SETEX, INCR, GET).io_uring Support (Linux)On Linux, Ferrokv can leverage io_uring for high-performance asynchronous I/O. This feature is powered by tokio's native io-uring integration, which is currently marked as unstable.
See: https://github.com/tokio-rs/tokio/discussions/7684
See: https://github.com/tokio-rs/tokio/pull/7621
To enable, add the feature in your Cargo.toml:
[dependencies]
ferrokv = { version = "1", features = ["io-uring"] }
Then, set RUSTFLAGS="--cfg tokio_unstable" in your environment to enable unstable tokio features.
Add this to your Cargo.toml:
[dependencies]
ferrokv = "1"
tokio = { version = "1", features = ["full"] }
Ferrokv feels like a Redis client, but the database lives inside your app.
use std::time::Duration;
use ferrokv::FerroKv;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// 1. Open the database at the given directory
// with default settings (creates ./data/ directory if missing)
let db = FerroKv::with_path("./data").await?;
// 2. Standard Key-Value ops
db.set(b"user:101", b"Alice").await?;
// 3. With TTL
// The key auto-expires from disk after 60 seconds.
db.set_ex(b"session:xyz", b"active", Duration::from_secs(60)).await?;
// 4. Atomic Increment
let visits = db.incr(b"site:visits").await?;
println!("Visits: {visits}");
// 5. Scan
let results = db.scan(..).await?;
for (key, value) in &results {
println!("{} = {}", String::from_utf8_lossy(key), String::from_utf8_lossy(value));
}
// 6. Delete
db.del(b"user:101").await?;
Ok(())
}
Use WriteBatch to amortize the cost of fsync across multiple operations:
use std::time::Duration;
use ferrokv::{FerroKv, WriteBatch};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let db = FerroKv::with_path("./data").await?;
// Collect multiple writes into a batch
let mut batch = WriteBatch::new();
batch
.set(b"foo:1", b"bar")
.set(b"foo:2", b"baz")
.set_ex(b"foo:3", b"qux", Duration::from_secs(3600))
.del(b"foo:2");
// Execute with single fsync
db.write_batch(batch).await?;
Ok(())
}
Ferrokv avoids complex locking schemes by using MVCC (Multi-Version Concurrency Control).
For a deep dive into the design philosophy and file format, strictly read the WHITEPAPER.md.
We use Mimalloc as the default allocator.
The standard system allocator often suffers from lock contention when used with the "work-stealing" patterns of the tokio runtime. Mimalloc is required to eliminate this contention and guarantee the low-latency performance constraints of this project.
This project is licensed under the MIT License.