1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
//! [`KevyCommands::resolve`]'s body — extracted from [`crate::lib`] to
//! keep that file under the 500-LOC house rule.
//!
//! The runtime calls `Commands::resolve` once per parsed command and
//! reads back `txn_kind` / `route` / `is_quit` / `is_write` /
//! `block_hint` / `wake_idx` from the returned [`ResolvedCmd`] without
//! re-scanning the verb. Folding every per-attribute scan into one
//! `match upper` is the primary hot-path win — keeping the body in
//! one place makes that contract obvious.
use kevy_resp::ArgvView;
use kevy_rt::{ResolvedCmd, Route, TxnKind, parse_slowlog_sub};
use crate::cmd::{self, scan_pattern, upper_verb};
use crate::cmd_block;
/// One-pass verb resolution for [`crate::KevyCommands`]. Single `match upper`
/// fans out into the per-attribute fields the runtime then consumes.
pub(crate) fn kevy_resolve<A: ArgvView + ?Sized>(args: &A) -> ResolvedCmd {
let Some(name) = args.first() else {
return ResolvedCmd {
txn_kind: TxnKind::Other,
route: Route::Local,
is_quit: false,
is_write: false,
block_hint: kevy_rt::BlockHint::None,
wake_idx: None,
};
};
let mut buf = [0u8; 32];
let upper = upper_verb(name, &mut buf);
// Tier-1 fast path (mirrors `dispatch_with_proto`'s): GET / SET resolve
// in ONE comparison each instead of walking the txn + route (~40 arms) +
// is_write + block_hint + wake_idx matches, all of which land in their
// catch-alls for these two verbs. Field values are byte-identical to
// what the general path below computes.
match upper {
b"GET" | b"SET" => {
return ResolvedCmd {
txn_kind: TxnKind::Other,
route: if args.len() >= 2 { Route::Single(1) } else { Route::Local },
is_quit: false,
is_write: upper == b"SET",
block_hint: kevy_rt::BlockHint::None,
wake_idx: None,
};
}
_ => {}
}
resolve_general(upper, args)
}
/// The general (non-GET/SET) resolution tail: one lookup per
/// [`ResolvedCmd`] field. Single call site in [`kevy_resolve`];
/// `inline(always)` keeps the split codegen-identical to the
/// pre-split fused body (this is still the per-op path for every
/// verb outside the tier-1 pair).
#[inline(always)]
fn resolve_general<A: ArgvView + ?Sized>(upper: &[u8], args: &A) -> ResolvedCmd {
let txn_kind = match upper {
b"MULTI" => TxnKind::Multi,
b"EXEC" => TxnKind::Exec,
b"DISCARD" => TxnKind::Discard,
b"WATCH" => TxnKind::Watch,
_ => TxnKind::Other,
};
let is_quit = upper == b"QUIT";
let is_write = cmd::is_write_verb(upper);
let route = route_for_verb(upper, args);
let block_hint = cmd_block::block_hint_for_verb(upper, args);
let wake_idx = cmd_block::wake_idx_for_verb(upper);
ResolvedCmd {
txn_kind,
route,
is_quit,
is_write,
block_hint,
wake_idx,
}
}
/// Map an uppercased verb + its argv to the routing decision the
/// runtime uses to pick local-fast-path / single-shard / multi-target
/// / pub/sub / transactional control. Pure data; the cost is one `match
/// upper` plus the small extractor calls (KEYS pattern, SCAN cursor,
/// XREAD STREAMS key, SLOWLOG sub-command).
// LOC-WAIVER: data-driven verb → Route match table — one arm per verb.
fn route_for_verb<A: ArgvView + ?Sized>(upper: &[u8], args: &A) -> Route {
match upper {
b"HELLO" => Route::Hello,
b"PING" | b"ECHO" | b"QUIT" | b"COMMAND" | b"CONFIG" | b"INFO" | b"CLUSTER" | b"DEBUG"
| b"SHUTDOWN" | b"CLIENT" | b"SELECT" | b"BLPOP" | b"BRPOP" | b"BZPOPMIN" | b"BRPOPLPUSH" => {
Route::Local
}
// v3.16 D1+D2 — replication barriers. Well-formed happy paths
// route to the runtime's deferred waiters; every immediate
// answer (arity / role / gen mismatch) falls back to Local and
// the cmd_repl dispatch handlers emit the precise reply.
b"WAIT" => crate::cmd_repl::wait_route(args),
b"REPL.TOKEN" => crate::cmd_repl::token_route(args),
b"REPL.WAIT" => crate::cmd_repl::repl_wait_route(args),
b"DBSIZE" => Route::Dbsize,
b"FLUSHDB" | b"FLUSHALL" => Route::Flush,
b"SAVE" => Route::Save,
b"BGSAVE" => Route::BgSave,
b"BGREWRITEAOF" => Route::RewriteAof,
b"MSET" if args.len() >= 3 && !args.len().is_multiple_of(2) => Route::MSet,
b"MGET" if args.len() >= 2 => Route::MGet,
b"SINTER" if args.len() >= 2 => Route::SInter,
b"SUNION" if args.len() >= 2 => Route::SUnion,
b"SDIFF" if args.len() >= 2 => Route::SDiff,
b"KEYS" if args.len() == 2 => Route::Keys(Some(args[1].to_vec())),
b"SCAN" if args.len() >= 2 => Route::Scan(scan_pattern(args)),
b"RANDOMKEY" if args.len() == 1 => Route::RandomKey,
b"SUBSCRIBE" if args.len() >= 2 => Route::Subscribe,
b"UNSUBSCRIBE" => Route::Unsubscribe,
b"PSUBSCRIBE" if args.len() >= 2 => Route::Psubscribe,
b"PUNSUBSCRIBE" => Route::Punsubscribe,
b"PUBLISH" if args.len() == 3 => Route::Publish,
b"WATCH" if args.len() >= 2 => Route::Watch,
b"UNWATCH" => Route::Unwatch,
b"ZINTERSTORE" if args.len() >= 4 => Route::ZAlgebraStore(kevy_rt::ZCombine::ZInter),
b"ZUNIONSTORE" if args.len() >= 4 => Route::ZAlgebraStore(kevy_rt::ZCombine::ZUnion),
b"ZDIFFSTORE" if args.len() >= 4 => Route::ZAlgebraStore(kevy_rt::ZCombine::ZDiff),
b"SINTERSTORE" if args.len() >= 3 => Route::ZAlgebraStore(kevy_rt::ZCombine::SInter),
b"SUNIONSTORE" if args.len() >= 3 => Route::ZAlgebraStore(kevy_rt::ZCombine::SUnion),
b"SDIFFSTORE" if args.len() >= 3 => Route::ZAlgebraStore(kevy_rt::ZCombine::SDiff),
b"ZINTERCARD" if args.len() >= 3 => Route::ZInterCard,
b"IDX.QUERY" if args.len() >= 4 => Route::Extension,
b"IDX.EXPLAIN" if args.len() >= 2 => Route::Extension,
b"IDX.REBUILD" if args.len() == 2 => Route::Extension,
b"IDX.COUNT" if args.len() >= 4 => Route::Extension,
b"IDX.VERIFY" if args.len() == 2 => Route::Extension,
b"IDX.LIST" if args.len() == 1 => Route::Extension,
b"VIEW.QUERY" if args.len() >= 2 => Route::Extension,
b"VIEW.LIST" if args.len() == 1 => Route::Extension,
b"VIEW.VERIFY" if args.len() == 2 => Route::Extension,
b"VIEW.REBUILD" if args.len() == 2 => Route::Extension,
b"VIEW.EXPLAIN" if args.len() == 2 => Route::Extension,
b"PREFIX.STATS" if args.len() == 2 => Route::PrefixStats,
b"PREFIX.DIGEST" if args.len() == 2 => Route::Extension,
b"FEED.READ" if args.len() >= 4 => Route::FeedRead,
b"FEED.TAIL" if args.len() == 2 => Route::FeedTail,
b"FEED.SHARDS" if args.len() == 1 => Route::FeedShards,
b"RENAME" => Route::Rename { nx: false },
b"RENAMENX" => Route::Rename { nx: true },
// (BLPOP / BRPOP fold into the Local-routed verb list above —
// they park on the conn's own origin shard, from where the
// cross-shard arbiter fans watch registrations out to each key's
// owning shard, see kevy_rt::block_xshard. Routing by key would
// strand the waiter on a shard that doesn't own the connection.)
// v1.27.1: EVAL/EVALSHA route by KEYS[1] (at argv[3]) when
// numkeys ≥ 1, so a multi-shard server lands the script on
// the shard that owns the keys it'll touch. With numkeys=0
// the script doesn't touch any specific shard's keyspace, so
// we let it run on the connection's own shard.
// SCRIPT subcommands all hit a process-global cache
// (see `crate::cmd_lua`), so Route::Local is fine for them.
b"EVAL" | b"EVALSHA" | b"EVAL_RO" | b"EVALSHA_RO" => {
if args.len() >= 4 {
let nk = std::str::from_utf8(&args[2])
.ok()
.and_then(|s| s.parse::<i64>().ok())
.unwrap_or(0);
if nk >= 1 && (args.len() as i64) >= 3 + nk {
Route::Single(3)
} else {
Route::Local
}
} else {
Route::Local
}
}
b"SCRIPT" => Route::Local,
b"XREAD" => cmd_block::xread_route(args),
b"XREADGROUP" => cmd_block::xreadgroup_route(args),
// XGROUP / XINFO put the stream key at args[2] (after the
// subcommand), not args[1] — route by the real key so a
// multi-shard server lands on the shard that owns the stream.
// Keyless forms (HELP) fall back to Local.
b"XGROUP" | b"XINFO" => {
if args.len() >= 3 {
Route::Single(2)
} else {
Route::Local
}
}
b"SLOWLOG" => Route::Slowlog(parse_slowlog_sub(args)),
b"DEL" | b"UNLINK" => {
if args.len() == 2 {
Route::Single(1)
} else {
Route::DelKeys
}
}
b"EXISTS" => {
if args.len() == 2 {
Route::Single(1)
} else {
Route::ExistsKeys
}
}
_ => {
if args.len() >= 2 {
Route::Single(1)
} else {
Route::Local
}
}
}
}