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.

§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

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.
ExpireStats
What Store::tick_expire saw and did. Surfaced for tests, INFO keyspace, and (eventually) Wave 2 task #4’s crash-safe verifier.
GroupStats
One group’s live statistics.
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.
KevyTierInfo
The # Tiering gauge set (B12) — summed across shards; field names mirror the server’s INFO # Tiering section one-to-one.
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.
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.
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. CH only changes the reply (changed count instead of added count) — callers read ZaddReport::changed when set; the engine behavior is identical.
ZaddReport
Outcome of a flags-aware ZADD.

Enums§

AggBy
Ranking metric for AggSegment::top_groups.
AppendFsync
When to fsync the AOF to disk.
BitOp
Operator for Store::bitop.
EvictionPolicy
Maxmemory eviction policy. Mirror of kevy_config::EvictionPolicy — duplicated here so kevy-store stays a leaf crate (no kevy-config dep).
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.
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.
PubsubFrame
One pub/sub event delivered to a Subscription.
StoreError
Operation errors surfaced to the command layer.
TableEnsure
What table_ensure found (the boot verb — see the embedded docs).
TierBudgetSpec
One transparent-tiering RAM budget, as configured. Resolution to bytes probes the OS memory bound through kevy-sys (the sanctioned boundary).
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.