kevy_rt/route.rs
1//! [`Route`] — how each command maps onto shards. Returned by
2//! [`crate::Commands::route`] / carried in [`crate::ResolvedCmd`]; the
3//! runtime's `start_command` matches on it to pick a dispatch shape.
4
5use crate::exec_slowlog::SlowlogSub;
6
7/// How a command maps onto shards.
8#[derive(Debug)]
9pub enum Route {
10 /// Keyless; execute on the connection's own shard (e.g. PING).
11 Local,
12 /// Single-key; route by `args[idx]`.
13 Single(usize),
14 /// `args[1..]` are keys; delete each on its shard, sum the counts.
15 DelKeys,
16 /// `args[1..]` are keys; count existing across shards.
17 ExistsKeys,
18 /// Sum every shard's key count.
19 Dbsize,
20 /// Flush every shard.
21 Flush,
22 /// Snapshot every shard's store to disk, synchronously (`SAVE` —
23 /// blocks until durable, the Redis contract for the explicit form).
24 Save,
25 /// `BGSAVE` — collect a COW view per shard and persist in the
26 /// background; the command returns once the views are frozen.
27 BgSave,
28 /// `BGREWRITEAOF` — rebuild every shard's AOF from in-memory state.
29 /// Synchronous in v1.0 (each shard blocks for its own rewrite duration).
30 RewriteAof,
31 /// `MSET` — `args[1..]` are key/value pairs, routed per key's shard.
32 MSet,
33 /// `MGET` — `args[1..]` are keys; values gathered in request order.
34 MGet,
35 /// `SINTER` / `SUNION` / `SDIFF` — `args[1..]` are set keys.
36 SInter,
37 SUnion,
38 SDiff,
39 /// v2.2 zset/set algebra `*STORE` family: gather sources, combine
40 /// per [`crate::message::ZCombine`], materialize at `args[1]`.
41 ZAlgebraStore(crate::ZCombine),
42 /// `ZINTERCARD numkeys key… [LIMIT n]` — read-only gathered count.
43 ZInterCard,
44 /// v2.3 `FEED.READ <shard> <gen> <offset> …` — shard-index routed.
45 FeedRead,
46 /// v2.3 `FEED.TAIL <shard>`.
47 FeedTail,
48 /// v2.3 `FEED.SHARDS` — answered locally.
49 FeedShards,
50 /// v2.3 `PREFIX.STATS <prefix>` — all-shard fanout, summed.
51 PrefixStats,
52 /// v2.5 extension fan-out (IDX.* reads): every shard runs
53 /// `Commands::extension_op`, the origin reduces.
54 Extension,
55 /// `KEYS pattern` — every shard returns its matching keys.
56 Keys(Option<Vec<u8>>),
57 /// `SCAN` (cursor-0 approximation) — like KEYS but replies `[cursor, keys]`.
58 Scan(Option<Vec<u8>>),
59 /// `RANDOMKEY` — one arbitrary key across all shards.
60 RandomKey,
61 /// `SUBSCRIBE` / `UNSUBSCRIBE` — connection-level (modifies this conn).
62 Subscribe,
63 Unsubscribe,
64 /// `PSUBSCRIBE pattern [pattern ...]` / `PUNSUBSCRIBE [pattern ...]` —
65 /// like Subscribe/Unsubscribe but the conn registers Redis-glob
66 /// patterns; `PUBLISH` to a matching channel delivers a `pmessage`
67 /// frame. Connection-level (modifies this conn + shared pattern
68 /// registry).
69 Psubscribe,
70 Punsubscribe,
71 /// `PUBLISH channel message` — delivered to subscribers on every core.
72 Publish,
73 /// `WATCH key [key ...]` — fan-out to record per-shard versions, then
74 /// stash the (key, version) pairs in the conn's `watched` set so the
75 /// next `EXEC` can validate them. Connection-level.
76 Watch,
77 /// `UNWATCH` — clear the conn's `watched` set. Connection-level, local.
78 Unwatch,
79 /// `HELLO [protover [AUTH user pass] [SETNAME name]]` — server
80 /// handshake; on `HELLO 3` flips the conn into RESP3 mode (per-conn
81 /// `proto` field). Reply shape itself is proto-aware (V2: array of
82 /// pairs; V3: Map). Connection-level, dispatch via the
83 /// [`crate::Commands::hello_reply`] hook so embedders set their own server
84 /// metadata.
85 Hello,
86 /// `RENAME source destination` / `RENAMENX source destination`. The
87 /// runtime handles the two-shard decision: same-shard renames go
88 /// through one atomic [`crate::Store::rename`] on the owning shard; cross-
89 /// shard renames use the Take→Put orchestrator (lands in v2-3b;
90 /// v2-3a emits `-CROSSSHARD ...` for that case).
91 Rename {
92 /// `true` for `RENAMENX` (no overwrite — reply `:0` if dst exists).
93 nx: bool,
94 },
95 /// `SLOWLOG GET / LEN / RESET / HELP`. The sub-command + parsed
96 /// args are pre-decoded at routing time so the runtime knows
97 /// whether to short-circuit (HELP / error) or fan out across
98 /// shards (GET / LEN / RESET). See [`crate::parse_slowlog_sub`].
99 Slowlog(SlowlogSub),
100 /// Non-blocking `XREAD` / `XREADGROUP` over **multiple** streams — fan
101 /// each stream out to its owning shard and merge the per-stream replies
102 /// in request order (single-stream forms still route via
103 /// [`Self::Single`]). Each element is `(stream key, last-seen id)`;
104 /// `count` is the optional `COUNT` cap applied per stream; `group`
105 /// `Some` makes each per-shard sub-query an `XREADGROUP` (a write —
106 /// PEL / last-delivered updates happen on each stream's owning shard
107 /// and are AOF-logged there as the rewritten single-stream command).
108 /// The command set builds this only for the non-blocking, ≥2-stream
109 /// forms; blocking reads park on the origin shard instead (see the
110 /// cross-shard BLOCK arbiter).
111 XReadGather {
112 streams: Vec<(Vec<u8>, Vec<u8>)>,
113 count: Option<usize>,
114 group: Option<XGroupCtx>,
115 },
116}
117
118/// The `GROUP <name> <consumer>` (+ `NOACK`) context an `XREADGROUP`
119/// gather carries to each per-stream sub-query.
120#[derive(Debug)]
121pub struct XGroupCtx {
122 /// Consumer-group name.
123 pub group: Vec<u8>,
124 /// Consumer name within the group.
125 pub consumer: Vec<u8>,
126 /// `NOACK` — deliver without adding to the PEL.
127 pub noack: bool,
128}