Skip to main content

Crate kevy_embedded

Crate kevy_embedded 

Source
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 kevy crate’s serve instead.
  • You need cross-process concurrency → kevy-embedded is single-process (one mutex). Multi-process needs the network layer. A persist dir is one engine’s: Store::open on a dir a live engine holds — in this process or another — errors (flock on <dir>/LOCK) instead of letting two writers interleave one AOF.

§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 = [...]:

featureadds
corein-memory KV + TTL + pub/sub + pipeline/atomic (the minimal base)
persistsnapshot + AOF durability (with_persist, replay on open)
indexsecondary indexes + views
textfull-text index segments (implies index)
vectorHNSW vector index segments (implies index)
replicateembed-as-replica / embed-as-writer + CDC feed (implies persist)
listenerthe read-only RESP listener
host-logStore::dispatch_argv_recorded: the frames a command’s write records, for a host that keeps the log itself

Structs§

AnnSpec
HNSW declaration (immutable once created).
AtomicAllShards
Context handed to the atomic_all_shards closure body. Methods route to the right shard by hashing the key.
AtomicCtx
Handle passed to the atomic closure body. Methods mirror the equivalent Store ops 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.
ChangeBatch
A batch of changes plus the cursor to resume from.
Config
Embedded-store config. Build by chaining with_* methods on Config::default, or assign its public fields.
ExpireStats
What Store::tick_expire saw and did. Surfaced for tests, INFO keyspace, and (eventually) Wave 2 task #4’s crash-safe verifier.
FeedPosition
A point in a feed: an offset within one generation’s offset history.
GroupStats
One group’s live statistics.
IdxAdvice
One Store::idx_advise row: how often the family was refused, the access path it asked for, and the declaration that serves it.
IndexCursor
Opaque pagination cursor: the last (value, key) served. Encoded by the runtime into the wire cursor; None = start.
IndexStats
Sizing + health counters (IDX.LIST / memory formula).
IndexVerify
Per-index verification counters, one row of TABLE.VERIFY.
KevyInfo
Snapshot of a store’s runtime counters, returned by Store::info. A cheap aggregate (one mutex lock); fields mirror the individual accessors.
KevyTierCompression
The value log’s compression accounting, summed across shards; field names mirror the last four lines of the server’s INFO # Tiering. Payload plus frame headers against raw bytes is the ratio; the dictionaries are memory, one per vlog file.
KevyTierInfo
The # Tiering gauge set (B12) — summed across shards; field names mirror the server’s INFO # Tiering section one-to-one.
Keypair
An X25519 key pair.
KeysIter
The iterator Store::keys_iter returns: the keys matching its pattern, a page at a time.
LinkKeys
One end’s key pair, and the public keys of the other ends it accepts.
MatchOpts
Everything a text MATCH carries beyond its index, query text and result limit — the embedded twin of the wire’s optional clauses.
MatchPage
A faceted query’s answer: the page, and per requested FACET field its (value, count) buckets over the whole match set.
OpenReport
What Store::open restored — and what it could not. The pull-style twin of KevyMetric::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.
OrderPath
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.
PrefixInfo
Per-prefix keyspace stats from Store::info_prefix.
ReconcileReport
The result of Snapshot::reconcile.
RewriteStats
Result of an Aof::rewrite_from call. Surfaced by BGREWRITEAOF / INFO persistence. The default is the empty rewrite (no keys, no bytes), the starting point for summing several shards’ stats.
ScalarPage
A clause-carrying query’s answer: the page, per requested FACET field its (value, count) buckets, and — on the FILTER-with-cursor path — the cursor to resume from.
ScalarQueryOpts
Everything a scalar RANGE/EQ query carries beyond its bounds and limit — the embedded twin of the wire’s optional clauses. ScalarQueryOpts::default is the plain query; the with_* methods name the clauses a caller wants.
ScoreBound
A score-range endpoint for ZRANGEBYSCORE/ZCOUNT (inclusive or exclusive). Use value = ±INFINITY for -inf/+inf.
Snapshot
A frozen, consistent point-in-time view of the whole store.
SnapshotEntry
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.
TableIndex
One declared secondary index: a column and a scalar kind, plus the stored VALUES columns residual FILTER/SORT read.
TableSpec
One declared table.
TableVerify
The whole TABLE.VERIFY answer.
ViewLeaf
One leaf: a declared index + the shape it contributes.
WeakStore
Weak handle to a Store — does not keep the underlying keyspace alive.
ZaddFlags
Parsed ZADD condition flags: NX / XX as a SetCondition, GT / LT as a ScoreCompare, and CH. Only Redis’s legal combinations can be built — ZaddFlags::new refuses NX with GT / LT — so the store never has a combination to reject. CH only changes the reply (changed count instead of added count); ZaddReport carries both counts either way.
ZaddReport
Outcome of a flags-aware ZADD.

Enums§

AggBy
Ranking metric for AggSegment::top_groups.
AppendFsync
When to fsync the AOF to disk. The three values of Redis’s appendfsync.
BitOp
Operator for the BITOP family.
CopyMode
Whether COPY may overwrite an existing destination (crate::Store::copy).
EvictionPolicy
Maxmemory eviction policy — the one type for it: kevy-config parses maxmemory-policy into it and the store enforces it.
FeedError
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.
HExpireCond
Condition flags for HEXPIRE (NX/XX/GT/LT; at most one).
IndexKind
Index kind (KIND range|unique).
IndexValType
Declared scalar type of an index (TYPE i64|f64|str).
IndexValue
One indexed scalar. Ordering is total within a type; the catalog guarantees a segment only ever holds one variant.
InsertPosition
Where LINSERT places the new element relative to its pivot.
KevyError
The error type of the embeddable stack: kevy_embedded::Store and kevy_client::Connection surfaces return Result<_, KevyError>.
KevyMetric
A persistence event worth observing. More variants may be added; match non-exhaustively (_ => {}) to stay forward-compatible.
PubsubEvent
One pub/sub event delivered to a Subscription — the same type the network clients receive, so a consumer handles either source alike. One pubsub frame received from the bus or the wire.
ReplayMode
What a replay (and the open that follows it) does at a corrupt record in the middle of a log.
SortOrder
Which way an order runs: ascending or descending.
StoreError
Operation errors surfaced to the command layer.
TableEnsure
What table_ensure found (the boot verb — see the embedded docs).
TierBudgetSpec
One tiering budget, as configured (not yet resolved to bytes).
TokenPositions
Whether a text index records each token’s position (crate::Store::idx_create_text).
TtlReaperMode
How the active TTL reaper runs.
Value
A stored value. One variant per Redis type.
ValueFilter
One FILTER predicate: which stored value field it reads, and the test on it — the wire’s RANGE / EQ shapes, in-process.
ViewMode
View mode.
ViewTree
The composition tree. Depth ≤ 3, leaves ≤ 4 (declarative caps, enforced at CREATE).
ZAggregate
AGGREGATE mode for inter/union (Redis 6.2; default Sum).

Type Aliases§

FacetCounts
One facet field’s reported buckets: (value, count), most frequent first.
HExpireCode
Per-field reply codes for HEXPIRE-family calls (Redis 7.4): -2 key or field missing, 0 condition (NX/XX/GT/LT) not met, 1 deadline set, 2 field deleted (deadline already due).
IndexPage
One page of index hits plus the cursor to resume from.
KevyResult
Unified result alias over KevyError.
ViewPage
One page of view members plus the resume cursor.