Expand description
kevy-embedded — kevy without the network.
In-process Redis-compatible key–value store: load + reply directly from your own threads, no TCP, no shards, no reactor. Use this when you want kevy’s data structures + persistence in the same address space as your app — caches, embedded databases, WASM blobs, sidecar tools.
Zero crates.io dependencies: only kevy-store (the keyspace)
and kevy-persist (snapshot + AOF). The whole network layer
(kevy-rt, kevy-sys, kevy-uring) is intentionally NOT pulled in.
§Quick start
use kevy_embedded::{Store, Config};
let s = Store::open(Config::default())?;
s.set(b"greeting", b"hello")?;
assert_eq!(s.get(b"greeting")?, Some(b"hello".to_vec()));§With persistence
with_persist(dir) enables AOF auto-append on every write and replays
on open — restart-safe out of the box. Snapshot (dump-0.rdb) is
loaded first if present; AOF (aof-0.aof) is replayed on top.
use kevy_embedded::{Store, Config};
let s = Store::open(Config::default().with_persist("./data"))?;
s.set(b"counter", b"42")?;
drop(s); // flushes AOF on drop
// Next process: state survives.
let s2 = Store::open(Config::default().with_persist("./data"))?;
assert_eq!(s2.get(b"counter")?, Some(b"42".to_vec()));§When NOT to use this crate
- You want a Redis-protocol TCP server → use the
kevycrate’sserveinstead. - You need cross-process concurrency → kevy-embedded is single-process (one mutex). Multi-process needs the network layer.
§Locking & concurrency
The keyspace is split into Config::shards independent shards, each a
kevy_store::Store behind its own RwLock (default: 1 shard = one
lock over the whole keyspace). A key maps to its shard by hash; writes take
that shard’s exclusive lock.
Reads take a shared per-shard lock where it’s sound to. GET (and the
FFI zero-copy get_shared lane) use the shared lock whenever the active
eviction policy won’t consume a per-read LRU/LFU tick — maxmemory == 0
(the default), or the NoEviction / *Random / VolatileTtl policies. The
true LRU/LFU policies (*Lru / *Lfu) instead take the exclusive lock so
each access stamps the clock the eviction scorer ranks by. Read-only
aggregations (DBSIZE, used_memory, the INFO counters) likewise take
shared locks so a full-keyspace scan doesn’t stall concurrent writers.
This is a lock-correctness property, not a throughput one: a read-only
operation doesn’t hold the exclusive lock against a concurrent writer on its
shard. It is not a lock-free read path — concurrent readers still
contend on the shard’s RwLock word (a shared cache line), so read scaling
is bounded by shard count, not core count. To spread read/write
contention across cores, raise Config::shards.
Known limitation: the sibling reads (hget, exists, smembers,
zscore, llen, scard, zcard, type_of, ttl_ms, …) currently still
take the shard’s write lock even though the underlying keyspace methods
are read-only. Moving them onto the shared lane is a tracked follow-up
(bench-gated separately from the GET lane above).
§Cargo features
default is the full surface. For constrained targets (IoT / edge)
cut it down with default-features = false, features = [...]:
| feature | adds |
|---|---|
core | in-memory KV + TTL + pub/sub + pipeline/atomic (the minimal base) |
persist | snapshot + AOF durability (with_persist, replay on open) |
index | secondary indexes + views |
text | full-text index segments (implies index) |
vector | HNSW vector index segments (implies index) |
replicate | embed-as-replica / embed-as-writer + CDC feed (implies persist) |
listener | the read-only RESP listener |
Structs§
- AnnSpec
- HNSW declaration (immutable once created).
- Atomic
AllShards - Context handed to the
atomic_all_shardsclosure body. Methods route to the right shard by hashing the key. - Atomic
Ctx - Handle passed to the
atomicclosure body. Methods mirror the equivalentStoreops but operate on the already-held write lock, so reads inside the block see the closure’s own writes. - Change
- One mutation delivered by
Store::changes_since. - Change
Batch - A batch of changes plus the cursor to resume from.
- Config
- Embedded-store config. Build by chaining
with_*methods onConfig::default. - Expire
Stats - What
Store::tick_expiresaw and did. Surfaced for tests, INFO keyspace, and (eventually) Wave 2 task #4’s crash-safe verifier. - Group
Stats - One group’s live statistics.
- Index
Cursor - Opaque pagination cursor: the last
(value, key)served. Encoded by the runtime into the wire cursor;None= start. - Index
Stats - Sizing + health counters (
IDX.LIST/ memory formula). - Index
Verify - Per-index verification counters, one row of
TABLE.VERIFY. - Kevy
Info - Snapshot of a store’s runtime counters, returned by
Store::info. A cheap aggregate (one mutex lock); fields mirror the individual accessors. - Kevy
Tier Info - The
# Tieringgauge set (B12) — summed across shards; field names mirror the server’sINFO # Tieringsection one-to-one. - Match
Opts - Everything a text MATCH carries beyond its index, query text and result limit — the embedded twin of the wire’s optional clauses.
- Match
Page - A faceted query’s answer: the page, and per requested
FACETfield its(value, count)buckets over the whole match set. - Open
Report - What
Store::openrestored — and what it could not. The pull-style twin ofKevyMetric::Replay(Store::open_report()), so a host can turn “the AOF lost bytes at boot” into a health-check verdict without wiring a metric sink or scraping stderr. - Order
Path - One composite-sort path (
ORDERPATH— cookbook §8 mechanized): compiles to a composite Range index named<table>.<name>. - Pipeline
- Builder-style write queue. Returned by
Store::pipeline; call fluent methods to enqueue +commit()to apply with batched AOF fsync. - Prefix
Info - Per-prefix keyspace stats from
Store::info_prefix. - Reconcile
Report - The result of
Snapshot::reconcile. - Rewrite
Stats - Result of an
Aof::rewrite_fromcall. Surfaced byBGREWRITEAOF/INFO persistence. - Scalar
Page - A clause-carrying query’s answer: the page, per requested
FACETfield its(value, count)buckets, and — on the FILTER-with-cursor path — the cursor to resume from. - Scalar
Query Opts - Everything a scalar RANGE/EQ query carries beyond its bounds and
limit — the embedded twin of the wire’s optional clauses.
ScalarQueryOpts::defaultis the plain query. - Score
Bound - A score-range endpoint for
ZRANGEBYSCORE/ZCOUNT(inclusive or exclusive). Usevalue = ±INFINITYfor-inf/+inf. - Snapshot
- A frozen, consistent point-in-time view of the whole store.
- Snapshot
Entry - One entry from
Snapshot::each_prefix/Snapshot::keys_prefix. - Store
- The embedded keyspace.
- Subscription
- A handle to one subscription — owns the receive end of the bus channel.
- Table
Index - One declared secondary index: a column and a scalar kind, plus the
stored
VALUEScolumns residual FILTER/SORT read. - Table
Spec - One declared table.
- Table
Verify - The whole
TABLE.VERIFYanswer. - View
Leaf - One leaf: a declared index + the shape it contributes.
- Weak
Store - Weak handle to a
Store— does not keep the underlying keyspace alive. - Zadd
Flags - Parsed
ZADDcondition flags.CHonly changes the reply (changed count instead of added count) — callers readZaddReport::changedwhen set; the engine behavior is identical. - Zadd
Report - Outcome of a flags-aware
ZADD.
Enums§
- AggBy
- Ranking metric for
AggSegment::top_groups. - Append
Fsync - When to fsync the AOF to disk.
- BitOp
- Operator for
Store::bitop. - Eviction
Policy - Maxmemory eviction policy. Mirror of
kevy_config::EvictionPolicy— duplicated here sokevy-storestays a leaf crate (nokevy-configdep). - Feed
Error - Why a feed read could not be served.
- GetShared
- Owned GET result for the FFI zero-copy shared lane
(
Store::get_shared_owned). Bulk values ride out as an Arc clone (no byte copy); small values as a plain Vec (one alloc — cheaper than a fresh Arc). The FFI’s shared free reconstructs whichever the tag says. - HExpire
Cond - Condition flags for
HEXPIRE(NX/XX/GT/LT; at most one). - Index
Kind - Index kind (
KIND range|unique). - Index
ValType - Declared scalar type of an index (
TYPE i64|f64|str). - Index
Value - One indexed scalar. Ordering is total within a type; the catalog guarantees a segment only ever holds one variant.
- Kevy
Error - The error type of the embeddable stack:
kevy_embedded::Storeandkevy_client::Connectionsurfaces returnResult<_, KevyError>. - Kevy
Metric - A persistence event worth observing. More variants may be added; match
non-exhaustively (
_ => {}) to stay forward-compatible. - Pubsub
Frame - One pub/sub event delivered to a
Subscription. - Store
Error - Operation errors surfaced to the command layer.
- Table
Ensure - What
table_ensurefound (the boot verb — see the embedded docs). - Tier
Budget Spec - One transparent-tiering RAM budget, as configured. Resolution to
bytes probes the OS memory bound through
kevy-sys(the sanctioned boundary). - TtlReaper
Mode - How the active TTL reaper runs.
- Value
- A stored value. One variant per Redis type.
- Value
Filter - One
FILTERpredicate: which stored value field it reads, and the test on it — the wire’sRANGE/EQshapes, in-process. - View
Mode - View mode.
- View
Tree - The composition tree. Depth ≤ 3, leaves ≤ 4 (declarative caps, enforced at CREATE).
- ZAggregate
AGGREGATEmode for inter/union (Redis 6.2; defaultSum).
Type Aliases§
- Facet
Counts - One facet field’s reported buckets:
(value, count), most frequent first. - HExpire
Code - Per-field reply codes for
HEXPIRE-family calls (Redis 7.4):-2key or field missing,0condition (NX/XX/GT/LT) not met,1deadline set,2field deleted (deadline already due). - Index
Page - One page of index hits plus the cursor to resume from.
- Kevy
Result - Unified result alias over
KevyError. - View
Page - One page of view members plus the resume cursor.