Skip to main content

yo_resp/dispatch/
server.rs

1//! The connection and server commands.
2//!
3//! None of these touch a key. They are here because a client library sends most
4//! of them before it sends anything else: a driver opens a socket, says `HELLO
5//! 3`, maybe `SELECT 4`, asks `COMMAND DOCS` or `COMMAND COUNT` to build its
6//! own routing table, and only then does any work. A server that answers `GET`
7//! perfectly and `HELLO` badly is a server no client library can talk to, which
8//! is why these land in the same milestone as the string commands rather than
9//! after them.
10//!
11//! The replies were read off a running Redis 8.8 in both protocols. The shapes
12//! are not obvious from the documentation: `HELLO` is a map on RESP3 and the
13//! same pairs flattened on RESP2, `CONFIG GET` is the same, `INFO` is a
14//! verbatim string on RESP3 and a bulk string on RESP2, and the flags in
15//! `COMMAND INFO` are simple strings inside an array rather than bulk strings.
16
17use super::args::{self, Args, is};
18use super::cluster::Migration;
19use super::keyspec::{self, Begin, Find, KeySpec};
20use super::table::{self, Spec};
21use super::{
22    DATABASES, Flow, Server, Session, acl, auth, backup, cpu, debug, multi, notify, persist,
23};
24use crate::proto::Proto;
25use crate::reply::Out;
26use core::fmt::Write;
27use std::time::{SystemTime, UNIX_EPOCH};
28use yo_common::num::parse_i64;
29use yo_common::{Code, Error, Result, glob};
30use yo_kv::Keyspace;
31use yo_kv::access::Policy;
32
33/// What we tell a client we are.
34///
35/// It is a lie and it is a deliberate one. Every client library in the world
36/// branches on this pair to decide which commands exist, and a driver that
37/// reads `yo` here falls back to its oldest code path or refuses to connect.
38/// Divergence D-12 in `divergences.toml` says so, and the honest answer is in
39/// the `yo_version` field of `INFO` next to this one.
40const REPORTED_SERVER: &str = "redis";
41/// The Redis version we answer 100 percent of, which is what `HELLO` reports.
42///
43/// [`super::backup`] writes it into the `redis-ver` aux field of the base file
44/// it produces, so a server told to load one reads the same version out of the
45/// file that a client reads off the connection.
46pub(super) const REPORTED_VERSION: &str = "8.8.0";
47
48/// The settings that are fixed for the life of the process.
49///
50/// `CONFIG SET` accepts a write to one of these that changes nothing and
51/// refuses everything else rather than pretending to have taken it. A client
52/// that sets `appendonly no` on a server that already has no append only file
53/// gets an `OK` and is telling the truth; one that sets `appendonly yes` gets
54/// told it cannot, which is better than an `OK` and no file.
55const SETTINGS: &[(&str, &str)] = &[
56    ("appendonly", "no"),
57    ("appendfsync", "everysec"),
58    // Where `BACKUP` writes, under `dir`. Fixed here where a real server takes
59    // it at startup, because nothing in this build reads it from a file.
60    ("backupdirname", backup::DIR_NAME),
61    ("databases", "16"),
62    ("proto-max-bulk-len", "536870912"),
63    // How much of the command stream a master keeps for a replica that comes
64    // back, which is a compiled in size here and happens to be the size Redis
65    // ships with. Fixed rather than writable because the backlog is one buffer
66    // that is allocated once and resizing it under a replica that is reading
67    // out of it is a change of its own. There is a test below that this number
68    // and `repl::BACKLOG_BYTES` are the same number.
69    ("repl-backlog-size", "1048576"),
70    ("save", ""),
71    ("timeout", "0"),
72];
73
74/// Which number on the size ladder a settings name refers to.
75#[derive(Debug, Clone, Copy, PartialEq, Eq)]
76enum Knob {
77    SetIntsetEntries,
78    SetListpackEntries,
79    SetListpackValue,
80    HashListpackEntries,
81    HashListpackValue,
82    MaxmemorySamples,
83    LfuLogFactor,
84    LfuDecayTime,
85}
86
87/// The settings that move the size ladder, which are the ones that really move.
88///
89/// These decide where a collection stops being a packed blob and becomes an
90/// element table, so they decide what `OBJECT ENCODING` answers, and a client
91/// that reads `OBJECT ENCODING` after setting one of these expects the two to
92/// agree. That is the whole reason they are writable when nothing else here is.
93///
94/// The `ziplist` spellings are the names these had before Redis renamed them
95/// and it still answers to both, so this does too. Two names, one number: a
96/// `CONFIG SET hash-max-ziplist-entries 4` shows up under the listpack name
97/// too, which was checked against 8.10.1 rather than assumed.
98///
99/// Moving one of these leaves every collection that already exists exactly as
100/// it is, and only decides what the next write builds. Redis does the same, and
101/// it is the reason `CONFIG SET set-max-listpack-entries 0` does not rewrite
102/// the keyspace.
103///
104/// The three eviction numbers are in here too, which stretches the name a
105/// little. They belong with these rather than with the immutable settings for
106/// the same reason: a client that sets one and then reads `OBJECT FREQ` or
107/// watches `evicted_keys` expects the two to agree. `maxmemory-samples` says how
108/// many keys a round of sampling looks at, and the two `lfu` numbers set what
109/// the counter under an LFU policy actually measures.
110const LADDER: &[(&str, Knob)] = &[
111    ("hash-max-listpack-entries", Knob::HashListpackEntries),
112    ("hash-max-listpack-value", Knob::HashListpackValue),
113    ("hash-max-ziplist-entries", Knob::HashListpackEntries),
114    ("hash-max-ziplist-value", Knob::HashListpackValue),
115    ("lfu-decay-time", Knob::LfuDecayTime),
116    ("lfu-log-factor", Knob::LfuLogFactor),
117    ("maxmemory-samples", Knob::MaxmemorySamples),
118    ("set-max-intset-entries", Knob::SetIntsetEntries),
119    ("set-max-listpack-entries", Knob::SetListpackEntries),
120    ("set-max-listpack-value", Knob::SetListpackValue),
121];
122
123/// The setting that decides which way the access field on every record is read.
124///
125/// It is on its own rather than in [`SETTINGS`] or [`LADDER`] because it is the
126/// only writable setting that is not a number, and rather than immutable because
127/// it really moves: a client that sets it and then reads `OBJECT FREQ` expects
128/// the two to agree, which is the same argument the size ladder makes.
129///
130/// Setting it changes nothing about the keys already stored. Whatever is in
131/// their access field stays there and means something different from the moment
132/// the policy changes, which is what the `OBJECT FREQ` error text warns about.
133const MAXMEMORY_POLICY: &str = "maxmemory-policy";
134
135/// How many threads are running commands, which is whatever the server was
136/// started with.
137///
138/// Immutable like the settings in [`SETTINGS`] and read from the server rather
139/// than written down next to them, which is the whole reason it is out here: the
140/// number is decided at startup by whoever starts the threads, so the fixed `1`
141/// it used to be was a lie on every server started with more than one. A write
142/// to it is treated the way a write to a fixed setting is, so `CONFIG SET
143/// io-threads` takes the count the server already has and refuses the rest.
144///
145/// `INFO server` reports the same number under `io_threads_active`. Redis means
146/// the configured count by that field rather than the count of threads that have
147/// picked work up, and matching Redis is why the field is there at all.
148const IO_THREADS: &str = "io-threads";
149
150/// How much the server is allowed to hold before it starts evicting.
151///
152/// Also on its own, and for the third different reason. It is not immutable,
153/// it is not on the size ladder and it is the only setting whose value is not a
154/// plain integer: a client writes `maxmemory 100mb` and means a hundred and
155/// four million bytes, so it needs a parser of its own.
156///
157/// Zero means no limit, which is the default and is what makes the check in
158/// front of every write one comparison. Setting it to a number smaller than
159/// what the server is already holding is allowed and is a real thing to do: the
160/// next write that would allocate evicts until it fits or is refused, which is
161/// what the `maxmemory-policy` decides between.
162const MAXMEMORY: &str = "maxmemory";
163
164/// How much the server is allowed to keep on the file before it starts evicting.
165///
166/// The other half of the eviction inversion `14` section 4.1 describes, and the
167/// only setting here that has no counterpart in Redis. `maxmemory` is a limit on
168/// memory, and the right answer to a memory limit on a system with a file under
169/// it is to move data to the file. Throwing data away is the right answer to a
170/// limit on the file, and this is that limit.
171///
172/// Minus one is no limit and is the default, so a server that never sets this
173/// grows until the disk is full and then refuses writes, which is what a
174/// database does. Zero is a real setting and it means the file may hold nothing,
175/// so migration cannot make room and eviction is all that is left, which is
176/// Redis exactly and is the documented setting for a drop in cache.
177const MAXSTORE: &str = "maxstore";
178
179/// Where the server writes, which `BACKUP LIST` answers paths under.
180///
181/// On its own for a fourth reason: it is readable and not writable, and it is
182/// not writable in a way of its own. Redis calls it a protected config, which
183/// means `CONFIG SET dir` is refused with a sentence about protection rather
184/// than about immutability unless the server was started with protected configs
185/// enabled. That distinction is copied, because the two messages are what an
186/// operator reads when a `CONFIG SET` does not take.
187const DIR: &str = "dir";
188
189/// What `SAVE` writes, under [`DIR`].
190///
191/// Protected in the same way and for a weaker version of the same reason: a
192/// server whose file name moved under a running backup script leaves a file
193/// nothing goes looking for. Redis protects it too, so `CONFIG SET dbfilename`
194/// is refused there as well without protected configs turned on.
195const DBFILENAME: &str = "dbfilename";
196
197/// The password every connection is asked for, empty when none is.
198///
199/// Writable, and the one setting here whose value is a secret. It reads back in
200/// the clear, which is what a real server does and is not an oversight of one:
201/// an operator who can send `CONFIG GET` on this server can already read
202/// everything in it.
203const REQUIREPASS: &str = "requirepass";
204
205/// How long a sealed backup is kept before it cleans itself up.
206///
207/// Seconds, and zero is the default and means it is kept until somebody says
208/// `BACKUP CLEANUP`. Writable, since a backup taken by a script that then died
209/// is exactly the thing this is for and setting it afterwards has to work.
210const SEALED_TTL: &str = "backup-sealed-ttl";
211
212/// The file the users are read from and written back to, empty when there is
213/// none.
214///
215/// Immutable, which is Redis's rule for it and is the right one: an operator who
216/// could point a running server at a different ACL file would have a way of
217/// changing who may reach it that is invisible to everything watching the file
218/// it was started with.
219const ACLFILE: &str = "aclfile";
220
221/// How many refusals `ACL LOG` keeps, and nought keeps none.
222///
223/// Writable, because the reason to change it is that something is happening
224/// right now and the log is either too short to see it or long enough to be in
225/// the way.
226const ACLLOG_MAX_LEN: &str = "acllog-max-len";
227
228/// Whether a new selector starts out allowed every channel.
229///
230/// Writable, and writing it changes nothing that already exists: it is read at
231/// the moment a selector is made and never looked at again. Redis 6 behaved as
232/// `allchannels` and Redis 7 changed the default to `resetchannels`, which is
233/// what this setting is for, and yo starts where Redis 7 did.
234const ACL_PUBSUB_DEFAULT: &str = "acl-pubsub-default";
235
236/// The two words `acl-pubsub-default` is allowed to be, in Redis's order.
237const CHANNEL_DEFAULTS: [&str; 2] = ["allchannels", "resetchannels"];
238
239/// Whether a client's write is refused while this server follows a master.
240///
241/// Writable, and on by default, which is Redis's default and is the only safe
242/// one: a write that lands on a replica is a write the master never hears about
243/// and that the next full resync throws away. Turning it off is a real thing to
244/// do and is what a cache in front of a slow master wants.
245///
246/// The `slave` spelling is the name this had before Redis renamed it and it
247/// still answers to both, so this does too, the same way the size ladder answers
248/// to `ziplist`. Two names, one setting.
249const REPLICA_READ_ONLY: [&str; 2] = ["replica-read-only", "slave-read-only"];
250
251/// The password and user the link to a master authenticates with.
252///
253/// Writable and both empty by default, which is a master that asks for nothing.
254/// A user without a password is not a thing to send, so an empty `masterauth`
255/// means the link sends no `AUTH` at all whatever `masteruser` says. They read
256/// back in the clear for the same reason `requirepass` does.
257const MASTERAUTH: &str = "masterauth";
258/// The user half of [`MASTERAUTH`], for a master with an ACL rather than a
259/// password.
260const MASTERUSER: &str = "masteruser";
261
262/// The two words a yes or no setting is allowed to be.
263const BOOLS: [&str; 2] = ["yes", "no"];
264
265/// The two cluster settings that really move, both of them a yes or a no.
266///
267/// `cluster-require-full-coverage` decides whether a node with a hole somewhere
268/// in the cluster refuses everything or only the keys in the hole, and
269/// `cluster-allow-reads-when-down` decides whether a node that has decided the
270/// cluster is down still answers reads. Both take effect on the next command,
271/// which is what an operator digging a cluster out of a hole wants.
272const CLUSTER_COVERAGE: [&str; 2] = [
273    "cluster-require-full-coverage",
274    "cluster-allow-reads-when-down",
275];
276
277/// The four settings a slot migration runs under.
278///
279/// Each row is the name, which knob it writes, whether it reads a byte count or
280/// a plain number, the smallest value it takes, and whether it is hidden. They
281/// are here as a group rather than one at a time because they are only ever
282/// read together, by an operator working out why a move is taking as long as it
283/// is, and a group with a hole in it is worse than no group at all.
284///
285/// Hidden is Redis's word for a setting that a pattern does not find and an
286/// exact name does. It is what upstream does with the two of these that are
287/// backstops rather than things to tune, and copying it matters because a tool
288/// that dumps `CONFIG GET *` and writes the result back out as a config file
289/// would otherwise carry them along.
290const MIGRATION: [(&str, Migration, bool, i64, bool); 4] = [
291    (
292        "cluster-slot-migration-handoff-max-lag-bytes",
293        Migration::Lag,
294        true,
295        0,
296        false,
297    ),
298    (
299        "cluster-slot-migration-write-pause-timeout",
300        Migration::Pause,
301        false,
302        0,
303        false,
304    ),
305    (
306        "cluster-slot-migration-sync-buffer-drain-timeout",
307        Migration::Drain,
308        false,
309        0,
310        true,
311    ),
312    (
313        "cluster-slot-migration-max-archived-tasks",
314        Migration::Archived,
315        false,
316        1,
317        true,
318    ),
319];
320
321/// Whether this server is a cluster node, which is fixed for the life of the
322/// process and is Redis's rule.
323///
324/// A server that could be turned into a cluster node while it was holding keys
325/// would be a server whose keys were suddenly in slots it does not own, so
326/// `CONFIG SET` refuses it and the only way to set it is at startup.
327const CLUSTER_ENABLED: &str = "cluster-enabled";
328
329/// Where a cluster node writes its table, which it was given at startup.
330const CLUSTER_CONFIG_FILE: &str = "cluster-config-file";
331
332/// Which classes of keyspace change are published, and on which two channels.
333///
334/// On its own for a fifth reason: it is the only setting whose value is neither
335/// a number nor one of a fixed list of words, but a set of characters that reads
336/// back in a different spelling from the one it was written in. `CONFIG SET
337/// notify-keyspace-events KEA` reads back as `AKE`. See the `notify` module for
338/// what each character means and why the order is what it is.
339const NOTIFY: &str = "notify-keyspace-events";
340
341/// Read a byte count the way `CONFIG SET maxmemory` reads one.
342///
343/// This is Redis's `memtoull`. Digits, then an optional unit that is not case
344/// sensitive: nothing or `b` is bytes, `k` is a thousand and `kb` is a kibibyte,
345/// and the same pairing again for `m` and `g`. The two spellings meaning
346/// different numbers is a trap and it is Redis's trap, so it is repeated here
347/// rather than tidied up.
348///
349/// A unit that overflows clamps rather than failing, which is upstream's
350/// `ULLONG_MAX` arm. There is no sign: a leading minus is refused before the
351/// digits are read, so `maxmemory -1` is not a very large number.
352///
353/// Public because `yodb serve` takes the same limits on the command line that
354/// `CONFIG SET` takes at runtime, and a server that accepts `100mb` from one and
355/// not the other, or reads it as a different number, is a server that gets
356/// misconfigured. One parser, one answer.
357#[must_use]
358pub fn parse_memory(value: &[u8]) -> Option<u64> {
359    let split = value
360        .iter()
361        .position(|b| !b.is_ascii_digit())
362        .unwrap_or(value.len());
363    let (digits, unit) = value.split_at(split);
364    if digits.is_empty() {
365        return None;
366    }
367    let mul: u64 = match unit {
368        [] => 1,
369        u if u.eq_ignore_ascii_case(b"b") => 1,
370        u if u.eq_ignore_ascii_case(b"k") => 1000,
371        u if u.eq_ignore_ascii_case(b"kb") => 1024,
372        u if u.eq_ignore_ascii_case(b"m") => 1000 * 1000,
373        u if u.eq_ignore_ascii_case(b"mb") => 1024 * 1024,
374        u if u.eq_ignore_ascii_case(b"g") => 1000 * 1000 * 1000,
375        u if u.eq_ignore_ascii_case(b"gb") => 1024 * 1024 * 1024,
376        _ => return None,
377    };
378    let mut n: u64 = 0;
379    for d in digits {
380        n = n.saturating_mul(10).saturating_add(u64::from(d - b'0'));
381    }
382    Some(n.saturating_mul(mul))
383}
384
385/// Every policy name, joined the way `CONFIG SET` lists them when it refuses one.
386///
387/// This is a formatter and not a string because the error path should not touch
388/// the allocator, and it walks [`Policy::ALL`] rather than spelling the ten names
389/// out again so the two cannot drift apart. The order is the order in Redis's own
390/// enum table, which is the whole reason `Policy::ALL` is written down.
391struct PolicyNames;
392
393impl core::fmt::Display for PolicyNames {
394    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
395        for (at, policy) in Policy::ALL.iter().enumerate() {
396            if at > 0 {
397                f.write_str(", ")?;
398            }
399            f.write_str(policy.name())?;
400        }
401        Ok(())
402    }
403}
404
405/// Run one connection or server command.
406pub(super) fn execute(
407    server: &Server,
408    session: &mut Session,
409    spec: &Spec,
410    args: Args<'_>,
411    out: &mut Out,
412) -> Result<Flow> {
413    match spec.name {
414        // The arity in the table is a minimum of one, and a real server then
415        // refuses a second argument as a wrong number of them.
416        "ping" => {
417            if args.len() > 2 {
418                return Err(args::wrong_arity("ping"));
419            }
420            // A RESP2 connection in subscribe mode is answered a two element
421            // array with `pong` in front, so that everything reaching a
422            // subscribed client on RESP2 has the same shape. The one place a
423            // command in this file cares what the connection has subscribed to.
424            if super::pubsub::ping(session, args, out) {
425                return Ok(Flow::Continue);
426            }
427            if args.len() == 2 {
428                out.bulk(args.get(1));
429            } else {
430                out.simple(b"PONG");
431            }
432        }
433        "echo" => out.bulk(args.get(1)),
434        "acl" => acl::execute(server, session, args, out)?,
435        "auth" => auth::execute(server, session, args, out)?,
436        "debug" => debug::execute(server, session, args, out)?,
437        "memory" => super::memory::execute(server, session, args, out)?,
438        "replconf" => super::repl::replconf(server, session, args, out)?,
439        "psync" | "sync" => super::repl::psync(server, session, args, out)?,
440        "replicaof" | "slaveof" => super::follow::replicaof(server, args, out)?,
441        "failover" => super::failover::execute(server, args, out)?,
442        "cluster" => super::cluster::execute(server, session, args, out)?,
443        // A top level command rather than a `CLUSTER` subcommand, and only ever
444        // sent by a node that has handed slots over to whoever is following it.
445        "trimslots" => super::cluster::trimslots(server, args, out)?,
446        // The three connection commands cluster mode adds. `ASKING` is the one
447        // that does anything: it says the next command is allowed into a slot
448        // this node is receiving and does not own yet, which is how a client
449        // follows an `ASK` it was told.
450        //
451        // `READONLY` and `READWRITE` say whether this connection will take
452        // reads from a replica rather than being redirected to the master, and
453        // on a node that is nobody's replica, which is every node here until the
454        // bus is in, both of them are an `OK` and nothing else. That is what a
455        // real master answers too, so a client library that sends `READONLY` on
456        // connect gets the same answer from both.
457        "asking" => {
458            if !server.cluster_enabled() {
459                return Err(super::cluster::disabled());
460            }
461            session.ask_next();
462            out.ok();
463        }
464        "readonly" | "readwrite" => {
465            if !server.cluster_enabled() {
466                return Err(super::cluster::disabled());
467            }
468            out.ok();
469        }
470        "hello" => hello(server, session, args, out)?,
471        "select" => {
472            // A cluster has one database and the slots are how it is cut up, so
473            // moving to another one would be moving to a database no slot points
474            // at. Database nought is still allowed, because a client library
475            // that sends SELECT 0 on connect is asking for where it already is.
476            let n = args.int(1)?;
477            if server.cluster_enabled() && n != 0 {
478                return Err(Error::new(
479                    Code::Invalid,
480                    "SELECT is not allowed in cluster mode",
481                ));
482            }
483            let ok = usize::try_from(n).is_ok_and(|n| n < DATABASES);
484            if !ok {
485                return Err(Error::new(Code::Invalid, "DB index is out of range"));
486            }
487            session.db = n as usize;
488            out.ok();
489        }
490        "reset" => {
491            // Everything a connection carries goes back to what it was when it
492            // was opened, and that includes the protocol: a connection that
493            // said `HELLO 3` is speaking RESP2 again after this.
494            //
495            // The transaction and the watches go first because letting go of a
496            // watch is a change to the server and not to the connection, so
497            // clearing the list here without saying so would leave rows on the
498            // server that nobody is watching. `RESET` inside `MULTI` answers
499            // `+RESET` and leaves no transaction, which is why it is one of the
500            // six commands a transaction does not queue.
501            // The subscriptions go with them, and for the same reason: a
502            // subscription is a row on the server naming this connection, so
503            // clearing the connection's list alone would leave the server
504            // delivering into a slot that is not listening any more.
505            // And the monitor with them, which is the one way out of monitor
506            // mode short of closing the socket. `RESET` still answers `+RESET`
507            // on a connection that was one, because the reply belongs to the
508            // client the connection has just gone back to being.
509            multi::release(server, session);
510            super::pubsub::release(server, session);
511            if session.monitoring() {
512                server.watch_no_more(session.row());
513            }
514            session.reset();
515            // Including the password, which is what `RESET` means by putting
516            // the connection back the way it was accepted: on a server with a
517            // password the client has to send `AUTH` again, and on a server
518            // without one it never had to.
519            session.admit(!server.guarded());
520            out.set_proto(Proto::Resp2);
521            out.simple(b"RESET");
522        }
523        // The reply goes out before the socket closes, which is why this is a
524        // flow answer and not something the body does to the connection.
525        "quit" => {
526            out.ok();
527            return Ok(Flow::Close);
528        }
529        // Every command on the server, from here on, on this connection. The
530        // reply is `OK` once and nothing after it, and a connection that sends
531        // it twice is answered nothing at all the second time, which is a real
532        // server's behaviour and not an oversight of one.
533        "monitor" => {
534            // A transaction replaying this has been promised a reply for every
535            // command it queued, and a connection that has turned into a feed
536            // cannot give one. A real server refuses it in the same words.
537            if session.running() {
538                return Err(Error::new(
539                    Code::Invalid,
540                    "MONITOR isn't allowed for DENY BLOCKING client",
541                ));
542            }
543            if server.watch_all(session.row()) {
544                out.ok();
545            }
546        }
547        "client" => return super::client::execute(server, session, spec, args, out),
548        "command" => command(args, out)?,
549        "config" => config(server, args, out)?,
550        "info" => info(server, args, out),
551        // A key that is past its deadline and has not been read since is still
552        // counted, which is what Redis does too: `DBSIZE` is the size of the
553        // dictionary and not a walk over it. Redis has an active expiry cycle
554        // that takes those keys out within a tick or so and we do not yet, so
555        // the two servers disagree for as long as a dead key sits unread. That
556        // gap closes with the maintenance slice rather than with a count here,
557        // because a count here would be O(N) on a command that is O(1)
558        // everywhere else.
559        "dbsize" => out.int(server.dbs[session.db].len() as i64),
560        "flushall" => {
561            flush_mode(args)?;
562            for db in &server.dbs {
563                db.clear();
564            }
565            server.search.lock().clear();
566            server.cursors.lock().wipe();
567            out.ok();
568        }
569        // The search indexes go too, and they go whichever database this is.
570        // An index that only ever followed keys on database zero is dropped by
571        // a `FLUSHDB` on database nine, which is measured against a real server
572        // rather than reasoned about: the module hangs its callback on the
573        // flush event without looking at which database flushed.
574        "flushdb" => {
575            flush_mode(args)?;
576            server.dbs[session.db].clear();
577            server.search.lock().clear();
578            server.cursors.lock().wipe();
579            out.ok();
580        }
581        // Two databases change places and no key moves. What is in the stripes
582        // is exchanged and the databases stay where they are, so this costs two
583        // pointer sized writes per stripe whatever is in either of them, which
584        // is what makes `SWAPDB` fast and dangerous at the same time.
585        //
586        // No connection is told. A client on database zero is still on database
587        // zero and is now looking at what used to be database one, which is the
588        // whole point of the command and is why Redis calls it dangerous. A
589        // client parked in `BLPOP` remembers the database index it blocked on
590        // and not the database, so it wakes up against the swapped in one, which
591        // is Redis's behaviour and falls out of the index being what is stored.
592        "swapdb" => {
593            if server.cluster_enabled() {
594                return Err(Error::new(
595                    Code::Invalid,
596                    "SWAPDB is not allowed in cluster mode",
597                ));
598            }
599            let first = db_index(args.get(1), "invalid first DB index")?;
600            let second = db_index(args.get(2), "invalid second DB index")?;
601            server.striped(first).swap_with(server.striped(second));
602            out.ok();
603        }
604        "time" => time(out),
605        // The four commands about writing the dataset to a file and the one
606        // about who this server is, all in the `persist` module because a client
607        // asks them together.
608        "save" | "bgsave" | "bgrewriteaof" | "lastsave" | "role" => {
609            persist::execute(server, session, spec, args, out)?;
610        }
611        "backup" => backup::execute(server, args, out)?,
612        "shutdown" => return shutdown(server, args),
613        _ => return Err(args::unknown_command(args)),
614    }
615    Ok(Flow::Continue)
616}
617
618/// `TIME`, which is two bulk strings and not one integer.
619///
620/// Seconds first and then microseconds within that second, both written out as
621/// decimal text, which is a shape nobody would choose today and is the shape
622/// every client library parses.
623///
624/// It reads the wall clock rather than the coarse clock the keyspace uses. The
625/// coarse one is a cached millisecond that a background tick refreshes, which is
626/// the right trade for deciding whether a key has expired and the wrong one for
627/// a command whose entire job is to say what time it is. A client that calls
628/// `TIME` twice in a row and gets the same microsecond has been lied to.
629fn time(out: &mut Out) {
630    let now = SystemTime::now()
631        .duration_since(UNIX_EPOCH)
632        .unwrap_or_default();
633    out.array(2);
634    out.bulk(now.as_secs().to_string().as_bytes());
635    out.bulk(now.subsec_micros().to_string().as_bytes());
636}
637
638// ---------------------------------------------------------------- SHUTDOWN
639
640/// `SHUTDOWN [NOSAVE | SAVE] [NOW] [FORCE] [ABORT]`.
641///
642/// On success this writes nothing at all and the connection closes under the
643/// client, which is what a server that has stopped looks like from the outside
644/// and is what every client library already expects. There is no `OK`, because
645/// an `OK` would be a promise made by a process that is about to not exist.
646///
647/// `SAVE` writes the file [`persist`] writes, and it is the only word here that
648/// does anything. `NOSAVE` is the default rather than an instruction, which is
649/// the same answer `save` gets from `CONFIG GET`: this server has no save points
650/// and never will, because what durability there is belongs to the file
651/// underneath and is already on disk by the time a command returns. So there is
652/// nothing for `NOSAVE` to skip and the file `SAVE` asks for is an export
653/// somebody wants a copy of on the way down. `NOW` and `FORCE` are about not
654/// waiting for replicas and about going anyway when a save failed, and neither
655/// has anything to wait for or to fail here.
656///
657/// # Errors
658///
659/// [`Code::Invalid`] for a word that is not one of the five, for `SAVE` and
660/// `NOSAVE` in the same call, and for `ABORT` alongside any other flag, all of
661/// which is what 8.10.1 says. `ABORT` on its own gets Redis's message for a
662/// cancel with nothing to cancel, and here that is not a state that can be
663/// reached rather than one that happens to be empty: a shutdown is decided and
664/// done inside one turn of the loop, so there is never a window in which one is
665/// in progress and a second client could call it off.
666fn shutdown(server: &Server, args: Args<'_>) -> Result<Flow> {
667    let (mut save, mut nosave, mut abort, mut other) = (false, false, false, false);
668    for at in 1..args.len() {
669        let arg = args.get(at);
670        match () {
671            () if is(arg, b"save") => save = true,
672            () if is(arg, b"nosave") => nosave = true,
673            () if is(arg, b"abort") => abort = true,
674            () if is(arg, b"now") || is(arg, b"force") => other = true,
675            () => return Err(args::syntax()),
676        }
677    }
678    // Repeating one is fine and contradicting yourself is not, and `ABORT` says
679    // to do nothing so it cannot be combined with a word about how to do it.
680    if (save && nosave) || (abort && (save || nosave || other)) {
681        return Err(args::syntax());
682    }
683    if abort {
684        return Err(Error::new(Code::Invalid, "No shutdown in progress."));
685    }
686    if save {
687        persist::on_shutdown(server);
688    }
689    server.stop();
690    // Closing is what stops anything the client pipelined behind this from
691    // being answered by a server that is on its way out.
692    Ok(Flow::Close)
693}
694
695// ------------------------------------------------------------------- FLUSH
696
697/// Check the optional `ASYNC` or `SYNC` on `FLUSHALL` and `FLUSHDB`.
698///
699/// Both are accepted and neither changes anything. On a real server the choice
700/// is whether the freeing happens on the connection's thread or on the lazy
701/// free thread, and either way the keyspace is empty before the `OK` goes out.
702/// That is the whole of what a client can observe, and it is the same here,
703/// so taking the word and ignoring it is answering the question rather than
704/// pretending to.
705///
706/// # Errors
707///
708/// [`Code::Invalid`] for a third argument, or for a second that is neither
709/// word, which is what Redis says about both.
710fn flush_mode(args: Args<'_>) -> Result<()> {
711    if args.len() == 1 {
712        return Ok(());
713    }
714    if args.len() > 2 || !(is(args.get(1), b"async") || is(args.get(1), b"sync")) {
715        return Err(args::syntax());
716    }
717    Ok(())
718}
719
720/// One of `SWAPDB`'s two database indexes, with Redis's two different
721/// complaints about it.
722///
723/// A word that is not a number, or a number too big to be a database index on a
724/// server that stores the index in a C `int`, gets the caller's message, which
725/// says which of the two arguments was wrong. A number that is a plausible index
726/// and is not one of ours gets the same out of range message `SELECT` gives. The
727/// split looks arbitrary and it is Redis's, and the reason for it is that the
728/// first check happens while reading the argument and the second happens inside
729/// the swap, so only the first one knows which argument it was looking at.
730fn db_index(arg: &[u8], bad: &'static str) -> Result<usize> {
731    let n = parse_i64(arg)
732        .filter(|n| i32::try_from(*n).is_ok())
733        .ok_or_else(|| Error::new(Code::Invalid, bad))?;
734    usize::try_from(n)
735        .ok()
736        .filter(|n| *n < DATABASES)
737        .ok_or_else(|| Error::new(Code::Invalid, "DB index is out of range"))
738}
739
740// ------------------------------------------------------------------- HELLO
741
742/// `HELLO [protover [AUTH username password] [SETNAME name]]`.
743///
744/// The order of the three things that can go wrong here is the reference's and
745/// is worth writing down, because it is not the order they appear in. The
746/// protocol version is read and refused first, so `HELLO 9 AUTH default right`
747/// on a connection that has not authenticated is a `NOPROTO` and leaves the
748/// connection unauthenticated. The `AUTH` option is applied next, so a wrong
749/// password is a `WRONGPASS` and the protocol stays where it was. Only then does
750/// the connection have to be authenticated at all, which is what makes a bare
751/// `HELLO` on a server with a password a `NOAUTH` rather than a greeting.
752fn hello(server: &Server, session: &mut Session, args: Args<'_>, out: &mut Out) -> Result<()> {
753    // The version this call agreed on, if it named one. Held rather than applied
754    // where it is read, because the reply buffer must not change protocol until
755    // the password below has been asked for and answered.
756    let mut agreed = None;
757    if args.len() > 1 {
758        let v = parse_i64(args.get(1)).ok_or_else(|| {
759            Error::new(
760                Code::Invalid,
761                "Protocol version is not an integer or out of range",
762            )
763        })?;
764        let Some(proto) = Proto::from_version(v) else {
765            // `NOPROTO` rather than `ERR`, and it is the one error in this file
766            // written straight into the buffer: the prefix is part of what the
767            // client branches on, and it is the only place in the engine that
768            // needs this one.
769            out.error(b"NOPROTO unsupported protocol version");
770            return Ok(());
771        };
772        let mut i = 2;
773        while i < args.len() {
774            let o = args.get(i);
775            if is(o, b"AUTH") && i + 2 < args.len() {
776                if !acl::authenticate(server, session, args.get(i + 1), args.get(i + 2), args, out)
777                {
778                    out.error(b"WRONGPASS invalid username-password pair or user is disabled.");
779                    return Ok(());
780                }
781                i += 3;
782            } else if is(o, b"SETNAME") && i + 1 < args.len() {
783                session.set_name(args.get(i + 1));
784                i += 2;
785            } else {
786                return Err(yo_alloc::allow(|| {
787                    Error::fmt(
788                        Code::Invalid,
789                        format_args!(
790                            "Syntax error in HELLO option '{}'",
791                            String::from_utf8_lossy(o)
792                        ),
793                    )
794                }));
795            }
796        }
797        agreed = Some(proto);
798    }
799
800    if server.guarded() && !session.authenticated() {
801        // Its own sentence rather than the one every other command gets, because
802        // a client that speaks RESP3 has to send `HELLO` before it can send
803        // `AUTH` and would otherwise be told to do the thing it is doing.
804        out.error(auth::HELLO_NOAUTH.as_bytes());
805        return Ok(());
806    }
807    // The reply is written in the protocol that was just agreed, not the one the
808    // request arrived in.
809    if let Some(proto) = agreed {
810        out.set_proto(proto);
811    }
812
813    let proto = out.proto().version();
814    out.map(7);
815    out.bulk(b"server");
816    out.bulk(REPORTED_SERVER.as_bytes());
817    out.bulk(b"version");
818    out.bulk(REPORTED_VERSION.as_bytes());
819    out.bulk(b"proto");
820    out.int(proto);
821    out.bulk(b"id");
822    out.int(session.id as i64);
823    out.bulk(b"mode");
824    out.bulk(b"standalone");
825    out.bulk(b"role");
826    out.bulk(b"master");
827    out.bulk(b"modules");
828    out.array(0);
829    Ok(())
830}
831
832// ----------------------------------------------------------------- COMMAND
833
834/// `COMMAND [COUNT|LIST|INFO|DOCS|GETKEYS|HELP]`.
835fn command(args: Args<'_>, out: &mut Out) -> Result<()> {
836    if args.len() == 1 {
837        out.array(table::COMMANDS.len());
838        for spec in table::COMMANDS {
839            write_spec(out, spec);
840        }
841        return Ok(());
842    }
843    let sub = args.get(1);
844    if is(sub, b"COUNT") {
845        // Nothing to say about, so anything after it is the caller meaning
846        // something else. The reference gives these two an exact arity where the
847        // rest of the container takes a list, and being loose here would let a
848        // client's typo answer as though it had asked the question it meant to.
849        if args.len() != 2 {
850            return Err(args::wrong_arity_sub("command", "count"));
851        }
852        out.int(table::COMMANDS.len() as i64);
853    } else if is(sub, b"INFO") {
854        if args.len() == 2 {
855            out.array(table::COMMANDS.len());
856            for spec in table::COMMANDS {
857                write_spec(out, spec);
858            }
859        } else {
860            out.array(args.len() - 2);
861            for i in 2..args.len() {
862                match table::lookup(args.get(i)) {
863                    Some(spec) => write_spec(out, spec),
864                    // A name nobody has heard of is a null in the list rather
865                    // than an error, so one bad name in a batch does not cost
866                    // the client the other answers. It is the plain null and
867                    // not the array one, which on RESP2 is the difference
868                    // between `$-1` and `*-1` and is what a real server sends.
869                    None => out.nil(),
870                }
871            }
872        }
873    } else if is(sub, b"LIST") {
874        list(args, out)?;
875    } else if is(sub, b"DOCS") {
876        docs(args, out);
877    } else if is(sub, b"GETKEYS") {
878        getkeys(args, out, false)?;
879    } else if is(sub, b"GETKEYSANDFLAGS") {
880        getkeys(args, out, true)?;
881    } else if is(sub, b"HELP") {
882        if args.len() != 2 {
883            return Err(args::wrong_arity_sub("command", "help"));
884        }
885        help(out, COMMAND_HELP);
886    } else {
887        return Err(args::unknown_subcommand(sub, "COMMAND"));
888    }
889    Ok(())
890}
891
892/// `COMMAND LIST [FILTERBY MODULE m|ACLCAT c|PATTERN p]`.
893fn list(args: Args<'_>, out: &mut Out) -> Result<()> {
894    if args.len() == 2 {
895        out.array(table::COMMANDS.len());
896        for spec in table::COMMANDS {
897            out.bulk(spec.name.as_bytes());
898        }
899        return Ok(());
900    }
901    if args.len() != 5 || !is(args.get(2), b"FILTERBY") {
902        return Err(args::syntax());
903    }
904    let (how, what) = (args.get(3), args.get(4));
905    let keep = |spec: &Spec| {
906        if is(how, b"MODULE") {
907            // Nothing here came from a module, so every filter by one is empty.
908            false
909        } else if is(how, b"ACLCAT") {
910            spec.acl
911                .iter()
912                .any(|c| c.len() == what.len() + 1 && c.as_bytes()[1..].eq_ignore_ascii_case(what))
913        } else {
914            glob::matches(what, spec.name.as_bytes())
915        }
916    };
917    if !is(how, b"MODULE") && !is(how, b"ACLCAT") && !is(how, b"PATTERN") {
918        return Err(args::syntax());
919    }
920    out.array(table::COMMANDS.iter().filter(|s| keep(s)).count());
921    for spec in table::COMMANDS.iter().filter(|s| keep(s)) {
922        out.bulk(spec.name.as_bytes());
923    }
924    Ok(())
925}
926
927/// `COMMAND DOCS [name ...]`.
928///
929/// The arguments field a real server sends is left out. It describes the shape
930/// of every option of every command in a form nothing but `redis-cli`'s hinting
931/// reads, and getting it wrong would be worse than not sending it, since a
932/// client that finds the field trusts it.
933fn docs(args: Args<'_>, out: &mut Out) {
934    if args.len() == 2 {
935        out.map(table::COMMANDS.len());
936        for spec in table::COMMANDS {
937            write_docs(out, spec);
938        }
939        return;
940    }
941    let found = (2..args.len())
942        .filter(|&i| table::lookup(args.get(i)).is_some())
943        .count();
944    out.map(found);
945    for i in 2..args.len() {
946        if let Some(spec) = table::lookup(args.get(i)) {
947            write_docs(out, spec);
948        }
949    }
950}
951
952/// One command's documentation, as the name and then the map about it.
953fn write_docs(out: &mut Out, spec: &Spec) {
954    out.bulk(spec.name.as_bytes());
955    out.map(4);
956    out.bulk(b"summary");
957    out.bulk(spec.summary.as_bytes());
958    out.bulk(b"since");
959    out.bulk(spec.since.as_bytes());
960    out.bulk(b"group");
961    out.bulk(spec.group.as_bytes());
962    out.bulk(b"complexity");
963    out.bulk(spec.complexity.as_bytes());
964}
965
966/// `COMMAND GETKEYS <full command>` and `COMMAND GETKEYSANDFLAGS <full command>`.
967///
968/// This is how a cluster aware client routes a command it does not have a rule
969/// for, so a wrong answer here is a client that sends a write to the wrong
970/// node. The answer comes off the key specs, which is the same place the ACL
971/// reads, so the two can never drift apart.
972///
973/// The three errors are the reference's own and they mean different things. A
974/// name nobody registered is one, a command that never takes a key whatever it
975/// is sent is another, and a command that does take keys and was handed
976/// arguments the specs cannot resolve is the third. Only the last is about what
977/// was actually typed.
978fn getkeys(args: Args<'_>, out: &mut Out, flags: bool) -> Result<()> {
979    let sub = if flags { "getkeysandflags" } else { "getkeys" };
980    if args.len() < 3 {
981        return Err(args::wrong_arity_sub("command", sub));
982    }
983    let inner = args.get(2);
984    let spec = table::lookup(inner)
985        .ok_or_else(|| Error::new(Code::Unsupported, "Invalid command specified"))?;
986    if !keyspec::takes_keys(spec, args, 2) {
987        return Err(Error::new(
988            Code::Invalid,
989            "The command has no key arguments",
990        ));
991    }
992    let argc = args.len() - 2;
993    if !table::arity_ok(spec, argc) {
994        return Err(Error::new(
995            Code::Invalid,
996            "Invalid number of arguments specified for command",
997        ));
998    }
999    // Three specs at most a command and one run each, so the answer is worked
1000    // out into a fixed array rather than a list that grows. A run is a first
1001    // argument and a count, so a hundred keys behind a count is still one of
1002    // these.
1003    let mut runs = [None; 4];
1004    let mut at = 0;
1005    let whole = keyspec::find(spec, args, 2, &mut |run| {
1006        if at < runs.len() {
1007            runs[at] = Some(run);
1008            at += 1;
1009        }
1010    });
1011    let found: usize = runs.iter().flatten().map(|r| r.count).sum();
1012    // A command that resolves to nothing is a syntax error, unless it is one of
1013    // the six that may honestly have no keys, which is the script family: `EVAL
1014    // body 0` is an ordinary thing to write and answers an empty list.
1015    if (!whole || found == 0) && !spec.flags.contains(&"no_mandatory_keys") {
1016        return Err(Error::new(
1017            Code::Invalid,
1018            "Invalid arguments specified for command",
1019        ));
1020    }
1021    let found = if whole { found } else { 0 };
1022    out.array(found);
1023    if found == 0 {
1024        return Ok(());
1025    }
1026    for run in runs.iter().flatten() {
1027        for i in 0..run.count {
1028            let key = args.get(run.first + i * run.step);
1029            if flags {
1030                out.array(2);
1031                out.bulk(key);
1032                out.set(run.flags.len());
1033                for f in run.flags {
1034                    out.simple(f.as_bytes());
1035                }
1036            } else {
1037                out.bulk(key);
1038            }
1039        }
1040    }
1041    Ok(())
1042}
1043
1044/// One command, in the ten field shape `COMMAND INFO` has had since 7.0.
1045///
1046/// The tips and the subcommands are still empty, which is what is left of
1047/// divergence D-13. The key specs are not: they say where the keys are for
1048/// everything in this table, including the commands the triple above them
1049/// cannot describe.
1050///
1051/// Five of the ten fields are sets rather than arrays, which only shows on
1052/// RESP3 and shows there on every command. A set is what the reference sends
1053/// for all five, and it is the honest type for them: nothing in a flag list or
1054/// an acl category list is ordered or repeated.
1055fn write_spec(out: &mut Out, spec: &Spec) {
1056    out.array(10);
1057    out.bulk(spec.name.as_bytes());
1058    out.int(i64::from(spec.arity));
1059    out.set(spec.flags.len());
1060    for f in spec.flags {
1061        out.simple(f.as_bytes());
1062    }
1063    out.int(i64::from(spec.first_key));
1064    out.int(i64::from(spec.last_key));
1065    out.int(i64::from(spec.step));
1066    out.set(spec.acl.len());
1067    for a in spec.acl {
1068        out.simple(a.as_bytes());
1069    }
1070    out.set(0);
1071    out.set(spec.keys.len());
1072    for key in spec.keys {
1073        write_key_spec(out, key);
1074    }
1075    out.set(0);
1076}
1077
1078/// One key spec, as the map `COMMAND INFO` reports it.
1079///
1080/// The notes come first and only when there are any, which is why the map is
1081/// three long or four rather than always four.
1082fn write_key_spec(out: &mut Out, key: &KeySpec) {
1083    out.map(if key.notes.is_empty() { 3 } else { 4 });
1084    if !key.notes.is_empty() {
1085        out.bulk(b"notes");
1086        out.bulk(key.notes.as_bytes());
1087    }
1088    out.bulk(b"flags");
1089    out.set(key.flags.len());
1090    for f in key.flags {
1091        out.simple(f.as_bytes());
1092    }
1093    out.bulk(b"begin_search");
1094    out.map(2);
1095    out.bulk(b"type");
1096    match key.begin {
1097        Begin::At(index) => {
1098            out.bulk(b"index");
1099            out.bulk(b"spec");
1100            out.map(1);
1101            out.bulk(b"index");
1102            out.int(i64::from(index));
1103        }
1104        Begin::After(word, from) => {
1105            out.bulk(b"keyword");
1106            out.bulk(b"spec");
1107            out.map(2);
1108            out.bulk(b"keyword");
1109            out.bulk(word);
1110            out.bulk(b"startfrom");
1111            out.int(i64::from(from));
1112        }
1113        Begin::Unknown => {
1114            out.bulk(b"unknown");
1115            out.bulk(b"spec");
1116            out.map(0);
1117        }
1118    }
1119    out.bulk(b"find_keys");
1120    out.map(2);
1121    out.bulk(b"type");
1122    match key.find {
1123        Find::Range { last, step, limit } => {
1124            out.bulk(b"range");
1125            out.bulk(b"spec");
1126            out.map(3);
1127            out.bulk(b"lastkey");
1128            out.int(i64::from(last));
1129            out.bulk(b"keystep");
1130            out.int(i64::from(step));
1131            out.bulk(b"limit");
1132            out.int(i64::from(limit));
1133        }
1134        Find::Counted { count, first, step } => {
1135            out.bulk(b"keynum");
1136            out.bulk(b"spec");
1137            out.map(3);
1138            out.bulk(b"keynumidx");
1139            out.int(i64::from(count));
1140            out.bulk(b"firstkey");
1141            out.int(i64::from(first));
1142            out.bulk(b"keystep");
1143            out.int(i64::from(step));
1144        }
1145        Find::Unknown => {
1146            out.bulk(b"unknown");
1147            out.bulk(b"spec");
1148            out.map(0);
1149        }
1150    }
1151}
1152
1153// ------------------------------------------------------------------ CONFIG
1154
1155/// What a ladder setting is set to now.
1156fn read_knob(db: &Keyspace, knob: Knob) -> usize {
1157    match knob {
1158        Knob::SetIntsetEntries => db.limits().max_intset_entries,
1159        Knob::SetListpackEntries => db.limits().max_listpack_entries,
1160        Knob::SetListpackValue => db.limits().max_listpack_value,
1161        Knob::HashListpackEntries => db.hash_limits().max_listpack_entries,
1162        Knob::HashListpackValue => db.hash_limits().max_listpack_value,
1163        Knob::MaxmemorySamples => db.samples(),
1164        Knob::LfuLogFactor => db.lfu().log_factor as usize,
1165        Knob::LfuDecayTime => db.lfu().decay_minutes as usize,
1166    }
1167}
1168
1169/// Move one ladder setting on one database.
1170fn write_knob(db: &mut Keyspace, knob: Knob, n: usize) {
1171    let mut set = *db.limits();
1172    let mut hash = *db.hash_limits();
1173    let mut lfu = db.lfu();
1174    match knob {
1175        Knob::SetIntsetEntries => set.max_intset_entries = n,
1176        Knob::SetListpackEntries => set.max_listpack_entries = n,
1177        Knob::SetListpackValue => set.max_listpack_value = n,
1178        Knob::HashListpackEntries => hash.max_listpack_entries = n,
1179        Knob::HashListpackValue => hash.max_listpack_value = n,
1180        Knob::MaxmemorySamples => db.set_samples(n),
1181        // Saturating rather than wrapping, because these two are read as `u32`
1182        // and a client is free to send a number that does not fit. Redis clamps
1183        // `lfu-log-factor` and `lfu-decay-time` to the same width.
1184        Knob::LfuLogFactor => lfu.log_factor = u32::try_from(n).unwrap_or(u32::MAX),
1185        Knob::LfuDecayTime => lfu.decay_minutes = u32::try_from(n).unwrap_or(u32::MAX),
1186    }
1187    db.set_limits(set);
1188    db.set_hash_limits(hash);
1189    db.set_lfu(lfu);
1190}
1191
1192/// The two things a real server says about a number it will not take.
1193///
1194/// Both name the setting the client typed and not the one it is an alias for,
1195/// so `hash-max-ziplist-entries` comes back saying `hash-max-ziplist-entries`.
1196/// A value past the range of an `i64` is the parse complaint and not the range
1197/// one, which is upstream reading it before it checks it.
1198fn bad_setting(name: &str, parsed: bool) -> Error {
1199    if parsed {
1200        Error::fmt(
1201            Code::Invalid,
1202            format_args!(
1203                "CONFIG SET failed (possibly related to argument '{name}') - argument must be between 0 and 9223372036854775807 inclusive"
1204            ),
1205        )
1206    } else {
1207        Error::fmt(
1208            Code::Invalid,
1209            format_args!(
1210                "CONFIG SET failed (possibly related to argument '{name}') - argument couldn't be parsed into an integer"
1211            ),
1212        )
1213    }
1214}
1215
1216/// Whether a `CONFIG GET` argument is a pattern rather than a name.
1217///
1218/// Upstream's test, character for character: it looks for any of `[*?` and
1219/// takes the direct dictionary lookup when there are none. A name with none of
1220/// them in it is a name even when it would have matched nothing, and a pattern
1221/// with one of them in it is a pattern even when it would only have matched the
1222/// one setting.
1223fn is_pattern(asked: &[u8]) -> bool {
1224    asked.iter().any(|b| matches!(b, b'[' | b'*' | b'?'))
1225}
1226
1227/// Which spelling of a setting to answer under, or `None` when nothing asked
1228/// for it.
1229///
1230/// A setting spelled out is answered under the spelling the client used, and a
1231/// setting a pattern found is answered under its own name. That looks like a
1232/// quirk and is really the shape of upstream's code: it collects the matches in
1233/// a dictionary keyed by whatever it looked up with, which for the exact branch
1234/// is the client's string and for the pattern branch is the table's. So
1235/// `CONFIG GET MAXMEMORY` answers `MAXMEMORY` and `CONFIG GET MAX*` answers
1236/// `maxmemory`, and a tool comparing the name it asked for with the name it got
1237/// back only works if that is copied.
1238///
1239/// The first argument that matches wins, for the same reason: the dictionary
1240/// already holds the setting by the time the second one is looked at, and a
1241/// setting already in it is skipped.
1242///
1243/// `hidden` is upstream's flag for a setting a pattern does not find. Those are
1244/// the backstops nobody is meant to tune, and leaving them out of `CONFIG GET *`
1245/// keeps them out of anything that dumps the settings and writes them back.
1246fn asked<'a>(args: Args<'a>, name: &'a str, hidden: bool) -> Option<&'a [u8]> {
1247    (2..args.len()).find_map(|i| {
1248        let want = args.get(i);
1249        if is_pattern(want) {
1250            (!hidden && glob::matches_nocase(want, name.as_bytes(), true))
1251                .then_some(name.as_bytes())
1252        } else {
1253            is(want, name.as_bytes()).then_some(want)
1254        }
1255    })
1256}
1257
1258/// `CONFIG GET|SET|RESETSTAT|REWRITE|HELP`.
1259fn config(server: &Server, args: Args<'_>, out: &mut Out) -> Result<()> {
1260    let sub = args.get(1);
1261    if is(sub, b"GET") {
1262        if args.len() < 3 {
1263            return Err(args::wrong_arity_sub("config", "get"));
1264        }
1265        // Every one of these is the name to answer under rather than a yes or a
1266        // no, because a setting spelled out is answered under the client's
1267        // spelling. See [`asked`] for why that is upstream's rule and not an
1268        // accident of it.
1269        let wanted = |name: &'static str| asked(args, name, false);
1270        // A setting that two patterns both ask for is sent once, which is what
1271        // makes this a count of settings rather than a count of matches. The
1272        // two spellings of a ladder setting are two settings by that rule, so
1273        // `CONFIG GET hash-max-*` sends the listpack name and the ziplist name
1274        // and the same number under both, which is what a real server does.
1275        let fixed = SETTINGS.iter().filter_map(|(k, v)| Some((wanted(k)?, v)));
1276        let ladder = LADDER.iter().filter_map(|(k, n)| Some((wanted(k)?, n)));
1277        let policy = wanted(MAXMEMORY_POLICY);
1278        let threads = wanted(IO_THREADS);
1279        let limit = wanted(MAXMEMORY);
1280        let store = wanted(MAXSTORE);
1281        let where_ = wanted(DIR);
1282        let file = wanted(DBFILENAME);
1283        let pass = wanted(REQUIREPASS);
1284        let ttl = wanted(SEALED_TTL);
1285        let events = wanted(NOTIFY);
1286        let acls = wanted(ACLFILE);
1287        let logged = wanted(ACLLOG_MAX_LEN);
1288        let channels = wanted(ACL_PUBSUB_DEFAULT);
1289        // Both spellings are two settings by the same rule the ladder follows,
1290        // so `CONFIG GET *read-only*` sends the replica name and the slave name
1291        // and the same word under both.
1292        let readonly = REPLICA_READ_ONLY.map(wanted);
1293        let mauth = wanted(MASTERAUTH);
1294        let muser = wanted(MASTERUSER);
1295        // The four cluster settings, which are there on every server and not
1296        // only on a node, the same way a real server answers them: a tool asking
1297        // `CONFIG GET cluster-enabled` wants a no rather than nothing back.
1298        let coverage = CLUSTER_COVERAGE.map(wanted);
1299        let clustered = wanted(CLUSTER_ENABLED);
1300        let nodes_file = wanted(CLUSTER_CONFIG_FILE);
1301        let migration = MIGRATION.map(|(name, _, _, _, hidden)| asked(args, name, hidden));
1302        out.map(
1303            fixed.clone().count()
1304                + ladder.clone().count()
1305                + usize::from(policy.is_some())
1306                + usize::from(threads.is_some())
1307                + usize::from(limit.is_some())
1308                + usize::from(store.is_some())
1309                + usize::from(where_.is_some())
1310                + usize::from(file.is_some())
1311                + usize::from(pass.is_some())
1312                + usize::from(ttl.is_some())
1313                + usize::from(events.is_some())
1314                + usize::from(acls.is_some())
1315                + usize::from(logged.is_some())
1316                + usize::from(channels.is_some())
1317                + usize::from(readonly[0].is_some())
1318                + usize::from(readonly[1].is_some())
1319                + usize::from(mauth.is_some())
1320                + usize::from(muser.is_some())
1321                + usize::from(coverage[0].is_some())
1322                + usize::from(coverage[1].is_some())
1323                + usize::from(clustered.is_some())
1324                + usize::from(nodes_file.is_some())
1325                + migration.iter().filter(|name| name.is_some()).count(),
1326        );
1327        for (k, v) in fixed {
1328            out.bulk(k);
1329            out.bulk(v.as_bytes());
1330        }
1331        for (k, knob) in ladder {
1332            out.bulk(k);
1333            out.bulk_int(read_knob(&server.settings(), *knob) as i64);
1334        }
1335        if let Some(k) = policy {
1336            out.bulk(k);
1337            out.bulk(server.settings().policy().name().as_bytes());
1338        }
1339        if let Some(k) = threads {
1340            out.bulk(k);
1341            out.bulk_int(server.io_threads() as i64);
1342        }
1343        if let Some(k) = limit {
1344            // Back as a plain number of bytes whatever the client typed to set
1345            // it, which is what a real server does: `CONFIG SET maxmemory 1gb`
1346            // reads back as 1073741824.
1347            out.bulk(k);
1348            out.bulk_int(server.maxmemory() as i64);
1349        }
1350        if let Some(k) = store {
1351            // Minus one for no limit, and a plain number of bytes otherwise.
1352            // Zero cannot mean no limit here the way it does for `maxmemory`,
1353            // because zero is the setting that says the file holds nothing.
1354            out.bulk(k);
1355            out.bulk_int(server.maxstore().map_or(-1, |n| n as i64));
1356        }
1357        if let Some(k) = where_ {
1358            // Absolute, which is what a real server answers too: it resolves the
1359            // directory at startup and reports the resolved one, so a client can
1360            // tell where the files are without knowing where the process was
1361            // launched from.
1362            out.bulk(k);
1363            yo_alloc::allow(|| out.bulk(server.dir().to_string_lossy().as_bytes()));
1364        }
1365        if let Some(k) = file {
1366            // The name on its own and not the path, which is how a real server
1367            // answers it too: the two settings are joined by whoever reads them.
1368            out.bulk(k);
1369            out.bulk(persist::FILE.as_bytes());
1370        }
1371        if let Some(k) = pass {
1372            out.bulk(k);
1373            server.with_password(|p| out.bulk(p));
1374        }
1375        if let Some(k) = ttl {
1376            out.bulk(k);
1377            out.bulk_int(server.backup().ttl() as i64);
1378        }
1379        if let Some(k) = events {
1380            // The flags and not the string that set them, which is what a real
1381            // server answers too and is why the parser has a formatter next to
1382            // it rather than the text being kept.
1383            out.bulk(k);
1384            let (buf, len) = notify::format(server.notify_flags());
1385            out.bulk(&buf[..len]);
1386        }
1387        if let Some(k) = acls {
1388            // Exactly what the server was started with, which for nearly every
1389            // server is nothing at all. Not resolved to an absolute path the way
1390            // `dir` is, because a real server answers what it was given here.
1391            out.bulk(k);
1392            yo_alloc::allow(|| {
1393                out.bulk(
1394                    server
1395                        .aclfile()
1396                        .map(|p| p.to_string_lossy())
1397                        .unwrap_or_default()
1398                        .as_bytes(),
1399                );
1400            });
1401        }
1402        if let Some(k) = logged {
1403            out.bulk(k);
1404            out.bulk_int(server.acl_log().max_len() as i64);
1405        }
1406        if let Some(k) = channels {
1407            out.bulk(k);
1408            out.bulk(CHANNEL_DEFAULTS[usize::from(!server.users().open_channels())].as_bytes());
1409        }
1410        for name in readonly.into_iter().flatten() {
1411            {
1412                out.bulk(name);
1413                out.bulk(BOOLS[usize::from(!server.replica_read_only_setting())].as_bytes());
1414            }
1415        }
1416        if let Some(k) = coverage[0] {
1417            out.bulk(k);
1418            out.bulk(BOOLS[usize::from(!server.cluster_full_coverage())].as_bytes());
1419        }
1420        if let Some(k) = coverage[1] {
1421            out.bulk(k);
1422            out.bulk(BOOLS[usize::from(!server.cluster_reads_when_down())].as_bytes());
1423        }
1424        if let Some(k) = clustered {
1425            out.bulk(k);
1426            out.bulk(BOOLS[usize::from(!server.cluster_enabled())].as_bytes());
1427        }
1428        if let Some(k) = nodes_file {
1429            out.bulk(k);
1430            yo_alloc::allow(|| out.bulk(server.cluster_file().as_bytes()));
1431        }
1432        // Always a plain number of bytes or milliseconds, whatever the client
1433        // typed to set it, the same way `maxmemory` reads back: `CONFIG SET
1434        // cluster-slot-migration-handoff-max-lag-bytes 2mb` answers 2097152.
1435        for ((_, knob, ..), name) in MIGRATION.iter().zip(migration) {
1436            if let Some(name) = name {
1437                out.bulk(name);
1438                out.bulk_int(server.migration_knob(*knob));
1439            }
1440        }
1441        if mauth.is_some() || muser.is_some() {
1442            server.with_master_auth(|user, pass| {
1443                if let Some(k) = mauth {
1444                    out.bulk(k);
1445                    out.bulk(pass);
1446                }
1447                if let Some(k) = muser {
1448                    out.bulk(k);
1449                    out.bulk(user);
1450                }
1451            });
1452        }
1453    } else if is(sub, b"SET") {
1454        // Too few is a wrong number of arguments and an odd number is a syntax
1455        // error, which is not the same sentence and is not the same rule. A
1456        // real server counts the pairs after it has decided there is at least
1457        // one, so `CONFIG SET appendonly` is an arity error and `CONFIG SET
1458        // appendonly no maxmemory` is a syntax one.
1459        if args.len() < 4 {
1460            return Err(args::wrong_arity_sub("config", "set"));
1461        }
1462        if !args.len().is_multiple_of(2) {
1463            return Err(args::syntax());
1464        }
1465        // Every pair is checked before any of them is applied, because a real
1466        // server takes the whole `CONFIG SET` or none of it. `CONFIG SET
1467        // hash-max-listpack-entries 7 set-max-listpack-entries abc` leaves the
1468        // hash setting where it was, which was checked rather than assumed.
1469        let mut writes = [None; 16];
1470        let mut count = 0;
1471        let mut policy = None;
1472        let mut limit = None;
1473        let mut store = None;
1474        let mut ttl = None;
1475        let mut events = None;
1476        let mut password = None;
1477        let mut logged = None;
1478        let mut channels = None;
1479        let mut readonly = None;
1480        let mut coverage: [Option<bool>; 2] = [None, None];
1481        let mut mauth = None;
1482        let mut muser = None;
1483        let mut migration: [Option<i64>; MIGRATION.len()] = [None; MIGRATION.len()];
1484        let mut i = 2;
1485        while i < args.len() {
1486            let (name, value) = (args.get(i), args.get(i + 1));
1487            i += 2;
1488            if is(name, MAXMEMORY.as_bytes()) {
1489                let Some(bytes) = parse_memory(value) else {
1490                    return Err(Error::fmt(
1491                        Code::Invalid,
1492                        format_args!(
1493                            "CONFIG SET failed (possibly related to argument '{MAXMEMORY}') - argument must be a memory value"
1494                        ),
1495                    ));
1496                };
1497                limit = Some(bytes);
1498                continue;
1499            }
1500            if is(name, MAXSTORE.as_bytes()) {
1501                // `-1` before the memory parser sees it, because that parser
1502                // refuses a sign and should keep refusing one: `maxmemory -1`
1503                // is not a very large number and never was.
1504                let parsed = if value == b"-1" {
1505                    Some(None)
1506                } else {
1507                    parse_memory(value).map(Some)
1508                };
1509                let Some(bytes) = parsed else {
1510                    return Err(Error::fmt(
1511                        Code::Invalid,
1512                        format_args!(
1513                            "CONFIG SET failed (possibly related to argument '{MAXSTORE}') - argument must be a memory value or -1"
1514                        ),
1515                    ));
1516                };
1517                store = Some(bytes);
1518                continue;
1519            }
1520            if is(name, MAXMEMORY_POLICY.as_bytes()) {
1521                // Named twice in one command, the last one wins, which is the
1522                // same rule the ladder settings follow here and is not what a
1523                // real server does with a setting repeated in a single `CONFIG
1524                // SET`. It refuses the command instead, which is D-138.
1525                let Some(p) = Policy::parse(value) else {
1526                    return Err(Error::fmt(
1527                        Code::Invalid,
1528                        format_args!(
1529                            "CONFIG SET failed (possibly related to argument '{MAXMEMORY_POLICY}') - argument(s) must be one of the following: {PolicyNames}"
1530                        ),
1531                    ));
1532                };
1533                policy = Some(p);
1534                continue;
1535            }
1536            // Refused whatever the value is, including the one they are already
1537            // set to, which is the one place a setting here does not take the
1538            // write that changes nothing. That is the reference's answer: a
1539            // protected config is refused before anybody looks at what was
1540            // asked for.
1541            if let Some(protected) = [DIR, DBFILENAME]
1542                .into_iter()
1543                .find(|p| is(name, p.as_bytes()))
1544            {
1545                return Err(Error::fmt(
1546                    Code::Unsupported,
1547                    format_args!(
1548                        "CONFIG SET failed (possibly related to argument '{protected}') - can't set protected config"
1549                    ),
1550                ));
1551            }
1552            // Refused whatever the value is, including the one it is already
1553            // set to, which is how a real server answers every immutable
1554            // config: the check is on the name and never reaches the value.
1555            // The names in `SETTINGS` take the value that changes nothing,
1556            // which is a difference and is registered as one.
1557            if is(name, ACLFILE.as_bytes()) {
1558                return Err(Error::fmt(
1559                    Code::Unsupported,
1560                    format_args!(
1561                        "CONFIG SET failed (possibly related to argument '{ACLFILE}') - can't set immutable config"
1562                    ),
1563                ));
1564            }
1565            if is(name, ACLLOG_MAX_LEN.as_bytes()) {
1566                let Some(n) = parse_i64(value).filter(|&n| n >= 0) else {
1567                    return Err(bad_setting(ACLLOG_MAX_LEN, parse_i64(value).is_some()));
1568                };
1569                logged = Some(n as u64);
1570                continue;
1571            }
1572            if is(name, ACL_PUBSUB_DEFAULT.as_bytes()) {
1573                let Some(at) = CHANNEL_DEFAULTS
1574                    .iter()
1575                    .position(|w| is(value, w.as_bytes()))
1576                else {
1577                    return Err(Error::fmt(
1578                        Code::Invalid,
1579                        format_args!(
1580                            "CONFIG SET failed (possibly related to argument '{ACL_PUBSUB_DEFAULT}') - argument(s) must be one of the following: {}, {}",
1581                            CHANNEL_DEFAULTS[0], CHANNEL_DEFAULTS[1]
1582                        ),
1583                    ));
1584                };
1585                channels = Some(at == 0);
1586                continue;
1587            }
1588            if is(name, REQUIREPASS.as_bytes()) {
1589                // Anything at all is a password, including an empty one, which
1590                // is how a password is taken off again. There is nothing to
1591                // refuse here and a real server refuses nothing either.
1592                password = Some(value);
1593                continue;
1594            }
1595            if let Some(spelling) = REPLICA_READ_ONLY.iter().find(|k| is(name, k.as_bytes())) {
1596                let Some(at) = BOOLS.iter().position(|w| is(value, w.as_bytes())) else {
1597                    return Err(Error::fmt(
1598                        Code::Invalid,
1599                        format_args!(
1600                            "CONFIG SET failed (possibly related to argument '{spelling}') - argument must be 'yes' or 'no'"
1601                        ),
1602                    ));
1603                };
1604                readonly = Some(at == 0);
1605                continue;
1606            }
1607            if let Some(at) = CLUSTER_COVERAGE.iter().position(|k| is(name, k.as_bytes())) {
1608                let Some(word) = BOOLS.iter().position(|w| is(value, w.as_bytes())) else {
1609                    return Err(Error::fmt(
1610                        Code::Invalid,
1611                        format_args!(
1612                            "CONFIG SET failed (possibly related to argument '{}') - argument must be 'yes' or 'no'",
1613                            CLUSTER_COVERAGE[at]
1614                        ),
1615                    ));
1616                };
1617                coverage[at] = Some(word == 0);
1618                continue;
1619            }
1620            if is(name, CLUSTER_ENABLED.as_bytes()) || is(name, CLUSTER_CONFIG_FILE.as_bytes()) {
1621                return Err(yo_alloc::allow(|| {
1622                    Error::fmt(
1623                        Code::Unsupported,
1624                        format_args!(
1625                            "CONFIG SET failed (possibly related to argument '{}') - can't set immutable config",
1626                            String::from_utf8_lossy(name).to_lowercase()
1627                        ),
1628                    )
1629                }));
1630            }
1631            if let Some((at, (k, knob, memory, least, _))) = MIGRATION
1632                .iter()
1633                .enumerate()
1634                .find(|(_, (k, ..))| is(name, k.as_bytes()))
1635            {
1636                // The one that counts bytes takes a unit and the three that
1637                // count something else do not, which is upstream's split
1638                // between a memory config and an integer one and is why
1639                // `write-pause-timeout 10s` is refused while
1640                // `handoff-max-lag-bytes 10mb` is taken.
1641                let parsed = if *memory {
1642                    parse_memory(value).map(|n| n as i64).filter(|n| *n >= 0)
1643                } else {
1644                    parse_i64(value)
1645                };
1646                let Some(n) = parsed else {
1647                    if *memory {
1648                        return Err(Error::fmt(
1649                            Code::Invalid,
1650                            format_args!(
1651                                "CONFIG SET failed (possibly related to argument '{k}') - argument must be a memory value"
1652                            ),
1653                        ));
1654                    }
1655                    return Err(bad_setting(k, false));
1656                };
1657                // The archived count is the only one with a ceiling, because it
1658                // is an `int` upstream and the other three are a `long long`.
1659                // The sentence names the pair it was given rather than the pair
1660                // it happens to have, which is what a real server answers.
1661                let most = if *knob == Migration::Archived {
1662                    i64::from(i32::MAX)
1663                } else {
1664                    i64::MAX
1665                };
1666                if n < *least || n > most {
1667                    return Err(Error::fmt(
1668                        Code::Invalid,
1669                        format_args!(
1670                            "CONFIG SET failed (possibly related to argument '{k}') - argument must be between {least} and {most} inclusive"
1671                        ),
1672                    ));
1673                }
1674                migration[at] = Some(n);
1675                continue;
1676            }
1677            if is(name, MASTERAUTH.as_bytes()) {
1678                // Anything at all, including nothing, which is how the password
1679                // is taken off again. It is read at the next dial rather than
1680                // now, so setting it on a replica whose link is already up takes
1681                // effect the next time that link breaks and comes back.
1682                mauth = Some(value);
1683                continue;
1684            }
1685            if is(name, MASTERUSER.as_bytes()) {
1686                muser = Some(value);
1687                continue;
1688            }
1689            if is(name, NOTIFY.as_bytes()) {
1690                // The only setting here whose error names what was wrong with
1691                // the value rather than what the value should have been, and it
1692                // quotes the accepted characters in the reference's order.
1693                let Some(flags) = notify::parse(value) else {
1694                    return Err(Error::fmt(
1695                        Code::Invalid,
1696                        format_args!(
1697                            "CONFIG SET failed (possibly related to argument '{NOTIFY}') - Invalid event class character. Use '{}'.",
1698                            notify::ACCEPTED
1699                        ),
1700                    ));
1701                };
1702                events = Some(flags);
1703                continue;
1704            }
1705            if is(name, SEALED_TTL.as_bytes()) {
1706                let Some(n) = parse_i64(value).filter(|&n| n >= 0) else {
1707                    return Err(bad_setting(SEALED_TTL, parse_i64(value).is_some()));
1708                };
1709                ttl = Some(n as u64);
1710                continue;
1711            }
1712            if let Some((k, knob)) = LADDER.iter().find(|(k, _)| is(name, k.as_bytes())) {
1713                let Some(n) = parse_i64(value).filter(|&n| n >= 0) else {
1714                    return Err(bad_setting(k, parse_i64(value).is_some()));
1715                };
1716                if count == writes.len() {
1717                    // Sixteen pairs is more than the ten names there are, so
1718                    // getting here means a name was given twice enough times to
1719                    // fill it, and the last one would have won anyway.
1720                    return Err(args::syntax());
1721                }
1722                writes[count] = Some((*knob, n as usize));
1723                count += 1;
1724                continue;
1725            }
1726            // The same rule the fixed settings follow, against a number that is
1727            // read off the server instead of written down: a write of the count
1728            // the server already has changes nothing and is taken, and anything
1729            // else is refused, including a value that is not a number at all.
1730            // The comparison is on the parsed number rather than on the text so
1731            // that nothing here has to format the count into a buffer first.
1732            if is(name, IO_THREADS.as_bytes()) {
1733                if parse_i64(value) != Some(server.io_threads() as i64) {
1734                    return Err(Error::fmt(
1735                        Code::Unsupported,
1736                        format_args!(
1737                            "CONFIG SET failed (possibly related to argument '{IO_THREADS}') - can't set immutable config"
1738                        ),
1739                    ));
1740                }
1741                continue;
1742            }
1743            let Some((k, v)) = SETTINGS.iter().find(|(k, _)| is(name, k.as_bytes())) else {
1744                return Err(yo_alloc::allow(|| {
1745                    Error::fmt(
1746                        Code::Invalid,
1747                        format_args!(
1748                            "Unknown option or number of arguments for CONFIG SET - '{}'",
1749                            String::from_utf8_lossy(name)
1750                        ),
1751                    )
1752                }));
1753            };
1754            if value != v.as_bytes() {
1755                return Err(Error::fmt(
1756                    Code::Unsupported,
1757                    format_args!(
1758                        "CONFIG SET failed (possibly related to argument '{k}') - can't set immutable config"
1759                    ),
1760                ));
1761            }
1762        }
1763        // Every stripe of every database, because these are one server wide
1764        // number in Redis and the fact that a `Keyspace` carries its own copy is
1765        // ours and not the client's problem. A stripe that missed one would put
1766        // a key in a different shape from the same key on the stripe next to it,
1767        // which `OBJECT ENCODING` would then answer differently for depending on
1768        // where the key happened to land.
1769        // The whole database is held while its stripes are set rather than one
1770        // stripe at a time, for the same reason they all get the same number: a
1771        // client that read `OBJECT ENCODING` in the middle of a half done change
1772        // would be told two different things about two keys depending on nothing
1773        // it can see.
1774        for (knob, n) in writes.iter().flatten() {
1775            for at in 0..DATABASES {
1776                let db = server.striped(at);
1777                let mut held = db.hold_many(0..db.width());
1778                for i in 0..db.width() {
1779                    write_knob(held.stripe_mut(i), *knob, *n);
1780                }
1781            }
1782        }
1783        if let Some(p) = policy {
1784            for at in 0..DATABASES {
1785                let db = server.striped(at);
1786                let mut held = db.hold_many(0..db.width());
1787                for i in 0..db.width() {
1788                    held.stripe_mut(i).set_policy(p);
1789                }
1790            }
1791        }
1792        if let Some(seconds) = ttl {
1793            server.backup().set_ttl(seconds);
1794        }
1795        if let Some(flags) = events {
1796            server.set_notify_flags(flags);
1797        }
1798        if let Some(n) = logged {
1799            server.acl_log().set_max_len(n);
1800        }
1801        if let Some(open) = channels {
1802            server.users().set_open_channels(open);
1803        }
1804        if let Some(yes) = readonly {
1805            server.set_replica_read_only(yes);
1806        }
1807        // One at a time and in the order they were written down, because none
1808        // of the four is read together with any of the others and there is
1809        // nothing here that a half applied pair would break.
1810        for ((_, knob, ..), value) in MIGRATION.iter().zip(migration) {
1811            if let Some(n) = value {
1812                server.set_migration_knob(*knob, n);
1813            }
1814        }
1815        // One write of the pair whichever of the two was named, for the same
1816        // reason the master credentials are written together: they are read as a
1817        // pair and a set of one has to leave the other where it was.
1818        if coverage[0].is_some() || coverage[1].is_some() {
1819            server.set_cluster_coverage(
1820                coverage[0].unwrap_or_else(|| server.cluster_full_coverage()),
1821                coverage[1].unwrap_or_else(|| server.cluster_reads_when_down()),
1822            );
1823        }
1824        // One write of the pair whichever of the two was named, because they
1825        // live together and a set of one has to leave the other where it was.
1826        if mauth.is_some() || muser.is_some() {
1827            let (user, pass) =
1828                yo_alloc::allow(|| server.with_master_auth(|u, p| (u.to_vec(), p.to_vec())));
1829            server.master_auth(muser.unwrap_or(&user), mauth.unwrap_or(&pass));
1830        }
1831        if let Some(value) = password {
1832            // The connections that are already open are left where they are,
1833            // including the one that sent this. See the `auth` module for why
1834            // that is the reference's rule and not an accident of it.
1835            server.set_password(value);
1836        }
1837        // Last, so that a `CONFIG SET maxmemory 1mb maxmemory-policy allkeys-lru`
1838        // has the policy in place before the limit that will act on it. The two
1839        // in the other order would run the first eviction under whatever the
1840        // policy used to be, which for a fresh server is `noeviction` and would
1841        // refuse the next write instead of making room for it.
1842        if let Some(bytes) = store {
1843            server.set_maxstore(bytes);
1844        }
1845        if let Some(bytes) = limit {
1846            server.set_maxmemory(bytes);
1847        }
1848        out.ok();
1849    } else if is(sub, b"RESETSTAT") {
1850        server.reset_stats();
1851        out.ok();
1852    } else if is(sub, b"REWRITE") {
1853        return Err(Error::new(
1854            Code::Unsupported,
1855            "The server is running without a config file",
1856        ));
1857    } else if is(sub, b"HELP") {
1858        help(out, CONFIG_HELP);
1859    } else {
1860        return Err(args::unknown_subcommand(sub, "CONFIG"));
1861    }
1862    Ok(())
1863}
1864
1865// -------------------------------------------------------------------- INFO
1866
1867/// `INFO [section ...]`.
1868///
1869/// Every number in here is one this layer can actually answer. There is no
1870/// `rdb_last_save_time` because there is no save, and a field that is not there
1871/// is a client falling back rather than a client believing a zero.
1872///
1873/// The `CPU` section used to be missing for the same reason and is here now,
1874/// because nothing measured it and then something did. It is one `getrusage`
1875/// call in [`super::cpu`], and the reason it went in is that Redis's own
1876/// `unit/info-command` tests fail without it: a monitoring tool graphs
1877/// processor time against wall clock to decide whether a server is busy or
1878/// waiting, so an absent field there is a real hole and not a tidy omission.
1879fn info(server: &Server, args: Args<'_>, out: &mut Out) {
1880    // Redis keeps two lists: the sections a bare `INFO` hands back, and the ones
1881    // that have to be asked for by name or by `all`. `commandstats` is in the
1882    // second, along with `latencystats` and `errorstats`, because they grow with
1883    // the number of distinct commands a server has seen and a monitoring tool
1884    // polling `INFO` every second does not want them.
1885    //
1886    // `unit/info-command` is exactly this distinction written down: it asks for
1887    // `INFO default` and insists `rejected_calls` is not in the answer, then
1888    // asks for `INFO all` and insists that it is.
1889    let named = |section: &str| (1..args.len()).any(|i| is(args.get(i), section.as_bytes()));
1890    let everything = (1..args.len()).any(|i| {
1891        let a = args.get(i);
1892        is(a, b"all") || is(a, b"everything")
1893    });
1894    let by_default = args.len() == 1 || (1..args.len()).any(|i| is(args.get(i), b"default"));
1895    let want = |section: &str| by_default || everything || named(section);
1896    let extra = |section: &str| everything || named(section);
1897    // One string, built once and written once. It allocates, which is allowed
1898    // here and nowhere near the commands that count: `INFO` is a monitoring
1899    // call and it is not on the path M2 is measured on.
1900    let text = yo_alloc::allow(|| {
1901        let mut s = String::with_capacity(1024);
1902        if want("server") {
1903            let _ = write!(
1904                s,
1905                "# Server\r\nredis_version:{REPORTED_VERSION}\r\nyo_version:{}\r\n\
1906                 redis_mode:{}\r\narch_bits:{}\r\nprocess_id:0\r\n\
1907                 run_id:0000000000000000000000000000000000000000\r\ntcp_port:{}\r\n\
1908                 uptime_in_seconds:{}\r\nio_threads_active:{}\r\n\r\n",
1909                env!("CARGO_PKG_VERSION"),
1910                if server.cluster_enabled() {
1911                    "cluster"
1912                } else {
1913                    "standalone"
1914                },
1915                usize::BITS,
1916                // The port the socket was actually bound to, which whoever bound
1917                // it told the server. Nought on an embedded caller that never
1918                // opened one, which is honest: there is no port.
1919                server.announced_port(),
1920                server.uptime_secs(),
1921                // The configured count, which is what Redis means by this field:
1922                // it is how many threads the server has for reading and writing
1923                // and not how many are busy at the moment the client asked.
1924                server.io_threads(),
1925            );
1926        }
1927        if want("clients") {
1928            let _ = write!(
1929                s,
1930                "# Clients\r\nconnected_clients:{}\r\nblocked_clients:{}\r\n\
1931                 pubsub_clients:{}\r\ncluster_connections:0\r\n\r\n",
1932                server
1933                    .totals()
1934                    .clients
1935                    .saturating_sub(server.replica_count()),
1936                server.parked(),
1937                server.pubsub_counts().clients,
1938            );
1939        }
1940        if want("memory") {
1941            // Both the cap and the quarter of it, because the quarter is an
1942            // empirical number and somebody surprised by it should be able to
1943            // see what it was a quarter of without reading the source. The
1944            // reasoning is written out in `cap`.
1945            let cap = crate::cap::cap();
1946            let compact = server.compaction();
1947            // Read out of its stripe before the write, because an argument list
1948            // keeps every temporary in it alive until the whole call is over
1949            // and one of the other arguments walks that same stripe.
1950            let policy = server.settings().policy().name();
1951            let _ = write!(
1952                s,
1953                "# Memory\r\nused_memory:{}\r\nused_memory_dataset:{}\r\n\
1954                 used_memory_overhead:{}\r\nmem_arena_bytes:{}\r\n\
1955                 mem_arena_segments:{}\r\nmem_arena_listed:{}\r\n\
1956                 mem_compact_walked:{}\r\n\
1957                 mem_compact_moved:{}\r\nmem_compact_bytes:{}\r\n\
1958                 mem_index_bytes:{}\r\n\
1959                 mem_client_buffers:{}\r\ntotal_system_memory:{}\r\n\
1960                 mem_cgroup_limit:{}\r\nmem_limit:{}\r\nmem_budget:{}\r\n\
1961                 maxmemory:{}\r\nmaxmemory_policy:{}\r\n\
1962                 maxstore:{}\r\nyo_store_bytes:{}\r\nyo_memory_regime:{}\r\n\r\n",
1963                server.memory_bytes(),
1964                server.dataset_bytes(),
1965                server.memory_bytes() - server.dataset_bytes(),
1966                server.arena_bytes(),
1967                server.segment_count(),
1968                server.listed_runs(),
1969                compact.walked,
1970                compact.moved,
1971                compact.bytes,
1972                server.index_bytes(),
1973                server.conn_bytes(),
1974                cap.host.unwrap_or(0),
1975                cap.cgroup.unwrap_or(0),
1976                cap.limit().unwrap_or(0),
1977                cap.budget(),
1978                server.maxmemory(),
1979                policy,
1980                server.maxstore().map_or(-1, |n| n as i64),
1981                server.store_bytes(),
1982                server.regime(),
1983            );
1984        }
1985        if want("persistence") {
1986            persist::info(server, &mut s);
1987        }
1988        if want("stats") {
1989            // The cold counters live here and not in the memory section,
1990            // because they are totals since the server started and everything
1991            // in that section is a level right now. `yo_cold_faults` over the
1992            // point reads a run issued is the ratio G9 is a gate on, and it
1993            // cannot be worked out from outside the server.
1994            let cold = server.cold_stats();
1995            let totals = server.totals();
1996            let subs = server.pubsub_counts();
1997            // The five ACL counters last, which is where a real server puts them
1998            // too: they are appended after the rest of the section rather than
1999            // written with it.
2000            let denied = server.acl_log().counters();
2001            let _ = write!(
2002                s,
2003                "# Stats\r\ntotal_connections_received:{}\r\n\
2004                 total_commands_processed:{}\r\nexpired_subkeys:{}\r\n\
2005                 expired_subkeys_active:{}\r\nexpired_keys:{}\r\n\
2006                 evicted_keys:{}\r\nkeyspace_hits:{}\r\nkeyspace_misses:{}\r\n\
2007                 yo_cold_demoted:{}\r\nyo_cold_promoted:{}\r\n\
2008                 yo_cold_faults:{}\r\nyo_cold_served:{}\r\nyo_cold_bytes_out:{}\r\n\
2009                 yo_cold_bytes_in:{}\r\npubsub_channels:{}\r\n\
2010                 pubsub_patterns:{}\r\npubsubshard_channels:{}\r\n\
2011                 acl_access_denied_auth:{}\r\nacl_access_denied_cmd:{}\r\n\
2012                 acl_access_denied_key:{}\r\nacl_access_denied_channel:{}\r\n\
2013                 acl_access_denied_tls_cert:{}\r\n\r\n",
2014                totals.connections,
2015                totals.commands,
2016                server.expired_fields(),
2017                server.expired_fields_active(),
2018                server.expired_keys(),
2019                server.evicted_keys(),
2020                server.keyspace_hits(),
2021                server.keyspace_misses(),
2022                cold.demoted,
2023                cold.promoted,
2024                cold.faults,
2025                cold.served,
2026                cold.bytes_out,
2027                cold.bytes_in,
2028                subs.channels,
2029                subs.patterns,
2030                subs.shard,
2031                denied[0],
2032                denied[1],
2033                denied[2],
2034                denied[3],
2035                denied[4],
2036            );
2037        }
2038        if want("cpu") {
2039            // Two of Redis's six are not here. `used_cpu_sys_main_thread` and
2040            // `used_cpu_user_main_thread` need `RUSAGE_THREAD`, which is Linux
2041            // only, and reporting the process totals under a name that says
2042            // main thread would be right on a single threaded server and wrong
2043            // on the one this becomes.
2044            if let Some(u) = cpu::usage() {
2045                let _ = write!(
2046                    s,
2047                    "# CPU\r\nused_cpu_sys:{:.6}\r\nused_cpu_user:{:.6}\r\n\
2048                     used_cpu_sys_children:{:.6}\r\nused_cpu_user_children:{:.6}\r\n\r\n",
2049                    u.sys, u.user, u.sys_children, u.user_children,
2050                );
2051            }
2052        }
2053        if want("replication") {
2054            super::repl::info(server, &mut s);
2055        }
2056        if want("cluster") {
2057            // One field, which is the one every client library reads on connect
2058            // to decide whether it needs a slot map at all.
2059            let _ = write!(
2060                s,
2061                "# Cluster\r\ncluster_enabled:{}\r\n\r\n",
2062                u8::from(server.cluster_enabled()),
2063            );
2064        }
2065        if extra("threads") {
2066            // An extra rather than a default section for the reason above: it
2067            // grows with the thread count, and a tool polling `INFO` every
2068            // second on a thirty two thread server does not want thirty two
2069            // more lines every time.
2070            //
2071            // The three numbers are the three questions worth asking of a
2072            // server where a connection belongs to the thread that accepted it.
2073            // `clients` says whether the connections open right now are shared
2074            // out. `connections` says whether they were shared out as they
2075            // arrived, which is a different question, because a split that was
2076            // fair at the start and is unfair now is clients hanging up rather
2077            // than an accept race. `commands` says whether an even split of
2078            // connections turned into an even split of work, which it does not
2079            // when the clients are not all asking for the same thing.
2080            let per = server.per_thread();
2081            let _ = write!(s, "# Threads\r\nio_threads:{}\r\n", per.len());
2082            for (at, thread) in per.iter().enumerate() {
2083                let _ = write!(
2084                    s,
2085                    "thread_{at}:clients={},connections={},commands={}\r\n",
2086                    thread.clients, thread.connections, thread.commands,
2087                );
2088            }
2089            s.push_str("\r\n");
2090        }
2091        if extra("commandstats") {
2092            s.push_str("# Commandstats\r\n");
2093            for (name, row) in server.command_stats() {
2094                let _ = write!(
2095                    s,
2096                    "cmdstat_{name}:calls={},rejected_calls={},failed_calls={}\r\n",
2097                    row.calls, row.rejected, row.failed,
2098                );
2099            }
2100            s.push_str("\r\n");
2101        }
2102        if want("keyspace") {
2103            s.push_str("# Keyspace\r\n");
2104            for i in 0..DATABASES {
2105                let keys = server.dbs[i].len();
2106                if keys > 0 {
2107                    // `avg_ttl` is still a zero, and Redis reports a zero there
2108                    // too on a server that has never run its active expiry
2109                    // cycle, because the number is a running estimate that cycle
2110                    // produces rather than something anybody measures on demand.
2111                    let expires = server.dbs[i].expires();
2112                    let _ = write!(s, "db{i}:keys={keys},expires={expires},avg_ttl=0\r\n");
2113                }
2114            }
2115            s.push_str("\r\n");
2116        }
2117        s
2118    });
2119    out.verbatim(b"txt", text.as_bytes());
2120}
2121
2122// -------------------------------------------------------------------- help
2123
2124/// The `HELP` reply, which is an array of simple strings on both protocols.
2125pub(super) fn help(out: &mut Out, lines: &[&str]) {
2126    out.array(lines.len());
2127    for line in lines {
2128        out.simple(line.as_bytes());
2129    }
2130}
2131
2132/// What `COMMAND HELP` says.
2133const COMMAND_HELP: &[&str] = &[
2134    "COMMAND <subcommand> [<arg> [value] [opt] ...]. Subcommands are:",
2135    "(no subcommand)",
2136    "    Return details about all commands.",
2137    "COUNT",
2138    "    Return the total number of commands in this server.",
2139    "LIST [FILTERBY <MODULE <module-name>|ACLCAT <category>|PATTERN <pattern>>]",
2140    "    Return a list of all commands in this server.",
2141    "INFO [<command-name> ...]",
2142    "    Return details about multiple commands.",
2143    "DOCS [<command-name> ...]",
2144    "    Return documentation details about multiple commands.",
2145    "GETKEYS <full-command>",
2146    "    Return the keys from a full command.",
2147    "HELP",
2148    "    Print this help.",
2149];
2150
2151/// What `CONFIG HELP` says.
2152const CONFIG_HELP: &[&str] = &[
2153    "CONFIG <subcommand> [<arg> [value] [opt] ...]. Subcommands are:",
2154    "GET <pattern>",
2155    "    Return parameters matching the glob-like <pattern> and their values.",
2156    "SET <directive> <value>",
2157    "    Set the configuration <directive> to <value>.",
2158    "RESETSTAT",
2159    "    Reset statistics reported by the INFO command.",
2160    "REWRITE",
2161    "    Rewrite the configuration file.",
2162    "HELP",
2163    "    Print this help.",
2164];
2165
2166#[cfg(test)]
2167mod tests {
2168    use super::{BOOLS, REPLICA_READ_ONLY, SETTINGS};
2169
2170    /// The backlog setting is a compiled in number written out twice, and the
2171    /// two have to be the same number: a client that reads `repl-backlog-size`
2172    /// and then works out how far behind a replica may fall before a full resync
2173    /// is reading this to answer that question.
2174    #[test]
2175    fn the_backlog_setting_is_the_size_the_backlog_actually_is() {
2176        let said = SETTINGS
2177            .iter()
2178            .find(|(k, _)| *k == "repl-backlog-size")
2179            .expect("the setting is there")
2180            .1;
2181        assert_eq!(
2182            said.parse::<usize>().expect("a number"),
2183            super::super::repl::BACKLOG_BYTES
2184        );
2185    }
2186
2187    /// The two spellings are one setting and the reference answers to both, so a
2188    /// script written against either works here.
2189    #[test]
2190    fn the_read_only_setting_answers_to_both_of_its_names() {
2191        assert_eq!(REPLICA_READ_ONLY, ["replica-read-only", "slave-read-only"]);
2192        assert_eq!(BOOLS, ["yes", "no"]);
2193    }
2194}