SparKV is an expirable in-memory key-value store for Rust with no dependencies.
- Zero dependencies — pure Rust
std, nothing added to your dependency tree. - Flexible expiration duration (a.k.a. time-to-live or TTL) per entry instead of a database-wide common TTL.
- This is similar to DNS, where each entry of the same domain can have its own unique TTL.
- Automatically clears expired entries by default.
- Generic over the value type, defaulting to
String. Store any type you like. - Fast data-entry enforcements, including entry size, database size and max TTL.
- SparKV is intentionally not an LRU cache.
- Configurable.
Add SparKV to your Cargo dependencies:
$ cargo add sparkvQuick start:
use sparkv::SparKV;
// The value type defaults to String.
let mut sparkv = SparKV::new();
sparkv.set("your-key", "your-value".to_string()).unwrap(); // write
let value = sparkv.get("your-key").unwrap(); // read
// Write with a unique TTL
sparkv.set_with_ttl("diff-ttl", "your-value".to_string(), std::time::Duration::from_secs(60)).unwrap();set takes the value by value, and get returns an owned clone (so the value type
must implement Clone).
SparKV is generic over the value type. Built-in types (integers, Vec<u8>, etc.)
work out of the box:
use sparkv::SparKV;
let mut counters: SparKV<i64> = SparKV::new();
counters.set("hits", 42).unwrap();
assert_eq!(counters.get("hits"), Some(42));To store your own type, implement ValueSize so SparKV can enforce max_item_size
(return 0 if you don't care about size enforcement):
use sparkv::{SparKV, ValueSize};
#[derive(Clone)]
struct Session {
user_id: u64,
csrf: String,
}
impl ValueSize for Session {
fn value_size(&self) -> usize {
std::mem::size_of::<u64>() + self.csrf.len()
}
}
let mut sessions: SparKV<Session> = SparKV::new();
sessions
.set_with_ttl(
"sid",
Session { user_id: 1, csrf: "abc".to_string() },
std::time::Duration::from_secs(60),
)
.unwrap();| Method | Description |
|---|---|
set(key, value) |
Insert/overwrite using config.default_ttl. |
set_with_ttl(key, value, ttl) |
Insert/overwrite with an explicit TTL. |
get(key) -> Option<V> |
Owned clone of a live (non-expired) value. |
get_item(key) -> Option<&KvEntry<V>> |
Borrow the live entry, including its expired_at. |
delete(key) -> Option<V> |
Remove and return the value. |
contains_key(key) -> bool |
Expiry-aware presence check (agrees with get). |
len() / is_empty() |
O(1) physical count (may include expired-but-unswept entries). |
clear_expired() -> usize |
Sweep expired entries, returning how many were removed. |
set / set_with_ttl return Result<(), Error>, where Error
is one of CapacityExceeded, ItemSizeExceeded, or TTLTooLong.
Construct a store with defaults via SparKV::new(), or supply your own Config
with SparKV::with_config(config):
use sparkv::{Config, SparKV};
let mut config = Config::new();
config.max_items = 50_000;
config.max_item_size = None; // disable per-entry size enforcement
config.default_ttl = std::time::Duration::from_secs(30);
let mut sparkv: SparKV = SparKV::with_config(config);| Field | Type | Default | Purpose |
|---|---|---|---|
max_items |
usize |
10_000 |
Maximum number of entries before set returns CapacityExceeded. |
max_item_size |
Option<usize> |
Some(500_000) |
Maximum per-entry size (via ValueSize); None disables the check. |
max_ttl |
Duration |
1 hour |
Upper bound on any entry's TTL; exceeding it returns TTLTooLong. |
default_ttl |
Duration |
5 minutes |
TTL applied by set when none is given. |
auto_clear_expired |
bool |
true |
Sweep expired entries automatically on writes/deletes. |
0.2.0 makes the value type generic (SparKV<V>, defaulting to String). The store
behaves exactly as before; the breaking changes are limited to how values are passed in
and how max_item_size is configured. The SparKV and KvEntry type names still
resolve to their String forms, so type annotations and struct fields are unaffected.
1. set / set_with_ttl now take the value by value. Pass an owned value instead
of a &str:
// 0.1
kv.set("key", "value");
// 0.2
kv.set("key", "value".to_string()); // or "value".into()get and delete still return Option<String> for the default String store, so
your read sites do not change.
2. Config.max_item_size is now Option<usize>. Wrap the limit in Some, or use
None to disable size enforcement:
// 0.1
config.max_item_size = 500_000;
// 0.2
config.max_item_size = Some(500_000); // or None to disable3. Custom value types need a ValueSize impl — only relevant if you switch away from
String. Built-in types (integers, Vec<u8>, &[u8], etc.) already implement it; see
the generic example above.
No other call sites change.
See CHANGELOG.md for the release history.
MIT License
Copyright © 2024 U-Zyn Chua