kevy 6.3.0

kevy — a pure-Rust, zero-dependency, Redis-compatible KV server.
Documentation
//! Lists, hashes, sets and sorted sets.
//!
//! 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 (SSCAN
//! is a single-batch copy, SPOP is deterministic, SINTER has no
//! smallest-set-first ordering).

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

#[rustfmt::skip]
pub(super) const ROWS: &[VerbMeta] = &[
    // ---- hash ------------------------------------------------------
    v("HDEL",        "hash", -3, W, "Delete one or more hash fields.", "1.0.0", "HDEL key field [field ...]",
      "O(M) fields",
      "full"),
    v("HEXISTS",     "hash", 3,  R, "Check whether a hash field exists.", "1.0.0", "HEXISTS key field",
      "O(1)",
      "full"),
    v("HEXPIRE",     "hash", -6, W, "Set per-field TTLs on a hash, in seconds.", "1.0.0", "HEXPIRE key seconds [NX|XX|GT|LT] FIELDS numfields field [field ...]",
      "O(F) fields in the FIELDS clause",
      "differs: the FIELDS parse error reads 'Mandatory keyword FIELDS is missing' where Redis says 'Mandatory argument FIELDS'"),
    v("HGET",        "hash", 3,  R, "Return the value of a hash field.", "1.0.0", "HGET key field",
      "O(1)",
      "full"),
    v("HGETALL",     "hash", 2,  R, "Return all fields and values of a hash.", "1.0.0", "HGETALL key",
      "O(N) fields, with a full copy of every name and value",
      "full"),
    v("HINCRBY",     "hash", 4,  W, "Increment the integer value of a hash field.", "1.0.0", "HINCRBY key field increment",
      "O(1)",
      "full"),
    v("HINCRBYFLOAT", "hash", 4,  W, "Increment the float value of a hash field.", "1.0.0", "HINCRBYFLOAT key field increment",
      "O(1)",
      "full"),
    v("HRANDFIELD",  "hash", -2, R, "Return random fields from a hash; a negative count allows repeats, WITHVALUES pairs each with its value.", "6.2.0", "HRANDFIELD key [count [WITHVALUES]]",
      "O(count) for the positive form (partial Fisher-Yates over a materialised field list), O(|count|) for the negative form",
      "full"),
    v("HKEYS",       "hash", 2,  R, "Return all field names of a hash.", "1.0.0", "HKEYS key",
      "O(N) fields",
      "full"),
    v("HLEN",        "hash", 2,  R, "Return the number of fields in a hash.", "1.0.0", "HLEN key",
      "O(1)",
      "full"),
    v("HMGET",       "hash", -3, R, "Return the values of multiple hash fields.", "1.0.0", "HMGET key field [field ...]",
      "O(M) fields requested",
      "full"),
    v("HMSET",       "hash", -4, W, "Deprecated alias of HSET that replies OK.", "1.0.0", "HMSET key field value [field value ...]",
      "O(M) pairs",
      "full"),
    v("HPERSIST",    "hash", -5, W, "Remove per-field TTLs from a hash.", "1.0.0", "HPERSIST key FIELDS numfields field [field ...]",
      "O(F) fields",
      "differs: silently accepts a stray NX|XX|GT|LT token before FIELDS (it shares HEXPIRE's parser) where Redis rejects it"),
    v("HPEXPIRE",    "hash", -6, W, "Set per-field TTLs on a hash, in milliseconds.", "1.0.0", "HPEXPIRE key milliseconds [NX|XX|GT|LT] FIELDS numfields field [field ...]",
      "O(F) fields",
      "differs: same FIELDS error-text deviation as HEXPIRE"),
    v("HPEXPIREAT",  "hash", -6, W, "Set per-field expiries as absolute Unix-millisecond deadlines.", "1.0.0", "HPEXPIREAT key unix-time-milliseconds [NX|XX|GT|LT] FIELDS numfields field [field ...]",
      "O(F) fields",
      "differs: same FIELDS error-text deviation as HEXPIRE"),
    v("HSCAN",       "hash", -3, R, "Iterate a hash's fields and values (single-batch cursor).", "1.0.0", "HSCAN key cursor [MATCH pattern] [COUNT count]",
      "O(N) — the whole hash is copied and returned in one batch",
      "differs: not a cursor iterator — the cursor is always 0 and the entire hash comes back in a single reply; NOVALUES is not parsed and COUNT is validated then ignored"),
    v("HSET",        "hash", -4, W, "Set one or more hash fields; returns the number of new fields.", "1.0.0", "HSET key field value [field value ...]",
      "O(M) pairs",
      "full"),
    v("HSETNX",      "hash", 4,  W, "Set a hash field only if it does not exist.", "1.0.0", "HSETNX key field value",
      "O(1)",
      "full"),
    v("HPTTL",       "hash", -5, R, "Return per-field remaining TTLs in milliseconds (-1 no TTL, -2 missing).", "4.0.0", "HPTTL key FIELDS numfields field [field ...]",
      "O(F) fields",
      "differs: silently accepts a stray NX|XX|GT|LT token before FIELDS where Redis rejects it"),
    v("HTTL",        "hash", -5, R, "Return per-field remaining TTLs in seconds (-1 no TTL, -2 missing).", "1.0.0", "HTTL key FIELDS numfields field [field ...]",
      "O(F) fields",
      "differs: silently accepts a stray NX|XX|GT|LT token before FIELDS where Redis rejects it"),
    v("HVALS",       "hash", 2,  R, "Return all values of a hash.", "1.0.0", "HVALS key",
      "O(N) fields",
      "full"),
    // ---- list ------------------------------------------------------
    v("BLPOP",       "list", -3, WB, "Blocking left pop across one or more lists (effect logged as LPOP).", "1.0.0", "BLPOP key [key ...] timeout",
      "O(1) to serve; the park is O(1) and waiters are woken FIFO per key",
      "differs: on a multi-shard server the multi-key form does not honour Redis's left-to-right key priority — keys on the connection's own shard are served before remote ones"),
    v("BRPOP",       "list", -3, WB, "Blocking right pop across one or more lists (effect logged as RPOP).", "1.0.0", "BRPOP key [key ...] timeout",
      "O(1) to serve; FIFO park",
      "differs: same multi-key priority deviation as BLPOP"),
    v("BRPOPLPUSH",  "list", 4,  WB, "Blocking RPOPLPUSH: pop the source tail and push it onto the destination head.", "1.0.0", "BRPOPLPUSH source destination timeout",
      "O(1) to serve",
      "differs: a cross-shard move is not atomic — the element is popped on the source's shard and pushed on the destination's, and between those two steps it is in neither list. Co-locate the keys with a {hashtag} to get Redis's atomic path"),
    v("LINDEX",      "list", 3,  R,  "Return the element at the given list index.", "1.0.0", "LINDEX key index",
      "O(1) — the list is a VecDeque, so an index is a direct offset (Redis's quicklist is O(N))",
      "full"),
    v("LLEN",        "list", 2,  R,  "Return the length of a list.", "1.0.0", "LLEN key",
      "O(1)",
      "full"),
    v("LMOVE",       "list", 5,  W,  "Atomically move an element between two lists.", "1.0.0", "LMOVE source destination LEFT|RIGHT LEFT|RIGHT",
      "O(1)",
      "differs: a cross-shard move is not atomic — see BRPOPLPUSH"),
    v("LPOP",        "list", -2, W,  "Pop one or more elements from the head of a list.", "1.0.0", "LPOP key [count]",
      "O(C) elements popped",
      "differs: LPOP key 0 on an existing key replies a nil array where Redis replies an empty array"),
    v("LPOS",        "list", -3, R,  "Return the index of matching elements in a list.", "1.0.0", "LPOS key element [RANK rank] [COUNT num-matches] [MAXLEN len]",
      "O(N) always — the whole list is copied before RANK / COUNT / MAXLEN are applied, so MAXLEN bounds the comparisons but not the copy",
      "full"),
    v("LPUSH",       "list", -3, W,  "Prepend one or more elements to a list.", "1.0.0", "LPUSH key element [element ...]",
      "O(M) values, O(1) amortised each",
      "full"),
    v("LRANGE",      "list", 4,  R,  "Return a range of list elements.", "1.0.0", "LRANGE key start stop",
      "O(M) elements returned — the start offset is skipped in O(1) (Redis is O(S+M))",
      "full"),
    v("LREM",        "list", 4,  W,  "Remove elements equal to the given value from a list.", "1.0.0", "LREM key count element",
      "O(N*R) worst case — the scan is O(N) but each removal shifts up to half the deque (Redis is O(N+R))",
      "full"),
    v("LINSERT",     "list", 5,  W,  "Insert an element before or after a pivot element.", "1.0.0", "LINSERT key BEFORE|AFTER pivot element",
      "O(N) — the pivot is found by a scan from the head",
      "full"),
    v("LSET",        "list", 4,  W,  "Set the list element at the given index.", "1.0.0", "LSET key index element",
      "O(1) — a direct index write (Redis is O(N) for a mid-list index)",
      "full"),
    v("LTRIM",       "list", 4,  W,  "Trim a list to the given range.", "1.0.0", "LTRIM key start stop",
      "O(R) elements removed",
      "full"),
    v("RPOP",        "list", -2, W,  "Pop one or more elements from the tail of a list.", "1.0.0", "RPOP key [count]",
      "O(C) elements popped",
      "differs: RPOP key 0 on an existing key replies a nil array where Redis replies an empty array"),
    v("RPOPLPUSH",   "list", 3,  W,  "Pop the source tail and push it onto the destination head.", "1.0.0", "RPOPLPUSH source destination",
      "O(1)",
      "differs: a cross-shard move is not atomic — see BRPOPLPUSH"),
    v("RPUSH",       "list", -3, W,  "Append one or more elements to a list.", "1.0.0", "RPUSH key element [element ...]",
      "O(M) values, O(1) amortised each",
      "full"),
    // ---- set -------------------------------------------------------
    v("SADD",        "set", -3, W, "Add one or more members to a set.", "1.0.0", "SADD key member [member ...]",
      "O(M) members",
      "full"),
    v("SCARD",       "set", 2,  R, "Return the number of members in a set.", "1.0.0", "SCARD key",
      "O(1)",
      "full"),
    v("SDIFF",       "set", -2, R, "Return the difference of the given sets (cross-shard gather).", "1.0.0", "SDIFF key [key ...]",
      "O(sum of the source cardinalities)",
      "full"),
    v("SDIFFSTORE",  "set", -3, W, "Store the difference of the given sets into destination.", "1.0.0", "SDIFFSTORE destination key [key ...]",
      "O(sum of the source cardinalities) + O(result) to write the destination on its own shard",
      "full"),
    v("SINTER",      "set", -2, R, "Return the intersection of the given sets (cross-shard gather).", "1.0.0", "SINTER key [key ...]",
      "O(sum of the source cardinalities) — there is NO smallest-set-first ordering, so SINTER huge tiny costs O(huge) where Redis costs O(tiny * k)",
      "full"),
    v("SINTERSTORE", "set", -3, W, "Store the intersection of the given sets into destination.", "1.0.0", "SINTERSTORE destination key [key ...]",
      "O(sum of the source cardinalities) + O(result); same no-smallest-first algorithm",
      "full"),
    v("SISMEMBER",   "set", 3,  R, "Check whether a value is a member of a set.", "1.0.0", "SISMEMBER key member",
      "O(1)",
      "full"),
    v("SMEMBERS",    "set", 2,  R, "Return all members of a set.", "1.0.0", "SMEMBERS key",
      "O(N) members",
      "full"),
    v("SPOP",        "set", -2, W, "Remove and return one or more random members of a set.", "1.0.0", "SPOP key [count]",
      "O(count) expected — each member is drawn by probing a random slot and taking the first occupied one",
      "full"),
    v("SRANDMEMBER", "set", -2, R, "Return one or more random members of a set without removing them. A negative count allows repeats.", "1.0.0", "SRANDMEMBER key [count]",
      "O(count) expected when count is a small fraction of the set (random-slot probing); O(N) once count exceeds a quarter of it, where copying and shuffling beats rejection sampling",
      "full"),
    v("SREM",        "set", -3, W, "Remove one or more members from a set.", "1.0.0", "SREM key member [member ...]",
      "O(M) members",
      "full"),
    v("SSCAN",       "set", -3, R, "Iterate a set's members (single-batch cursor).", "1.0.0", "SSCAN key cursor [MATCH pattern] [COUNT count]",
      "O(N) — the whole set is copied and returned in one batch",
      "differs: not a cursor iterator — the cursor is always 0 and the entire set comes back in one reply; COUNT is validated then ignored"),
    v("SUNION",      "set", -2, R, "Return the union of the given sets (cross-shard gather).", "1.0.0", "SUNION key [key ...]",
      "O(sum of the source cardinalities)",
      "full"),
    v("SUNIONSTORE", "set", -3, W, "Store the union of the given sets into destination.", "1.0.0", "SUNIONSTORE destination key [key ...]",
      "O(sum of the source cardinalities) + O(result)",
      "full"),
    // ---- zset ------------------------------------------------------
    v("BZPOPMIN",    "zset", -3, WB, "Blocking pop of the lowest-scored member across one or more sorted sets.", "1.0.0", "BZPOPMIN key [key ...] timeout",
      "O(log N) per pop; the park is O(1)",
      "full"),
    v("ZADD",        "zset", -4, W,  "Add members with scores to a sorted set, with conditional flags.", "1.0.0", "ZADD key [NX|XX] [GT|LT] [CH] [INCR] score member [score member ...]",
      "O(M log N) — O(1) hash insert plus O(log N) tree insert per member",
      "full"),
    v("ZCARD",       "zset", 2,  R,  "Return the number of members in a sorted set.", "1.0.0", "ZCARD key",
      "O(1)",
      "full"),
    v("ZCOUNT",      "zset", 4,  R,  "Count members with scores within the given bounds.", "1.0.0", "ZCOUNT key min max",
      "O(log N) — two rank descents on the order-statistic tree bracket the score range; nothing is scanned",
      "full"),
    v("ZDIFFSTORE",  "zset", -4, W,  "Store the difference of the given sorted sets into destination.", "1.0.0", "ZDIFFSTORE destination numkeys key [key ...]",
      "O(sum of the source cardinalities + R log R); each source is extracted whole on its own shard",
      "full"),
    v("ZINCRBY",     "zset", 4,  W,  "Increment a member's score in a sorted set.", "1.0.0", "ZINCRBY key increment member",
      "O(log N)",
      "full"),
    v("ZINTERCARD",  "zset", -3, R,  "Return the cardinality of the intersection of the given sorted sets.", "1.0.0", "ZINTERCARD numkeys key [key ...] [LIMIT limit]",
      "O(sum of the source cardinalities) — LIMIT does not short-circuit the gather, only the combine",
      "full"),
    v("ZINTERSTORE", "zset", -4, W,  "Store the intersection of the given sorted sets into destination.", "1.0.0", "ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]",
      "O(sum of the source cardinalities + R log R)",
      "full"),
    v("ZPOPMIN",     "zset", -2, W,  "Pop up to count members with the lowest scores.", "1.0.0", "ZPOPMIN key [count]",
      "O(M log N)",
      "full"),
    v("ZPOPMIN.BELOW", "zset", -3, &["write", "extension"], "kevy extension: pop up to count lowest members with score strictly below the threshold (delayed-job primitive).", "3.0.0", "ZPOPMIN.BELOW key below [count]",
      "O(M log N) — take_while stops at the first score at or above the threshold, so it never walks past the due prefix",
      "kevy-only: a delayed-job primitive (score = due time); no Redis analogue"),
    v("ZRANGE",      "zset", -4, R,  "Return members by rank range.", "1.0.0", "ZRANGE key start stop [WITHSCORES]",
      "O(log N + M) — one rank descent seeks to start, then the M requested members are walked",
      "differs: only the legacy 'key start stop [WITHSCORES]' form is accepted — the Redis 6.2 BYSCORE / BYLEX / REV / LIMIT arguments are a syntax error"),
    v("ZRANGEBYSCORE", "zset", -4, R, "Return members with scores within the given bounds.", "1.0.0", "ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]",
      "O(log N + M) — a rank descent seeks to the score bound, then only the M in-range members are walked; LIMIT is still applied after the match set is materialised, so it bounds the reply, not the walk",
      "full"),
    v("ZRANK",       "zset", 3,  R,  "Return a member's rank, ordered from the lowest score.", "1.0.0", "ZRANK key member",
      "O(log N) — a hash lookup for the score, then one descent of the rank-augmented (score, member) tree",
      "differs: the Redis 7.2 WITHSCORE option is not supported"),
    v("ZREM",        "zset", -3, W,  "Remove one or more members from a sorted set.", "1.0.0", "ZREM key member [member ...]",
      "O(M log N)",
      "full"),
    v("ZREMRANGEBYRANK", "zset", 4, W, "Remove members within the given rank range.", "1.0.0", "ZREMRANGEBYRANK key start stop",
      "O(log N + M log N) — a rank descent seeks to start, then M member deletes",
      "full"),
    v("ZREMRANGEBYSCORE", "zset", 4, W, "Remove members with scores within the given bounds.", "1.0.0", "ZREMRANGEBYSCORE key min max",
      "O(log N + M log N) — a seek finds the hit set, then M deletes",
      "full"),
    v("ZREVRANGEBYSCORE", "zset", -4, R, "Return members with scores within the given bounds, highest first.", "1.0.0", "ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]",
      "O(log N + M) — a seeked forward walk of the M matches, then the result is reversed",
      "full"),
    v("ZREVRANGE",   "zset", -4, R,  "Return members by rank range, highest score first.", "1.0.0", "ZREVRANGE key start stop [WITHSCORES]",
      "O(N + M) — the set is read in rank order and reversed, then the M requested members are taken",
      "full"),
    v("ZSCAN",       "zset", -3, R,  "Iterate a sorted set's members and scores (single-batch cursor).", "1.0.0", "ZSCAN key cursor [MATCH pattern] [COUNT count]",
      "O(N) — the whole set is materialised",
      "differs: not a cursor iterator — every call returns the whole set with cursor 0, and COUNT is parsed then ignored"),
    v("ZSCORE",      "zset", 3,  R,  "Return a member's score.", "1.0.0", "ZSCORE key member",
      "O(1) — a hash lookup",
      "full"),
    v("ZUNIONSTORE", "zset", -4, W,  "Store the union of the given sorted sets into destination.", "1.0.0", "ZUNIONSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]",
      "O(sum of the source cardinalities + R log R)",
      "full"),
];