Expand description
kevy-rt — shared-nothing, thread-per-core runtime.
Each core runs its own reactor (kqueue/epoll) and owns one shard of the
keyspace (hash(key) % nshards). There is no shared mutable state and no
lock on the hot path — cores communicate only by message passing over
channels, woken via a self-pipe (kevy_sys::Waker). Connections are spread
across cores by SO_REUSEPORT; a command whose key lives on another core is
forwarded to that core, executed there, and the reply routed back to the
originating connection.
Per-connection reply ordering is preserved (RESP is pipelined): each command gets a monotonic seq; replies are emitted only in contiguous seq order, so an async cross-core reply never overtakes an earlier one.
The cross-core channel currently uses std::sync::mpsc (pure Rust, zero
deps); swapping in a lock-free SPSC/MPSC ring is a perf-polish item.
Command semantics are injected via the Commands trait, keeping the
runtime independent of the concrete command set. Part of the kevy server.
§Module map
Runtime(inruntime) — public entry point; spawns oneshardper core.shard— the per-core reactor: sockets, the inbound queue, reply flushing.exec— command semantics: routing, execution, and result reduction.message— internal cross-core work/result types.conn— per-connection state (input/output, seq ring, subscriptions).reduce— reply reduction (materialize) and pure helpers (set algebra, shard hashing, pub/sub framing).
§Example
Implement Commands for your command set and run it. (Store is
re-exported so you don’t need a separate dependency.)
use kevy_rt::{ArgvView, Commands, Route, Runtime, Store, TxnKind};
use std::sync::Arc;
use std::sync::atomic::AtomicBool;
#[derive(Clone)]
struct MyCommands;
impl Commands for MyCommands {
fn route<A: ArgvView + ?Sized>(&self, args: &A) -> Route {
if args.len() >= 2 { Route::Single(1) } else { Route::Local }
}
fn dispatch<A: ArgvView + ?Sized>(&self, _store: &mut Store, _args: &A) -> Vec<u8> {
b"+OK\r\n".to_vec()
}
fn is_quit<A: ArgvView + ?Sized>(&self, args: &A) -> bool {
args.first().is_some_and(|c| c.eq_ignore_ascii_case(b"QUIT"))
}
fn is_write<A: ArgvView + ?Sized>(&self, _args: &A) -> bool { false }
fn txn_kind<A: ArgvView + ?Sized>(&self, _args: &A) -> TxnKind { TxnKind::Other }
}
// One shard per core, listening on 127.0.0.1:6379, until `stop` is set.
let rt = Runtime::builder(MyCommands).bind([127, 0, 0, 1], 6379).shards(4);
rt.run(Arc::new(AtomicBool::new(false))).unwrap();Every public item here is documented, and the lint keeps it that
way: kevy-rt is the reactor, and warnings = "deny" turns a new
gap into a compile error rather than a number that drifts. Closed
from 35 sites in v6 — all of them fields inside well-documented
variants, which is where prose review does not look.
Modules§
- propagation
- Effect-frame propagation override — how a verb whose effect is nondeterministic (SPOP’s random pick) keeps the AOF and the replication stream deterministic.
- serve_
counters - Debug-only signal that a cross-shard serve reply was processed by the
origin (i.e.
origin_on_serve_respran). The escrow regression uses it to tell a genuine cross-shard placement from a co-located one: with N shards a random key lands on the conn’s own shard ~1/N of the time, and that takes the LOCAL block path, not this cross-shard one — so the test retries until it provably exercised the cross-shard code. Non-I/O, so it does not perturb the timing.
Structs§
- Argv
- A parsed command’s argument vector.
- Argv
Borrowed - A parsed command’s argument vector that borrows its bytes from a contiguous input buffer.
- Live
Runtime Config - Live snapshot of the runtime-owned knobs that may have been changed
since this shard’s last tick. Built by the
Commandsimpl from its own config source (e.g. kevy readsconfig_global). EachSome(_)is applied to the shard; eachNoneleaves the existing setting alone. - Notification
Flags - Parsed view of
NotificationSection::notify_keyspace_events. The runtime caches this struct per-shard (hot-reload via the existingLiveRuntimeConfigtick path) so the per-write-command check reduces to four bool reads on the hot path. - Replica
Ack - A replica’s acknowledged state, published per shard tick via
Commands::on_replication_view: the offset from its latestREPLCONF ACKplus that ACK’s age at publication time.Nonein the view tuple means the replica has never ACKed. - Replica
Inbox Receiver - Receiver end. Lives inside the (private)
Shard; drained every reactor iteration. Constructed byreplica_inbox_pairand handed to the runtime viaRuntime::with_replica_inboxes. - Replica
Inbox Sender - Sender end of a per-shard replica inbox.
Send + Clone + Sync(one std::sync::mpsc::Sender, no extra state) so the embedder can hand it freely to runner threads. - Replicated
Apply Guard - RAII guard that marks the current thread as “applying a replicated
frame” for the guard’s lifetime. The replica runner
enters this scope before each
dispatchcall so the apply doesn’t re-push the frame into this shard’s own backlog. - Resolved
Cmd - Per-command verb-resolution result. Produced once by
Commands::resolvein the reactor’s parse-then-dispatch loop, reused for routing decisions, AOF logging, and the QUIT branch — so the per-cmdupper_verbcost goes from 4× down to 1×. - Runtime
- The public entry point: configure and run the thread-per-core server.
- Scan
Args - Parsed
SCANarguments carried byRoute::Scan. - Snapshot
Gate - Opaque completion token riding on
ReplicaApply::SnapshotEnd. The shard drops it only AFTER the snapshot swap has landed in itsStore, so the embedder can hang side effects (e.g. lowering a-LOADINGread gate) on the token’sDropand know they fire once the new keyspace — not the one about to be replaced — is what readers will see. Clones share one inner value: in broadcast (single-source) mode every shard holds a clone and theDropfires when the LAST shard finishes its load. - Store
- A single-database keyspace.
- XGroup
Ctx - The
GROUP <name> <consumer>(+NOACK) context anXREADGROUPgather carries to each per-stream sub-query.
Enums§
- Block
Hint - How a command wants to block, if at all. Returned by
Commands::resolveinsidecrate::ResolvedCmdso the verb-table lookup happens once per command.Noneis the zero-cost default for every non-blocking verb (≥ 99.9 % of dispatches in steady state). - Block
Kind - Which blocking command a waiter is parked in. Drives both timeout-nil shape and wake-retry dispatch.
- Client
Kill Filter - Parsed
CLIENT KILLselector.Addrmatches the peerip:portexactly;Idmatches the instance-unique conn id. - Extension
Reduced - Outcome of an extension fan-out reduce (
Commands::extension_reduce). - Fsync
- When to fsync the AOF to disk.
- GeoHits
- What a geo
*STORE’s search phase produced on the source key’s shard. Public:crate::Commands::geo_searchreturns it. The runtime never interprets the scores — the command layer decides whether they carry the source geohash or (withSTOREDIST) the distance in the queried unit. - MultiOp
- The multi-key gather reductions computed on the originating shard.
Public:
crate::Route::Gathercarries it, and embedders’route()implementations construct it. - Notify
Class - Keyspace-notification event class — what category a write command belongs to, so the runtime can match it against the per-conn notify_keyspace_events flags before publishing.
- Replica
Apply - One event delivered from a replica runner to its target shard.
Mirrors
kevy_replicate::replica::ReplicaEventexceptFramecarries an ownedArgv(already decoded by the runner) instead of aDecodedFrame { offset, argv }— the offset is gap-checked by the runner on the way in, so the shard doesn’t need it. - Resp
Version - Which version of RESP a connection is speaking. Negotiated via the
HELLOcommand — RESP2 is the default for backwards compatibility with every Redis 6.x and earlier client; RESP3 is opt-in viaHELLO 3and unlocks the additive reply types (Reply::Map/Reply::Set/Reply::Double/Reply::Boolean/Reply::Verbatim/Reply::BigNumber/Reply::Null/Reply::Push/Reply::BlobError) plus out-of-band push frames forPUBLISHdelivery. - Route
- How a command maps onto shards.
- Slowlog
Sub - Parsed
SLOWLOG <sub> [args]decision — picked at routing time so the runtime knows whether to fan out or short-circuit. - TxnKind
- Transaction-control classification for a command.
- ZCombine
- Which algebra combination a
*STOREorchestrator runs after its gather completes. Public:crate::Route::ZAlgebraStorecarries it, and embedders’route()implementations construct it.
Traits§
- Argv
View - Read-only view over a parsed command’s argument vector.
- Commands
- Command-set semantics injected into the runtime. Cloned to every core, so it must be cheap/stateless to clone.
Functions§
- parse_
slowlog_ sub - Parse
args([verb, sub, ...]) into aSlowlogSub. Verb name is assumed to already be SLOWLOG (the caller’s route table dispatched to here). Embedders call this from theirCommands::resolve/Commands::routeimpl. - push_
lua_ wake_ key - Lua’s
redis.calldispatch closure calls this after every wake-triggering write (LPUSH / RPUSH / XADD / ZADD / ZINCRBY). The runtime drains via [drain_lua_wake_buffer] after the outer EVAL dispatch returns and fireswake_keyfor each. - repl_
trace - True when replication tracing is enabled for this process.
- repl_
trace_ line - Emit one trace line stamped with the shared wall clock (epoch ms) —
the availgate crime scene spans three processes on one host, and
only a shared clock lets their probe lines interleave into a single
sequence. Callers gate on
repl_tracefirst. - replica_
inbox_ pair - Create a matched (sender, receiver) pair for one shard’s replica
inbox. The embedder calls this
nshardstimes beforeRuntime::run. - shard_
of_ key - Shard index for
keyovernshards. Independent of the store’s internal hash so a cross-shard routing change doesn’t require rehashing the store. - shard_
slot_ range - The contiguous slot range
[start, end](inclusive, CLUSTER SLOTS shape) shardiofnowns:[ceil(i·16384/n), ceil((i+1)·16384/n) - 1]. Exact inverse ofreduce::slot_to_shard’s multiply-shift.
Type Aliases§
- Replica
View Row - One replica conn’s row in the per-tick replication view:
(replica_id, peer_ipv4, peer_port, sent_offset, ack). The id is the identity string the replica presented at handshake — command layers group per-shard rows by it to render one aggregate entry per replica process.