kevy 6.3.0

kevy — a pure-Rust, zero-dependency, Redis-compatible KV server.
Documentation
//! Strings, the generic key surface, and the keyspace sweeps.
//!
//! One row per dispatch-reachable verb. `complexity` and `compat` were
//! derived by reading THIS engine's implementation — never copied from
//! Redis's documentation, because several of ours genuinely differ (SCAN
//! sweeps in one batch, SPOP is deterministic, LINDEX is O(1) where
//! Redis's quicklist is O(N)).

use super::flags::*;
use super::{VerbMeta, v};

#[rustfmt::skip]
pub(super) const ROWS: &[VerbMeta] = &[
    // ---- scan ------------------------------------------------------
    v("KEYS",        "scan", 2,  R, "Return every key matching the glob pattern (cross-shard gather).", "1.0.0", "KEYS pattern",
      "O(N) keys in the whole keyspace — every shard walks its entire map, plus an O(P*K) glob match per key",
      "full"),
    v("RANDOMKEY",   "scan", 1,  R, "Return a random key from the keyspace.", "1.0.0", "RANDOMKEY",
      "O(1) expected — each shard probes a random slot, and the origin folds the candidates through a reservoir weighted by shard key counts, so every key is equally likely",
      "full"),
    v("SCAN",        "scan", -2, R, "Iterate the keyspace incrementally: each call walks ~COUNT buckets of one shard and returns the next cursor.", "4.0.0", "SCAN cursor [MATCH pattern] [COUNT count] [TYPE type]",
      "O(COUNT) buckets per call, amortised O(N) for a full sweep — reverse-binary cursor, rehash-tolerant (a key present for the whole sweep is returned at least once even across table growth)",
      "differs: cursors encode (shard, position), so a cursor is only valid on the server and shard count that issued it — the same per-node property Redis Cluster cursors have. MATCH and TYPE filter after collection; COUNT bounds the buckets visited, not the keys returned"),
    // ---- generic ---------------------------------------------------
    v("DEL",         "generic", -2, W, "Delete one or more keys.", "1.0.0", "DEL key [key ...]",
      "O(N) keys, plus O(M) per key holding a collection of M elements (the drop walks the container)",
      "differs: the multi-key form is split by shard and summed with no barrier, so it is not atomic across shards; the single-key form is"),
    v("EXISTS",      "generic", -2, R, "Count how many of the given keys exist.", "1.0.0", "EXISTS key [key ...]",
      "O(N) keys, one O(1) probe each",
      "differs: the multi-key form is gathered per shard, so the count is not a point-in-time snapshot across shards"),
    v("EXPIRE",      "generic", 3,  W, "Set a key's TTL in seconds (non-positive TTL deletes the key).", "1.0.0", "EXPIRE key seconds",
      "O(1)",
      "differs: the NX / XX / GT / LT condition flags are not parsed — arity is fixed at 3, so EXPIRE key seconds NX is a wrong-arity error"),
    v("EXPIREAT",    "generic", 3,  W, "Set a key's expiry as an absolute Unix timestamp in seconds.", "1.0.0", "EXPIREAT key unix-time-seconds",
      "O(1)",
      "differs: the NX / XX / GT / LT condition flags are not parsed — arity is fixed at 3"),
    v("PERSIST",     "generic", 2,  W, "Remove a key's TTL.", "1.0.0", "PERSIST key",
      "O(1)",
      "full"),
    v("PEXPIRE",     "generic", 3,  W, "Set a key's TTL in milliseconds.", "1.0.0", "PEXPIRE key milliseconds",
      "O(1)",
      "differs: the NX / XX / GT / LT condition flags are not parsed — arity is fixed at 3"),
    v("PEXPIREAT",   "generic", 3,  W, "Set a key's expiry as an absolute Unix timestamp in milliseconds.", "1.0.0", "PEXPIREAT key unix-time-milliseconds",
      "O(1)",
      "differs: the NX / XX / GT / LT condition flags are not parsed — arity is fixed at 3"),
    v("PTTL",        "generic", 2,  R, "Return a key's remaining TTL in milliseconds (-1 no TTL, -2 missing).", "1.0.0", "PTTL key",
      "O(1)",
      "full"),
    v("RENAME",      "generic", 3,  W, "Rename a key, overwriting the destination (write routed at the runtime Op level).", "1.0.0", "RENAME key newkey",
      "O(1) for a string value; O(M) for a collection of M elements (the entry's weight is re-derived by walking the value)",
      "differs: a cross-shard rename is a non-atomic Take then Put — there is a window in which neither key is visible; co-locate the two keys with a {hashtag} for the atomic path"),
    v("RENAMENX",    "generic", 3,  W, "Rename a key only if the destination does not exist (write routed at the runtime Op level).", "1.0.0", "RENAMENX key newkey",
      "O(1) for a string value; O(M) for a collection of M elements",
      "differs: cross-shard RENAMENX is a non-atomic Take, Put, restore-on-refusal sequence — there is a window in which the source is absent from the keyspace"),
    v("TTL",         "generic", 2,  R, "Return a key's remaining TTL in seconds (-1 no TTL, -2 missing).", "1.0.0", "TTL key",
      "O(1)",
      "full"),
    v("TYPE",        "generic", 2,  R, "Return the type of the value stored at key.", "1.0.0", "TYPE key",
      "O(1)",
      "full"),
    v("COPY",        "generic", -3, W, "Copy a key's value, and its remaining TTL, to another key.", "1.0.0", "COPY source destination [REPLACE]",
      "O(1) same-shard; O(V) across shards, where V is the value copied over the ring",
      "differs: the DB argument is not accepted — kevy is single-database. A cross-shard copy is not atomic with anything else, but a failure between its two steps leaves both keys untouched"),
    v("TOUCH",       "generic", -2, R, "Count how many of the given keys exist.", "1.0.0", "TOUCH key [key ...]",
      "O(N) keys — one existence probe per key on its owning shard",
      "differs: kevy has no idle-time clock to reset, so TOUCH is EXISTS with the eviction bookkeeping the probe already does"),
    v("UNLINK",      "generic", -2, W, "Delete one or more keys (alias of DEL in kevy's shard model).", "1.0.0", "UNLINK key [key ...]",
      "O(N) keys, plus O(M) per collection-typed key — identical to DEL",
      "differs: UNLINK is an alias of DEL — the value is freed inline on the owning shard's reactor thread, not by a background thread, so it gives no latency benefit"),
    // ---- string ----------------------------------------------------
    v("APPEND",      "string", 3,  W, "Append bytes to a string value; returns the new length.", "1.0.0", "APPEND key value",
      "O(1) amortised while the value is <= 64 B; O(N) per call once it exceeds that (the Arc payload is copied out and re-boxed on every append), so building a large string by repeated APPEND is O(N^2)",
      "full"),
    v("BITOP",       "string", -4, W, "Combine strings bitwise into a destination key.", "1.0.0", "BITOP AND|OR|XOR|NOT destkey key [key ...]",
      "O(N) over the longest source; on a multi-shard server each source is read on its own shard and the result written on the destination's",
      "differs: the DIFF / DIFF1 / ANDOR / ONE operators of Redis 8.2 are a syntax error"),
    v("BITCOUNT",    "string", -2, R, "Count set bits in a string, optionally over a byte range.", "1.0.0", "BITCOUNT key [start end]",
      "O(N) over the counted bytes",
      "differs: the BYTE / BIT unit argument of Redis 7.0 is not accepted — ranges are byte offsets"),
    v("BITPOS",      "string", -3, R, "Return the position of the first bit set to 0 or 1.", "1.0.0", "BITPOS key bit [start [end]]",
      "O(N) over the scanned bytes",
      "differs: the BYTE / BIT unit argument of Redis 7.0 is not accepted — ranges are byte offsets"),
    v("GETBIT",      "string", 3,  R, "Return the bit at the given offset of a string.", "1.0.0", "GETBIT key offset",
      "O(1)",
      "full"),
    v("GETEX",       "string", -2, W, "Read a key and set its expiry in the same call.", "1.0.0", "GETEX key [EX seconds | PX milliseconds]",
      "O(1)",
      "differs: EXAT / PXAT / PERSIST are not accepted, and an expiry of zero or less is refused rather than applied"),
    v("GETRANGE",    "string", 4,  R, "Return a byte range of a string value.", "1.0.0", "GETRANGE key start end",
      "O(M) over the returned bytes",
      "full"),
    v("SETBIT",      "string", 4,  W, "Set the bit at the given offset, returning its old value.", "1.0.0", "SETBIT key offset value",
      "O(1) amortised; O(N) on the call that grows the value out to the offset",
      "full"),
    v("SETRANGE",    "string", 4,  W, "Overwrite a string from the given offset, returning the new length.", "1.0.0", "SETRANGE key offset value",
      "O(M) over the written bytes; O(N) on the call that grows the value out to the offset",
      "full"),
    v("DECR",        "string", 2,  W, "Decrement the integer value of a key by one.", "1.0.0", "DECR key",
      "O(1)",
      "full"),
    v("DECRBY",      "string", 3,  W, "Decrement the integer value of a key by the given amount.", "1.0.0", "DECRBY key decrement",
      "O(1)",
      "full"),
    v("GET",         "string", 2,  R, "Return the string value of a key.", "1.0.0", "GET key",
      "O(1)",
      "full"),
    v("GETDEL",      "string", 2,  W, "Return the string value of a key and delete it.", "1.0.0", "GETDEL key",
      "O(1)",
      "full"),
    v("GETSET",      "string", 3,  W, "Set a key's string value and return its previous value.", "1.0.0", "GETSET key value",
      "O(1)",
      "full"),
    v("INCR",        "string", 2,  W, "Increment the integer value of a key by one.", "1.0.0", "INCR key",
      "O(1)",
      "full"),
    v("INCRBY",      "string", 3,  W, "Increment the integer value of a key by the given amount.", "1.0.0", "INCRBY key increment",
      "O(1)",
      "full"),
    v("INCRBYFLOAT", "string", 3,  W, "Increment the float value of a key by the given amount.", "1.0.0", "INCRBYFLOAT key increment",
      "O(1)",
      "differs: arithmetic is f64 and the reply uses Rust's shortest-round-trip float printer, not a long double accumulator, so trailing digits can diverge from Redis"),
    v("MGET",        "string", -2, R, "Return the values of multiple keys (cross-shard gather).", "1.0.0", "MGET key [key ...]",
      "O(N) keys, one O(1) probe each on the owning shard",
      "differs: keys are grouped by shard and each shard is probed independently, so the reply is not a point-in-time snapshot across shards"),
    v("MSET",        "string", -3, W, "Set multiple key/value pairs atomically per shard.", "1.0.0", "MSET key value [key value ...]",
      "O(N) pairs, one O(1) SET each",
      "differs: atomic only per shard — pairs are split by key and applied shard-by-shard with no cross-shard barrier, so a reader can observe some pairs written and others not; Redis MSET is globally atomic"),
    v("PSETEX",      "string", 4,  W, "Set a key's value with a TTL in milliseconds.", "1.0.0", "PSETEX key milliseconds value",
      "O(1)",
      "full"),
    v("SET",         "string", -3, W, "Set a key's string value with optional TTL and existence conditions.", "1.0.0", "SET key value [EX seconds|PX milliseconds] [NX|XX]",
      "O(1)",
      "differs: only NX / XX / EX / PX are parsed — KEEPTTL, EXAT, PXAT, GET and the IFEQ family are a syntax error; a repeated expire option is silently accepted last-one-wins instead of erroring"),
    v("SETEX",       "string", 4,  W, "Set a key's value with a TTL in seconds.", "1.0.0", "SETEX key seconds value",
      "O(1)",
      "full"),
    v("SETNX",       "string", 3,  W, "Set a key's value only if it does not exist.", "1.0.0", "SETNX key value",
      "O(1)",
      "full"),
    v("STRLEN",      "string", 2,  R, "Return the length of a key's string value.", "1.0.0", "STRLEN key",
      "O(1)",
      "full"),
];