pub struct Store { /* private fields */ }Expand description
The embedded keyspace.
Store is Clone (since v1.1.0). A clone is a cheap Arc bump:
every clone reaches the same underlying shards + AOF + reaper + pub/sub
bus. The reaper thread is joined and each shard’s AOF is flushed exactly
once, when the last clone is dropped.
use kevy_embedded::{Config, Store};
let s = Store::open(Config::default().with_ttl_reaper_manual())?;
let s2 = s.clone();
std::thread::spawn(move || {
s2.set(b"from-thread", b"v").unwrap();
}).join().unwrap();
assert_eq!(s.get(b"from-thread")?, Some(b"v".to_vec()));Every method takes &self. Sharding (see Config::with_shards) lets a
multi-threaded consumer scale across cores; pub/sub is process-wide
(handled on shard 0).
Implementations§
Source§impl Store
impl Store
Sourcepub fn info(&self) -> KevyInfo
pub fn info(&self) -> KevyInfo
One-shot snapshot of the store’s introspection counters. See
KevyInfo. Takes the embedded mutex once; safe to call from a
health endpoint.
Sourcepub fn expire_pending_count(&self) -> usize
pub fn expire_pending_count(&self) -> usize
Number of live keys that currently carry a TTL (the expire-set size, summed across shards).
Source§impl Store
impl Store
Sourcepub fn set(&self, key: &[u8], value: &[u8]) -> Result<bool>
pub fn set(&self, key: &[u8], value: &[u8]) -> Result<bool>
SET key value (no TTL, no NX/XX). Returns true always under the
embedded API (Redis semantics: SET overwrites; NX/XX vetoes would
return false but we don’t expose those here — use Store::with
for the full surface).
Sourcepub fn set_with_ttl(
&self,
key: &[u8],
value: &[u8],
ttl: Duration,
) -> Result<bool>
pub fn set_with_ttl( &self, key: &[u8], value: &[u8], ttl: Duration, ) -> Result<bool>
SET key value PX ms — overwrites + sets TTL. The AOF records an
absolute PEXPIREAT deadline (not the relative ttl) so the key
expires at the same wall-clock instant after a restart — a relative
PEXPIRE would be re-anchored to replay-time, resetting the TTL to a
fresh full duration on every restart (INC-2026-06-09).
Sourcepub fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>>
pub fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>>
GET key — Some(bytes) on hit, None on miss or expired.
With eviction off (maxmemory == 0, the default) this takes the read
lock and a non-mutating store lookup, so concurrent readers scale across
cores — the path a read-heavy embed cache lives on. With eviction on it
falls back to the exclusive lock + mutating get so each access still
stamps the LRU clock.
Sourcepub fn del(&self, keys: &[&[u8]]) -> Result<usize>
pub fn del(&self, keys: &[&[u8]]) -> Result<usize>
DEL key1 [key2 ...]. Returns the count of keys actually removed.
Keys fan out to their owning shards.
Sourcepub fn exists(&self, keys: &[&[u8]]) -> Result<usize>
pub fn exists(&self, keys: &[&[u8]]) -> Result<usize>
EXISTS key1 [key2 ...]. Count of existing keys (duplicates counted
multiple times, matching Redis).
Sourcepub fn incr_by(&self, key: &[u8], delta: i64) -> Result<i64>
pub fn incr_by(&self, key: &[u8], delta: i64) -> Result<i64>
INCRBY key delta. Negative delta does DECR-style work.
Sourcepub fn expire(&self, key: &[u8], ttl: Duration) -> Result<bool>
pub fn expire(&self, key: &[u8], ttl: Duration) -> Result<bool>
EXPIRE key seconds. Returns true if a key was touched. The AOF
records an absolute PEXPIREAT deadline (see Self::set_with_ttl)
so the TTL survives a restart unchanged.
Sourcepub fn persist(&self, key: &[u8]) -> Result<bool>
pub fn persist(&self, key: &[u8]) -> Result<bool>
PERSIST key. Returns true if a TTL was actually cleared.
Sourcepub fn ttl_ms(&self, key: &[u8]) -> i64
pub fn ttl_ms(&self, key: &[u8]) -> i64
Remaining TTL in ms (or Redis-style -1/-2 for no-TTL/no-key).
Sourcepub fn type_of(&self, key: &[u8]) -> &'static str
pub fn type_of(&self, key: &[u8]) -> &'static str
TYPE key — "string", "hash", "list", "set", "zset", or "none".
Sourcepub fn flushall(&self) -> Result<()>
pub fn flushall(&self) -> Result<()>
FLUSHALL — empty every shard (each logs FLUSHALL so a replay reaches
the same empty state).
Named flushall — not flush — to avoid colliding with
Write::flush’s “sync buffered writes to disk” meaning. This call
WIPES the store; durability needs no explicit call (each write appends
to the AOF, the shard’s BufWriter lands per AppendFsync cadence
and on drop).
Sourcepub fn flush(&self) -> Result<()>
👎Deprecated since 1.2.0: renamed to flushall: flush collides with Write::flush (sync-to-disk); this WIPES the store
pub fn flush(&self) -> Result<()>
renamed to flushall: flush collides with Write::flush (sync-to-disk); this WIPES the store
Deprecated alias for Self::flushall. The old name read like
Write::flush (sync-to-disk) but actually WIPES the store — a
data-loss footgun.
Sourcepub fn key_bytes(&self, key: &[u8]) -> Option<u64>
pub fn key_bytes(&self, key: &[u8]) -> Option<u64>
MEMORY USAGE for one key — Some(bytes) or None if absent.
Sourcepub fn used_memory(&self) -> u64
pub fn used_memory(&self) -> u64
Live used_memory estimate (summed across shards).
Sourcepub fn evictions_total(&self) -> u64
pub fn evictions_total(&self) -> u64
INFO-style counter: total keys evicted by maxmemory (all shards).
Sourcepub fn expired_keys_total(&self) -> u64
pub fn expired_keys_total(&self) -> u64
INFO-style counter: total keys expired (lazy + active reaper, all shards).
Sourcepub fn hset(&self, key: &[u8], pairs: &[(&[u8], &[u8])]) -> Result<usize>
pub fn hset(&self, key: &[u8], pairs: &[(&[u8], &[u8])]) -> Result<usize>
HSET key field value [field value ...]. Returns count newly added.
Sourcepub fn hget(&self, key: &[u8], field: &[u8]) -> Result<Option<Vec<u8>>>
pub fn hget(&self, key: &[u8], field: &[u8]) -> Result<Option<Vec<u8>>>
HGET key field. None if absent.
Sourcepub fn hdel(&self, key: &[u8], fields: &[&[u8]]) -> Result<usize>
pub fn hdel(&self, key: &[u8], fields: &[&[u8]]) -> Result<usize>
HDEL key field [field ...]. Returns count actually removed.
Sourcepub fn lpush(&self, key: &[u8], values: &[&[u8]]) -> Result<usize>
pub fn lpush(&self, key: &[u8], values: &[&[u8]]) -> Result<usize>
LPUSH key value [value ...]. Returns the new list length.
Sourcepub fn rpush(&self, key: &[u8], values: &[&[u8]]) -> Result<usize>
pub fn rpush(&self, key: &[u8], values: &[&[u8]]) -> Result<usize>
RPUSH key value [value ...]. Returns the new list length.
Sourcepub fn lpop(&self, key: &[u8], count: usize) -> Result<Vec<Vec<u8>>>
pub fn lpop(&self, key: &[u8], count: usize) -> Result<Vec<Vec<u8>>>
LPOP key count. Returns popped values from the head.
Sourcepub fn rpop(&self, key: &[u8], count: usize) -> Result<Vec<Vec<u8>>>
pub fn rpop(&self, key: &[u8], count: usize) -> Result<Vec<Vec<u8>>>
RPOP key count. Symmetric to LPOP from the tail.
Sourcepub fn llen(&self, key: &[u8]) -> Result<usize>
pub fn llen(&self, key: &[u8]) -> Result<usize>
LLEN key. Length of the list at key; 0 if absent.
Sourcepub fn sadd(&self, key: &[u8], members: &[&[u8]]) -> Result<usize>
pub fn sadd(&self, key: &[u8], members: &[&[u8]]) -> Result<usize>
SADD key member [member ...]. Returns count newly added.
Sourcepub fn srem(&self, key: &[u8], members: &[&[u8]]) -> Result<usize>
pub fn srem(&self, key: &[u8], members: &[&[u8]]) -> Result<usize>
SREM key member [member ...]. Returns count actually removed.
Sourcepub fn smembers(&self, key: &[u8]) -> Result<Vec<Vec<u8>>>
pub fn smembers(&self, key: &[u8]) -> Result<Vec<Vec<u8>>>
SMEMBERS key. Order implementation-defined; empty if absent.
Sourcepub fn zadd(&self, key: &[u8], pairs: &[(f64, &[u8])]) -> Result<usize>
pub fn zadd(&self, key: &[u8], pairs: &[(f64, &[u8])]) -> Result<usize>
ZADD key score member [score member ...]. Returns count newly added.
Sourcepub fn zrem(&self, key: &[u8], members: &[&[u8]]) -> Result<usize>
pub fn zrem(&self, key: &[u8], members: &[&[u8]]) -> Result<usize>
ZREM key member [member ...]. Returns count actually removed.
Sourcepub fn zscore(&self, key: &[u8], member: &[u8]) -> Result<Option<f64>>
pub fn zscore(&self, key: &[u8], member: &[u8]) -> Result<Option<f64>>
ZSCORE key member. Some(score) if present.
Sourcepub fn publish(&self, channel: &[u8], payload: &[u8]) -> usize
pub fn publish(&self, channel: &[u8], payload: &[u8]) -> usize
PUBLISH channel payload. Delivers payload to every subscriber on
channel (direct + pattern matches) inside this process. Returns
the count of receivers the message reached.
Sourcepub fn subscribe(&self, channels: &[&[u8]]) -> Subscription
pub fn subscribe(&self, channels: &[&[u8]]) -> Subscription
Open a Subscription subscribed to channels. Drop the handle
to unsubscribe from everything atomically. Pass &[] to start
with no subscriptions and add some later via
Subscription::subscribe / Subscription::psubscribe.
Sourcepub fn psubscribe(&self, patterns: &[&[u8]]) -> Subscription
pub fn psubscribe(&self, patterns: &[&[u8]]) -> Subscription
Convenience: open a Subscription starting on pattern subscriptions.
Source§impl Store
impl Store
Sourcepub fn open(config: Config) -> Result<Self>
pub fn open(config: Config) -> Result<Self>
Open an embedded keyspace per config.
- Pure in-memory when
config.data_dirisNone. - With persistence: each shard loads its snapshot then replays its AOF
(
config.shards > 1re-shards a legacy single AOF on first open). - Spawns a background TTL reaper thread when
config.ttl_reaper == Background(the default). - When
config.replica_upstream = Some("host:port"), spawns a background thread that streams replication frames from the named primary and applies them to this store; local writes are rejected withREADONLY(seeSelf::open_replica).
Sourcepub fn open_replica(upstream: impl Into<String>) -> Result<Self>
pub fn open_replica(upstream: impl Into<String>) -> Result<Self>
Convenience constructor for an embed-as-read-replica store
streaming writes from upstream ("host:port" of a kevy
server’s replication listener).
The replica:
- has its local AOF force-disabled (the upstream stream is the source of truth; replica AOF would diverge and double-apply on restart);
- rejects every local write with a
READONLYio::Error(you can still call read APIs concurrently); - reconnects with exponential backoff on disconnect, resuming from the last applied offset;
- gets a process-unique
replica_idso an open / drop / reopen cycle within the primary’s reconnect window does not look like the same slot from the primary’s POV (which would evict backlog frames the new embed still needs from offset 0). Override viaConfig::with_replica_idwhen you specifically want the slot to be re-claimed across restarts.
For full builder control (custom replica id, backoff bounds,
snapshot dir, etc.) use Self::open with
Config::with_replica_upstream + the related setters
instead.
Sourcepub fn is_replica(&self) -> bool
pub fn is_replica(&self) -> bool
true when this store was opened against a replication
upstream — local writes are rejected with READONLY.
Sourcepub fn set_replica_upstream(
&self,
new_upstream: impl Into<String>,
) -> Result<()>
pub fn set_replica_upstream( &self, new_upstream: impl Into<String>, ) -> Result<()>
Retarget this replica at a new primary URL (host:port). The
runner picks up the change on its next connect — which is
forced now by shutdowning the current socket clone, so the
retarget lands within Config::replica_reconnect_min (default
100 ms) of this call.
Returns Err with ErrorKind::InvalidInput when this store is
not a replica (no upstream was configured at open). Application
code typically drives this from a kevy-elect failover signal —
see docs/cluster.md “embed-as-read-replica” / Phase 2 / T2.7.
kevy-embedded itself stays elect-protocol-agnostic; the
integration glue lives in the application.
Sourcepub fn config(&self) -> &Config
pub fn config(&self) -> &Config
The active config (a clone — modifying it has no effect on the
running store). Useful for introspection / INFO-style telemetry.
Sourcepub fn with<F, R>(&self, f: F) -> R
pub fn with<F, R>(&self, f: F) -> R
Run f against the underlying kevy_store::Store under its lock. Use
for direct access to methods this crate hasn’t wrapped. The closure can
mutate, but does not auto-log to the AOF — call Self::log yourself
if the mutation must survive a crash.
Sharded stores: this targets shard 0 only. Use Self::with_key
to reach the shard owning a specific key.
Sourcepub fn with_key<F, R>(&self, key: &[u8], f: F) -> R
pub fn with_key<F, R>(&self, key: &[u8], f: F) -> R
Like Self::with but targets the shard that owns key.
Sourcepub fn collect_keys(
&self,
pattern: Option<&[u8]>,
limit: Option<usize>,
) -> Vec<Vec<u8>>
pub fn collect_keys( &self, pattern: Option<&[u8]>, limit: Option<usize>, ) -> Vec<Vec<u8>>
KEYS / SCAN-glob across every shard — the cross-shard
replacement for with(|s| s.collect_keys(pat, lim)), which only sees
shard 0 once sharding is on. Behaves identically to with(...) when
shard_count() == 1. limit bounds the total returned across shards.
Takes a read lock per shard (concurrent-safe).
Sourcepub fn for_each_shard<F: FnMut(&mut Store)>(&self, f: F)
pub fn for_each_shard<F: FnMut(&mut Store)>(&self, f: F)
Run f against each shard’s underlying kevy_store::Store (in
shard-index order) — the cross-shard escape hatch. The caller assembles
the merged result. Pairs with Self::shard_count. For a single key,
prefer Self::with_key; for a glob scan, prefer Self::collect_keys.
Sourcepub fn shard_count(&self) -> usize
pub fn shard_count(&self) -> usize
Number of keyspace shards (== Config::shards).
Sourcepub fn log(&self, parts: &[&[u8]]) -> Result<()>
pub fn log(&self, parts: &[&[u8]]) -> Result<()>
Append a raw RESP-frame argument list to the shard owning its key’s AOF. No-op when persistence is disabled.
Sourcepub fn tick(&self) -> ExpireStats
pub fn tick(&self) -> ExpireStats
Run one TTL-reaper tick across every shard. Required call cadence in
Manual mode (~10×/s to match Redis hz=10). Returns the summed stats.
Source§impl Store
impl Store
Sourcepub fn rewrite_aof(&self) -> Result<Option<RewriteStats>>
pub fn rewrite_aof(&self) -> Result<Option<RewriteStats>>
BGREWRITEAOF: rebuild every shard’s AOF from current state.
Synchronous. Returns the summed stats (None if persistence is off /
no shard rewrote).
Sourcepub fn save_snapshot(&self) -> Result<bool>
pub fn save_snapshot(&self) -> Result<bool>
Snapshot every shard to its dump-{i}.rdb (single shard: the configured
name), atomically. Ok(false) when persistence is disabled.