Expand description
kevy-store — the keyspace.
A single-threaded, multi-type keyspace with lazy expiration. Each Redis data
type is backed by a modern std structure — behaviour-compatible, but not
Redis’s legacy encodings:
| Type | Backing structure |
|---|---|
| String | Vec<u8> |
| Hash / Set | HashMap / HashSet (hashbrown Swiss table) |
| List | VecDeque (ring buffer, O(1) ends) |
| Sorted set | HashMap + BTreeSet<(score, member)> (a B-tree, not a skiplist) |
Wrong-type access returns StoreError::WrongType. The API is &mut self
and lock-free, so a thread-per-core runtime (kevy-rt) can own one shard per
core with no locking. Part of the kevy key–value server.
maxmemory enforcement + 8 eviction policies live in evict; toggle via
Store::set_max_memory. With maxmemory == 0 (the default) the hot-path
cost collapses to a single predicted-not-taken branch, matching the
“unlimited” mode in Redis byte-for-byte.
§Example
use kevy_store::Store;
use std::borrow::Cow;
let mut s = Store::new();
s.set(b"greeting", b"hello".to_vec(), None, false, false);
assert_eq!(s.get(b"greeting").unwrap(), Some(Cow::Borrowed(&b"hello"[..])));
s.hset(b"user:1", &[(b"name".to_vec(), b"alice".to_vec())]).unwrap();
assert_eq!(s.hget(b"user:1", b"name").unwrap(), Some(&b"alice"[..]));
// A string command on a hash key is a type error, as in Redis.
assert_eq!(s.get(b"user:1"), Err(kevy_store::StoreError::WrongType));Re-exports§
pub use expire::ExpireStats;
Modules§
- evict
- Maxmemory enforcement and the 8 eviction policies.
- expire
- Active TTL reaper — Redis’s
activeExpireCycle, adapted to the thread-per-core / single-shardStore.
Structs§
- Autoclaim
Result - Snapshot of
XAUTOCLAIMwork in progress: cursor for the next call, IDs successfully transferred, and IDs skipped because the stream has since deleted them. - Consumer
Group - One consumer group’s state. Sorted PEL plus a map of known consumers (with cached pel_count for O(1) XINFO answers).
- Consumer
State - Per-consumer cached counters so
XINFO CONSUMERSanswers in O(1). - Loaded
Group - One consumer group decoded into primitive tuples — the dump/load wire form shared by snapshot v4, AOF-rewrite filtering, and reshard’s in-memory redistribution.
- PelEntry
- One pending entry: who got it, when, and how many times.
- Pending
Extended - Extended form of
XPENDING key group [IDLE ms] start end count [consumer]: one row per matching PEL entry. - Pending
Extended Row - One row of the extended XPENDING reply.
- Pending
Summary - Summary form of
XPENDING key group(only 3 args): total pending, min/max IDs across the PEL, and per-consumer aggregate counts. - Score
- A total-ordered f64 score (Redis scores are never NaN).
total_cmpgives a total order so scores can key aBTreeSet. - Score
Bound - A score-range endpoint for
ZRANGEBYSCORE/ZCOUNT(inclusive or exclusive). Usevalue = ±INFINITYfor-inf/+inf. - Small
Hash Data - Inline packed hash storage. 24 bytes total.
- Small
Hash Iter - Iterator over
SmallHashDatayielding(&[u8] field, &[u8] value). - Small
List Data - Inline packed list storage. 24 bytes total.
- Small
List Iter - Iterator over
SmallListData. - Small
SetData - Inline packed set storage. 24 bytes total — see module docs for layout.
- Small
SetIter - Iterator over
SmallSetDatamembers as&[u8]slices. - SmallZ
SetData - Inline packed sorted-set storage. 24 bytes total.
- SmallZ
SetIter - Iterator over
SmallZSetDatayielding(member, score). - Snapshot
View - A frozen,
Sendview of one store’s live entries at a single instant. - Store
- A single-database keyspace.
- Stream
Data - One stream’s storage: every entry in
entriesplus the per-stream scalar state Redis exposes viaXINFO STREAM, plus the consumer groups map (sprint B). An emptygroupsmap costs ~8 bytes and makes the no-group fast path (sprint A XADD/XREAD) zero-overhead. - Stream
Id - A stream entry’s
<ms>-<seq>identifier. TheOrdderivation comparesmsfirst thenseq, which is exactly the monotonic order the protocol requires; same derivation givesEq,Hash, and theBTreeMapkey bound. - XClaim
Opts - Knobs for
crate::StreamData’sxclaim:min-idle-msplus theIDLE/TIME/RETRYCOUNT/FORCE/JUSTIDflag tail. - ZSet
Data - Sorted set: a member→score map plus a B-tree ordered by
(score, member). (A B-tree is cache-friendlier than Redis’s skiplist;ZRANKis O(n) here — an order-statistics tree for O(log n) rank is a later perf item.)
Enums§
- Eviction
Policy - Maxmemory eviction policy. Mirror of
kevy_config::EvictionPolicy— duplicated here sokevy-storestays a leaf crate (nokevy-configdep). - GetReply
- L1 return shape for
Store::get_for_reply— lets the reactor’s reply path choose between memcpy (Bytes) and writev zero-copy (ArcBulk) off one keyspace lookup. - Group
Create Mode XGROUP CREATEID argument: either an explicit ID or$(= current stream’slast_id, resolved by the caller).- Read
Group Id - XREADGROUP’s per-stream ID: either
>(= new entries) or an explicit “after this id” for PEL replay. - Rename
Outcome - Outcome of
Store::rename— three-way result so the dispatch layer can pick the right RESP frame (+OK/-ERR no such key/:0forRENAMENX-with-existing-dst). - Store
Error - Operation errors surfaced to the command layer.
- Stream
IdError - Errors
parse_*_idmay emit. Distinct fromStoreError::NotIntegerso callers can map to the more specific Redis wire shape (ERR Invalid stream ID specified as stream command argument). - Value
- A stored value. One variant per Redis type.
- XAdd
IdSpec - XADD’s ID argument: either an explicit
<ms>-<seq>(both parts may be*to auto-fillseqonly) or fully auto-generate via*.
Constants§
- BULK_
THRESHOLD - Threshold (bytes) above which a SET stores its value as
Value::ArcBulk(writev-eligible on GET) instead ofValue::Str(inlineSmallBytes). 64 B ≈ one cache line — below that the inline-SmallBytes storage wins on L1 locality; above it the writev-borrow win dominates. - ENTRY_
OVERHEAD - Per-entry overhead in the top-level keyspace map: the inline 24-byte
SmallByteskey cell + the 64-byteEntry(post weight/clock fields) + metadata. Approximation that errs slightly high soused_memorystays a conservative upper bound vs the actual allocator footprint. - HEAP_
HEAVY_ BYTES - Heap-size threshold above which an overwritten
Valueis sent to the runtime’s bio thread for off-reactor drop instead of being freed inline (v1.25 A.3 lazy-drop).
Functions§
- glob_
match - Redis-style glob match (
*,?,[...]classes with ranges/^,\escape). - hash_
field_ weight - Per-field delta a new hash field charges against the entry weight: heap bytes for the field name (if not inline) + value capacity + one slot of bucket overhead. Used when an HSET inserts a brand-new field.
- list_
item_ weight - Per-item delta a new list element charges (Vec header slot + heap cap).
- now_
unix_ ms - Wall-clock millis. Shared with dispatchers so every XADD on a shard uses
the same clock source. On native targets reads
SystemTime::now()(falls back to 0 on a pre-UNIX-EPOCH clock — impossible on supported platforms); onwasm32-unknown-unknown, whereSystemTime::now()traps, reads the host-fed wall clock (seecrate::set_wall_clock_ms, wasm-only). - parse_
explicit_ id - Parse a fully-explicit ID for XREAD’s per-stream “last-seen” arg
(
0,0-0,5-2).$is handled by the caller (it means “the stream’s currentlast_id”, which only Store can resolve). - parse_
range_ end - Parse an XRANGE
endID. Accepts+(=StreamId::MAX), barems(seq=u64::MAX so the entire ms is included), and fullms-seq. - parse_
range_ start - Parse an XRANGE
startID. Accepts-(=StreamId::MIN), barems(seq=0), and fullms-seq. - parse_
xadd_ id - Parse an XADD ID argument (
*,ms,ms-*,ms-seq). - set_
member_ weight - Per-member delta a new set member charges. Mirrors
hash_field_weightfor the set variant (no separate value, single bucket slot). - zset_
member_ weight - Per-member delta a new zset member charges: hash slot for
by_member+ BTreeSet slot forby_score+ the member’s heap bytes.
Type Aliases§
- BioDrop
Sender - Sender half of the runtime’s bio-drop channel. Wired from
kevy-rt’sbio.rsviacrate::Store::set_bio_drop_sender; the concrete payload isVec<Box<Value>>— a batch of values produced by one shard since its last flush (A.2 batch-send model). The bio thread (kevy-rt::bio::spawn) iterates the batch and drops each item. One mpsc message per shard-flush amortises the channel cost (atomic + cross-thread cacheline traffic) across however many values landed in the batch. - Entry
Batch - Cloned-out view of stream entries, the cross-module wire form. Keeps
the same shape Redis sends and lets the callers stay decoupled from
the
SmallBytesinterning the store uses internally. - Hash
Data - Backing structure for a Hash value —
KevyMapkeyed bySmallBytes(22 B inline / heap-else). Field names ≤22B (the vast majority —name,email, etc.) live entirely inside the bucket, saving the 24 B Vec metadata + heap allocation per field on a 22-byte budget. - List
Data - Backing structure for a List value (a ring-buffer deque — O(1) both ends).
- Loaded
PelEntry - One PEL row in primitive form:
(ms, seq, consumer, delivery_time_ms, delivery_count). The persist crate serializes these verbatim. - Loaded
Stream Entry - Snapshot-loader payload: one stream entry decoded into primitive
tuples
(ms, seq, [(field, value), ...]). The persist crate emits these andStore::load_streamconsumes them. - SetData
- Backing structure for a Set value —
KevySetofSmallBytes.
Unions§
- Small
Bytes - A 24-byte owned byte string with inline small-string optimization.