Skip to main content

yo_resp/dispatch/
cluster.rs

1//! Cluster mode: the 16384 slots, who owns them, and what a client is told
2//! when it asks the wrong node.
3//!
4//! A Redis cluster has no coordinator and no lookup service. Every key belongs
5//! to one of 16384 slots, worked out from the key name alone, and every node
6//! knows which node owns which slot. A client that guesses wrong is not proxied,
7//! it is told where to go, and it remembers. So the whole of routing is a
8//! function from a key to a number and a table from that number to a node, and
9//! both of them are here.
10//!
11//! # The slot
12//!
13//! `CRC16(key) mod 16384`, with one rule on top: if the key contains a `{`
14//! followed later by a `}` with at least one byte between them, only the bytes
15//! between them are hashed. That is the hash tag, and it is the only way a
16//! client has of making two keys land together, which is the only way a command
17//! that names two keys can be run at all. `{user1000}.following` and
18//! `{user1000}.followers` are one slot and one node.
19//!
20//! # The table
21//!
22//! 16384 entries, each naming the node that owns that slot or nobody. A slot
23//! nobody owns is a hole, and a cluster with a hole in it is down: rather than
24//! guess, every node refuses every key in the hole and, with
25//! `cluster-require-full-coverage` left on, refuses every key at all. That is
26//! severe on purpose. A cluster that answers for the slots it happens to have is
27//! a cluster that silently loses the rest.
28//!
29//! Two more tables sit beside it for the slots that are moving. A slot this node
30//! owns and is sending away is migrating, and a key in it that is already gone
31//! is answered with `ASK`, which is a redirection for one command rather than
32//! for the slot. A slot this node does not own and is receiving is importing, and
33//! a command lands on it only if the connection said `ASKING` first. The pair is
34//! what makes a slot move without a window where a key is on neither node.
35//!
36//! # What is here and what is not
37//!
38//! The slots, the table, the whole `CLUSTER` container, the redirections and the
39//! configuration file that carries all of it across a restart. The bus, which is
40//! the binary protocol nodes talk to each other over and the thing that actually
41//! fills the table, is next door in [`bus`]. Nothing in this file dials anybody:
42//! it reads the table and edits it, and a change an operator makes here is
43//! announced by asking the bus to send a packet once the lock is gone.
44//!
45//! What is not here is the failover vote, so a replica of a node that dies is
46//! listed and gossiped correctly and will not promote itself, which is D-152.
47
48use core::fmt::Write as _;
49use std::sync::atomic::Ordering::Relaxed;
50use std::sync::atomic::{AtomicBool, AtomicU64};
51
52use yo_common::lock::Lock;
53use yo_common::{Code, Error, Result};
54
55use crate::reply::Out;
56
57use super::args::{self, Args};
58use super::{Server, Session};
59
60mod bus;
61use super::keyspec;
62use super::table::Spec;
63
64/// How many slots a cluster has, which is Redis's number and is not a setting.
65///
66/// It is 16384 rather than a round number because the bus gossips a bitmap of
67/// every slot in every heartbeat, and 16384 bits is two kilobytes, which is
68/// what the authors were willing to spend on a packet sent every second.
69pub const SLOTS: usize = 16384;
70
71/// How long a cluster has to be fully covered before it says it is up.
72///
73/// Redis waits before turning `cluster_state` from fail to ok, so a node that
74/// has just been given its slots does not announce itself ready in the same
75/// instant and then take it back when the next heartbeat disagrees. The number
76/// is Redis's `CLUSTER_WRITABLE_DELAY`.
77const WRITABLE_DELAY_MS: u64 = 2000;
78
79/// How long a node that has been down waits before saying it is up again.
80///
81/// The two delays are different because they are about different things. The
82/// first is about a node that has only just started and has never been anything
83/// but ready. The second is about a node that was in the minority and has just
84/// seen the coverage come back, which is the case where being hasty is how a
85/// healed partition serves a key that has already been elected away somewhere
86/// else. Redis works it out as the node timeout clamped between 500ms and 5s,
87/// and with the default fifteen second timeout that is five seconds every time.
88const REJOIN_DELAY_MS: u64 = 5000;
89
90/// What the bus port is, which is the client port plus this.
91const BUS_OFFSET: u16 = 10000;
92
93/// How long a node id is, in hex characters.
94const ID_LEN: usize = 40;
95
96// ----------------------------------------------------------------- the slot
97
98/// The CRC16 table Redis hashes keys with, which is CCITT with the XMODEM
99/// parameters: polynomial 0x1021, no reflection, no initial value and no final
100/// exclusive or.
101///
102/// Written out rather than computed at startup because it is what every key on
103/// the command path goes through, and a table in the binary is a table that is
104/// already in cache by the time the first command arrives.
105#[rustfmt::skip]
106const CRC16: [u16; 256] = [
107    0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
108    0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
109    0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
110    0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
111    0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
112    0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
113    0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
114    0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
115    0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
116    0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
117    0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
118    0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
119    0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
120    0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
121    0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
122    0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
123    0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
124    0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
125    0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
126    0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
127    0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
128    0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
129    0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
130    0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
131    0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
132    0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
133    0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
134    0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
135    0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
136    0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
137    0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
138    0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0,
139];
140
141/// The CRC16 of `data`, which is only ever used to pick a slot.
142#[must_use]
143fn crc16(data: &[u8]) -> u16 {
144    let mut crc: u16 = 0;
145    for &byte in data {
146        let at = ((crc >> 8) ^ u16::from(byte)) & 0xff;
147        crc = (crc << 8) ^ CRC16[at as usize];
148    }
149    crc
150}
151
152/// Which slot `key` belongs to.
153///
154/// The hash tag rule is Redis's `keyHashSlot` and its edge cases are worth being
155/// exact about, because a client library implements the same rule and the two
156/// have to agree on every key or the client routes to a node that will not serve
157/// it. A `{` with no `}` after it hashes the whole key. A `{}` with nothing
158/// between hashes the whole key. Only the first `}` after the first `{` counts,
159/// so `{a}{b}` hashes `a`. And a key with no braces at all hashes whole, which
160/// is nearly every key there has ever been.
161#[must_use]
162pub fn key_slot(key: &[u8]) -> u16 {
163    let tagged = match key.iter().position(|&b| b == b'{') {
164        Some(open) => match key[open + 1..].iter().position(|&b| b == b'}') {
165            // The empty tag is not a tag, which is the `if (e == s+1)` arm.
166            Some(0) | None => key,
167            Some(len) => &key[open + 1..open + 1 + len],
168        },
169        None => key,
170    };
171    crc16(tagged) % SLOTS as u16
172}
173
174// ---------------------------------------------------------------- the table
175
176/// The reference's own node flag bits.
177///
178/// Kept as its bits rather than as a set of booleans because they go out on the
179/// bus in exactly this shape and come back in it, so anything else would be two
180/// translations for no gain.
181pub(crate) const FLAG_MASTER: u16 = 1;
182pub(crate) const FLAG_SLAVE: u16 = 2;
183pub(crate) const FLAG_PFAIL: u16 = 4;
184pub(crate) const FLAG_FAIL: u16 = 8;
185pub(crate) const FLAG_MYSELF: u16 = 16;
186pub(crate) const FLAG_HANDSHAKE: u16 = 32;
187pub(crate) const FLAG_NOADDR: u16 = 64;
188pub(crate) const FLAG_MEET: u16 = 128;
189pub(crate) const FLAG_MIGRATE_TO: u16 = 256;
190pub(crate) const FLAG_NOFAILOVER: u16 = 512;
191pub(crate) const FLAG_EXTENSIONS: u16 = 1024;
192
193/// One node in the cluster, which is this one and everybody the bus has found.
194#[derive(Clone)]
195struct Node {
196    /// The forty hex characters that name it, which never change while it lives.
197    id: String,
198    /// Where a client reaches it. Empty for this node, which is what a real
199    /// server reports for itself until something tells it its own address.
200    host: String,
201    /// The port a client reaches it on.
202    port: u16,
203    /// The port the bus reaches it on, which is nearly always the client port
204    /// plus ten thousand but is carried separately because a node behind a port
205    /// map announces something else.
206    bus: u16,
207    /// Which shard it belongs to, which is a master and its replicas.
208    shard: String,
209    /// The config epoch it claims its slots under.
210    epoch: u64,
211    /// What it is and how it is doing, in the reference's bits.
212    flags: u16,
213    /// The master it follows, as an index into the same table.
214    master: Option<u16>,
215    /// When a ping went out to it with no pong back yet, or nought for none
216    /// outstanding, which is the field the timeout is measured from.
217    ping_sent: u64,
218    /// When the last pong came back.
219    pong_recv: u64,
220    /// When anything at all last arrived from it. A node under a heavy pub/sub
221    /// load can be slow with its pong and perfectly alive, so traffic of any
222    /// kind counts as proof it is there.
223    data_recv: u64,
224    /// When this node decided it had failed.
225    fail_time: u64,
226    /// The replication offset it last put in a packet.
227    offset: u64,
228    /// Whether there is an outbound link to it at the moment.
229    linked: bool,
230    /// Who has said it is unreachable and when they last said so, which is the
231    /// weak quorum the FAIL flag needs.
232    reports: Vec<(String, u64)>,
233}
234
235impl Node {
236    /// A node with nothing known about it but where it is.
237    fn new(id: String, host: String, port: u16, bus: u16, flags: u16, now: u64) -> Node {
238        Node {
239            id,
240            host,
241            port,
242            bus,
243            shard: String::from_utf8_lossy(&new_id()).into_owned(),
244            epoch: 0,
245            flags,
246            master: None,
247            ping_sent: 0,
248            // Both stamps start at zero and not at now, which is the reference's
249            // own rule and is what makes the `myself` line read `0 0`. Nothing
250            // has been heard from a node that was made a moment ago, and saying
251            // otherwise would also hold off the first ping for a second.
252            pong_recv: 0,
253            // This one does start at now, because it stands in for the creation
254            // time as well and the handshake timeout is measured against it.
255            data_recv: now,
256            fail_time: 0,
257            offset: 0,
258            linked: false,
259            reports: Vec::new(),
260        }
261    }
262
263    /// Whether it is a master, which is the reference's rule that anything not
264    /// flagged a replica counts as one.
265    fn is_master(&self) -> bool {
266        self.flags & FLAG_SLAVE == 0
267    }
268
269    /// Whether it is down as far as anybody can tell.
270    fn down(&self) -> bool {
271        self.flags & (FLAG_PFAIL | FLAG_FAIL) != 0
272    }
273
274    /// The comma separated flag list a `CLUSTER NODES` line carries, in the
275    /// reference's own order, with `noflags` for a node with none of them.
276    fn flag_names(&self, into: &mut String) {
277        const NAMES: [(u16, &str); 8] = [
278            (FLAG_MYSELF, "myself"),
279            (FLAG_MASTER, "master"),
280            (FLAG_SLAVE, "slave"),
281            (FLAG_PFAIL, "fail?"),
282            (FLAG_FAIL, "fail"),
283            (FLAG_HANDSHAKE, "handshake"),
284            (FLAG_NOADDR, "noaddr"),
285            (FLAG_NOFAILOVER, "nofailover"),
286        ];
287        let mut first = true;
288        for (bit, name) in NAMES {
289            if self.flags & bit == 0 {
290                continue;
291            }
292            if !first {
293                into.push(',');
294            }
295            into.push_str(name);
296            first = false;
297        }
298        if first {
299            into.push_str("noflags");
300        }
301    }
302
303    /// The `ip:port@bus` field of a `CLUSTER NODES` line.
304    fn address(&self, into: &mut String) {
305        let _ = write!(into, "{}:{}@{}", self.host, self.port, self.bus);
306    }
307
308    /// The same field with the aux fields on the end of it, which is what goes in
309    /// the config file and not what goes on the wire. The reference keeps them
310    /// off `CLUSTER NODES` on purpose, because a client that learned to parse
311    /// them would break the next time somebody added one.
312    fn address_on_disk(&self, into: &mut String) {
313        self.address(into);
314        let _ = write!(into, ",,tls-port=0,shard-id={}", self.shard);
315    }
316}
317
318/// Who owns what, and what is moving.
319///
320/// One lock over all three tables rather than one each, because every reader
321/// wants a consistent answer across them: a slot is owned, or migrating, or
322/// importing, and a reader that saw two of those from different instants would
323/// route a client to a node that had already handed the slot on.
324struct Map {
325    /// Every node this one knows about, with this one first.
326    nodes: Vec<Node>,
327    /// Which node owns each slot, as an index into `nodes`, or `None`.
328    owner: Vec<Option<u16>>,
329    /// The slots this node owns and is sending away, and to whom.
330    migrating: Vec<Option<u16>>,
331    /// The slots this node is receiving, and from whom.
332    importing: Vec<Option<u16>>,
333}
334
335impl Map {
336    /// An empty table with room for every slot.
337    fn new(me: Node) -> Map {
338        Map {
339            nodes: vec![me],
340            owner: vec![None; SLOTS],
341            migrating: vec![None; SLOTS],
342            importing: vec![None; SLOTS],
343        }
344    }
345
346    /// The index of the node with this id, or `None` for one nobody knows.
347    fn find(&self, id: &[u8]) -> Option<u16> {
348        self.nodes
349            .iter()
350            .position(|n| n.id.as_bytes() == id)
351            .map(|at| at as u16)
352    }
353
354    /// Drop a node and everything that pointed at it.
355    ///
356    /// Every other table here holds an index into `nodes`, so dropping one out
357    /// of the middle means walking them and shifting anything above it down.
358    /// That is fine because a node leaves a cluster about as often as an
359    /// operator types, and holding ids instead of indices everywhere would cost
360    /// a string compare on the routing path, which is the one path that has to
361    /// be fast.
362    fn forget(&mut self, at: u16) {
363        self.nodes.remove(usize::from(at));
364        let shift = |slot: &mut Option<u16>| match *slot {
365            Some(node) if node == at => *slot = None,
366            Some(node) if node > at => *slot = Some(node - 1),
367            _ => {}
368        };
369        for slot in 0..SLOTS {
370            shift(&mut self.owner[slot]);
371            shift(&mut self.migrating[slot]);
372            shift(&mut self.importing[slot]);
373        }
374        for node in &mut self.nodes {
375            shift(&mut node.master);
376        }
377    }
378
379    /// How many masters are serving at least one slot and have not failed, which
380    /// is the electorate a FAIL vote is counted against.
381    fn voters(&self) -> usize {
382        let mut seen = vec![false; self.nodes.len()];
383        for owner in self.owner.iter().flatten() {
384            seen[*owner as usize] = true;
385        }
386        seen.iter().filter(|s| **s).count()
387    }
388
389    /// Whether this node owns `slot`, which is index nought owning it.
390    fn mine(&self, slot: u16) -> bool {
391        self.owner[slot as usize] == Some(0)
392    }
393
394    /// How many slots have an owner.
395    fn assigned(&self) -> usize {
396        self.owner.iter().filter(|o| o.is_some()).count()
397    }
398
399    /// How many nodes are serving at least one slot, which is Redis's
400    /// `cluster_size` and is not the same as how many nodes there are.
401    fn size(&self) -> usize {
402        let mut seen = vec![false; self.nodes.len()];
403        for owner in self.owner.iter().flatten() {
404            seen[*owner as usize] = true;
405        }
406        seen.iter().filter(|s| **s).count()
407    }
408
409    /// The runs of consecutive slots one node owns, in order.
410    fn runs(&self, node: u16) -> Vec<(u16, u16)> {
411        let mut runs: Vec<(u16, u16)> = Vec::new();
412        for slot in 0..SLOTS as u16 {
413            if self.owner[slot as usize] != Some(node) {
414                continue;
415            }
416            match runs.last_mut() {
417                Some(last) if last.1 + 1 == slot => last.1 = slot,
418                _ => runs.push((slot, slot)),
419            }
420        }
421        runs
422    }
423}
424
425// -------------------------------------------------------------- the settings
426
427/// Everything about cluster mode, all of it idle on a server that was not
428/// started with it on, which is nearly every server.
429pub(crate) struct Cluster {
430    /// Whether `--cluster-enabled yes` was given. Immutable for the life of the
431    /// process, which is Redis's rule and the only sane one: a server that could
432    /// be turned into a cluster node while it was holding keys would be a server
433    /// whose keys were suddenly in slots it did not own.
434    on: bool,
435    /// The tables, empty on a server that is not a cluster node.
436    map: Lock<Map>,
437    /// Redis's `currentEpoch`, which is the highest epoch anybody has claimed.
438    epoch: AtomicU64,
439    /// When the coverage last became complete, for the delay before this node
440    /// says the cluster is up. Nought means it is not covered.
441    covered_at: AtomicU64,
442    /// When cluster mode was turned on, which is the other end of the shorter
443    /// delay.
444    booted_at: AtomicU64,
445    /// Whether this node has been uncovered at any point since it started, which
446    /// is what picks between the two delays.
447    was_down: AtomicBool,
448    /// Whether every slot has to have an owner before anything is served, which
449    /// is Redis's `cluster-require-full-coverage` and is on by default.
450    full_coverage: AtomicBool,
451    /// Whether a read is still served while the cluster is down, which is
452    /// Redis's `cluster-allow-reads-when-down` and is off by default.
453    ///
454    /// Off is the safe answer and it is the one a real server ships with. A node
455    /// that cannot see the whole cluster does not know whether the part it
456    /// cannot see has already elected somebody else, so a read it served could
457    /// be a read of a key that has moved on. An operator who would rather have
458    /// stale reads than no reads turns it on and knows what they bought.
459    reads_when_down: AtomicBool,
460    /// Where the table is written, under the server's `dir`.
461    file: Lock<String>,
462    /// The links, the blacklist and the secret, none of which exist until the
463    /// bus is started and none of which a command reads on the hot path.
464    bus: bus::Bus,
465}
466
467impl Default for Cluster {
468    fn default() -> Cluster {
469        Cluster {
470            on: false,
471            map: Lock::new(Map {
472                nodes: Vec::new(),
473                owner: Vec::new(),
474                migrating: Vec::new(),
475                importing: Vec::new(),
476            }),
477            epoch: AtomicU64::new(0),
478            covered_at: AtomicU64::new(0),
479            booted_at: AtomicU64::new(0),
480            was_down: AtomicBool::new(false),
481            full_coverage: AtomicBool::new(true),
482            reads_when_down: AtomicBool::new(false),
483            file: Lock::new(String::new()),
484            bus: bus::Bus::default(),
485        }
486    }
487}
488
489impl Server {
490    /// Whether this server is a cluster node, which is one relaxed read of a
491    /// field that is false on nearly every server there is.
492    #[must_use]
493    pub fn cluster_enabled(&self) -> bool {
494        self.cluster.on
495    }
496
497    /// Turn cluster mode on, which only whoever built the server may do.
498    ///
499    /// Called before the first connection is accepted and never again, which is
500    /// why it takes `&mut self`: there is no lock here because there is nobody
501    /// to race with.
502    pub fn enable_cluster(&mut self, file: &str, port: u16) {
503        self.cluster.on = true;
504        self.cluster.booted_at.store(self.now_ms(), Relaxed);
505        let now = self.now_ms();
506        let me = yo_alloc::allow(|| {
507            Node::new(
508                String::from_utf8_lossy(&new_id()).into_owned(),
509                String::new(),
510                port,
511                port + BUS_OFFSET,
512                FLAG_MYSELF | FLAG_MASTER,
513                now,
514            )
515        });
516        // Under the server's directory when the name is a bare one, which is
517        // what a real server does with `cluster-config-file`: it takes the
518        // directory first and then opens everything relative to it. No name at
519        // all is a node that keeps nothing across a restart, which is not
520        // something a real server offers and is what a test wants.
521        let path = yo_alloc::allow(|| {
522            if file.is_empty() {
523                String::new()
524            } else {
525                self.dir().join(file).to_string_lossy().into_owned()
526            }
527        });
528        yo_alloc::allow(|| {
529            *self.cluster.map.lock() = Map::new(me);
530            *self.cluster.file.lock() = path;
531            // The secret nodes recognise each other by is made here rather than
532            // when the bus starts, because a node with no bus still has to have
533            // one: the whole cluster ends up on the smallest secret anybody
534            // started with, and a node holding an empty string would win that
535            // and leave everybody with no secret at all.
536            *self.cluster.bus.secret.lock() = String::from_utf8_lossy(&new_id()).into_owned();
537        });
538        // Whatever was written last time this node ran, if anything was. A node
539        // that comes back without its slots is a node that has silently given
540        // its half of the keyspace away, so a file that is there and unreadable
541        // is worth more noise than a file that is not there at all.
542        if let Err(e) = self.reload_cluster() {
543            eprintln!("cluster config file could not be read: {e}");
544        }
545        self.recount_coverage();
546    }
547
548    /// The secret one node authenticates to another node's client port with.
549    ///
550    /// Forty hex characters on a cluster node and empty on anything else, which
551    /// is what makes `AUTH "internal connection"` impossible to get past on a
552    /// server that is not in a cluster: there is no secret to guess.
553    ///
554    /// Handed out as a copy because it is read on `AUTH` and nowhere else, so
555    /// one allocation on a command that runs once a connection is cheaper than
556    /// keeping the lock held across a comparison.
557    pub(crate) fn cluster_secret(&self) -> String {
558        let held = self.cluster.bus.secret.lock();
559        yo_alloc::allow(|| held.clone())
560    }
561
562    /// Whether every slot has to be covered before anything is served.
563    pub(crate) fn cluster_full_coverage(&self) -> bool {
564        self.cluster.full_coverage.load(Relaxed)
565    }
566
567    /// Whether a read is served while the cluster is down.
568    pub(crate) fn cluster_reads_when_down(&self) -> bool {
569        self.cluster.reads_when_down.load(Relaxed)
570    }
571
572    /// Set either of the two, which `CONFIG SET` does and which really move: a
573    /// cluster with a hole in it starts answering for the slots it does have.
574    pub(crate) fn set_cluster_coverage(&self, full: bool, reads_when_down: bool) {
575        self.cluster.full_coverage.store(full, Relaxed);
576        self.cluster.reads_when_down.store(reads_when_down, Relaxed);
577        self.recount_coverage();
578    }
579
580    /// Where the table is written, which `CONFIG GET` reports.
581    pub(crate) fn cluster_file(&self) -> String {
582        let file = self.cluster.file.lock();
583        yo_alloc::allow(|| file.clone())
584    }
585
586    /// This node's id, which is what `CLUSTER MYID` answers.
587    pub(crate) fn cluster_id(&self) -> String {
588        let map = self.cluster.map.lock();
589        yo_alloc::allow(|| map.nodes.first().map_or_else(String::new, |n| n.id.clone()))
590    }
591
592    /// Whether the cluster is up, which is every slot covered for long enough.
593    ///
594    /// A hole makes it down, and so does having just been covered, for the delay
595    /// a real server waits. `cluster-require-full-coverage no` turns the first
596    /// half off, and then a node with any slots at all says it is up and refuses
597    /// only the keys in the hole.
598    pub(crate) fn cluster_up(&self) -> bool {
599        let at = self.cluster.covered_at.load(Relaxed);
600        if at == 0 {
601            return false;
602        }
603        let (since, wait) = if self.cluster.was_down.load(Relaxed) {
604            (at, REJOIN_DELAY_MS)
605        } else {
606            (self.cluster.booted_at.load(Relaxed), WRITABLE_DELAY_MS)
607        };
608        self.now_ms().saturating_sub(since) >= wait
609    }
610
611    /// Look at the coverage again and start or stop the clock on it.
612    ///
613    /// Called after anything that moves a slot. Cheap enough to do on the spot
614    /// rather than in a cron, and doing it on the spot is what makes
615    /// `CLUSTER ADDSLOTS` followed by `CLUSTER INFO` agree.
616    fn recount_coverage(&self) {
617        let covered = {
618            let map = self.cluster.map.lock();
619            let assigned = map.assigned();
620            if self.cluster_full_coverage() {
621                assigned == SLOTS
622            } else {
623                assigned > 0
624            }
625        };
626        if covered {
627            let _ =
628                self.cluster
629                    .covered_at
630                    .compare_exchange(0, self.now_ms().max(1), Relaxed, Relaxed);
631        } else {
632            self.cluster.covered_at.store(0, Relaxed);
633            self.cluster.was_down.store(true, Relaxed);
634        }
635    }
636}
637
638/// Forty hex characters from the same entropy the replication id comes from.
639fn new_id() -> [u8; ID_LEN] {
640    const HEX: &[u8; 16] = b"0123456789abcdef";
641    let mut raw = [0u8; ID_LEN / 2];
642    yo_common::entropy::fill(&mut raw);
643    let mut id = [0u8; ID_LEN];
644    for (i, byte) in raw.iter().enumerate() {
645        id[i * 2] = HEX[usize::from(byte >> 4)];
646        id[i * 2 + 1] = HEX[usize::from(byte & 15)];
647    }
648    id
649}
650
651// ------------------------------------------------------------ the redirects
652
653/// Whether the command carries its own `ASKING` rather than needing one sent.
654///
655/// One command does, `RESTORE-ASKING`, and the reference spells that as a flag on
656/// the command rather than as a name the routing gate knows, so this reads the
657/// flag. It is what makes a slot migration work at all: the node being sent the
658/// keys does not own the slot yet, and a client that had to send `ASKING` in
659/// front of every key would double the round trips for no reason.
660pub(super) fn asks(spec: &Spec) -> bool {
661    spec.flags.contains(&"asking")
662}
663
664/// Where a command's keys say it should run, or `None` for run it here.
665///
666/// Called for every command on a cluster node and for none at all on a server
667/// that is not one, which is the reason the enabled flag is read first and is a
668/// plain field rather than anything atomic. A command that names no key never
669/// redirects, whatever the state of the cluster, which is what lets a client
670/// send `PING` and `SUBSCRIBE` to a node that is not serving anything.
671///
672/// The order the checks go in is `getNodeByQuery`'s, and it is not the order
673/// anybody would guess, so it is worth writing down. The first key decides the
674/// slot and the node, and a first key in a slot nobody owns is
675/// `CLUSTERDOWN Hash slot not served` before the rest of the keys have even been
676/// looked at, so a command whose keys are in two slots and whose first slot is a
677/// hole is told about the hole and not about the two slots. Then the rest of the
678/// keys, and one in another slot is `CROSSSLOT`. Then the health of the cluster
679/// as a whole. Then the slot being handed over, which is where `ASK` and
680/// `TRYAGAIN` come from. And `MOVED` last, because it is the answer left when
681/// nothing else applies and the node is not this one.
682pub(super) fn gate(
683    server: &Server,
684    db: usize,
685    asking: bool,
686    spec: &Spec,
687    args: Args<'_>,
688) -> Option<Error> {
689    // Nothing to route. Cheap and first, because a container command and every
690    // connection command land here too.
691    if !keyspec::takes_keys(spec, args, 0) {
692        return None;
693    }
694    let mut slot: Option<u16> = None;
695    let mut crossed = false;
696    let mut keys = 0usize;
697    let mut present = 0usize;
698    let mut missing = 0usize;
699    let (owner, migrating, importing, here) = {
700        let map = server.cluster.map.lock();
701        // The slot of the first key, and then whether everything else agrees.
702        keyspec::find(spec, args, 0, &mut |run| {
703            for i in 0..run.count {
704                let at = run.first + i * run.step;
705                if at >= args.len() {
706                    continue;
707                }
708                let this = key_slot(args.get(at));
709                keys += 1;
710                match slot {
711                    None => slot = Some(this),
712                    Some(first) if first != this => crossed = true,
713                    Some(_) => {}
714                }
715            }
716        });
717        let at = usize::from(slot?);
718        (
719            map.owner[at],
720            map.migrating[at].map(|to| node_at(&map, to)),
721            map.importing[at].is_some(),
722            map.owner[at] == Some(0),
723        )
724    };
725    let slot = slot?;
726    // The first key's slot having no owner beats everything, including a command
727    // whose keys are in two slots.
728    let Some(owner) = owner else {
729        return Some(Error::new(
730            Code::Invalid,
731            "CLUSTERDOWN Hash slot not served",
732        ));
733    };
734    if crossed {
735        return Some(Error::new(
736            Code::Invalid,
737            "CROSSSLOT Keys in request don't hash to the same slot",
738        ));
739    }
740    // A cluster with a hole in it somewhere else. Reads are still served when
741    // the operator asked for that, writes never are, because a write to a
742    // cluster that cannot see all of itself is a write that may be about to be
743    // written somewhere else as well.
744    if !server.cluster_up() {
745        if !server.cluster.reads_when_down.load(Relaxed) {
746            return Some(Error::new(Code::Invalid, "CLUSTERDOWN The cluster is down"));
747        }
748        if spec.flags.contains(&"write") {
749            return Some(Error::new(
750                Code::Invalid,
751                "CLUSTERDOWN The cluster is down and only accepts read commands",
752            ));
753        }
754    }
755    // The slot is moving, so which keys are still here decides the answer. This
756    // is the only part of the gate that touches the keyspace, and it only runs
757    // for a slot that somebody is in the middle of handing over.
758    if migrating.is_some() || importing {
759        let held = &server.dbs[db];
760        keyspec::find(spec, args, 0, &mut |run| {
761            for i in 0..run.count {
762                let at = run.first + i * run.step;
763                if at >= args.len() {
764                    continue;
765                }
766                let key = args.get(at);
767                let mut stripe = held.hold(key);
768                if stripe.exists(key) {
769                    present += 1;
770                } else {
771                    missing += 1;
772                }
773            }
774        });
775    }
776    if let Some(to) = migrating
777        && missing > 0
778    {
779        // Some of them are here and some have gone, so there is no node that can
780        // answer the whole command and the client is told to come back.
781        if present > 0 {
782            return Some(Error::new(
783                Code::Invalid,
784                "TRYAGAIN Multiple keys request during rehashing of slot",
785            ));
786        }
787        return Some(redirect("ASK", slot, &to));
788    }
789    if importing && asking {
790        if keys > 1 && missing > 0 {
791            return Some(Error::new(
792                Code::Invalid,
793                "TRYAGAIN Multiple keys request during rehashing of slot",
794            ));
795        }
796        return None;
797    }
798    if here {
799        return None;
800    }
801    let (host, port) = {
802        let map = server.cluster.map.lock();
803        node_at(&map, owner)
804    };
805    Some(redirect("MOVED", slot, &(host, port)))
806}
807
808/// Where a node is reachable, copied out while the lock is held.
809fn node_at(map: &Map, at: u16) -> (String, u16) {
810    let node = &map.nodes[at as usize];
811    (yo_alloc::allow(|| node.host.clone()), node.port)
812}
813
814/// A `MOVED` or an `ASK` line, which are the same shape with a different word.
815///
816/// A node with no address of its own is written as the loopback, because a
817/// redirection has to name somewhere a client can connect to and an empty host
818/// is not one. A real server does the same when it has nothing better.
819fn redirect(word: &str, slot: u16, node: &(String, u16)) -> Error {
820    let host = if node.0.is_empty() {
821        "127.0.0.1"
822    } else {
823        node.0.as_str()
824    };
825    Error::fmt(
826        Code::Invalid,
827        format_args!("{word} {slot} {host}:{}", node.1),
828    )
829}
830
831// ------------------------------------------------------------- the container
832
833/// `CLUSTER <subcommand> ...`.
834pub(super) fn execute(
835    server: &Server,
836    session: &mut Session,
837    args: Args<'_>,
838    out: &mut Out,
839) -> Result<()> {
840    let session_db = session.db;
841    let sub = args.get(1);
842    // Every subcommand but `HELP` and the two that are only about arguments is
843    // refused outright on a server that was not started as a cluster node, which
844    // is Redis's rule and covers the whole container including `HELP` itself.
845    if !server.cluster_enabled() {
846        return match arity_of(sub) {
847            Some(n) if !arity_ok(n, args.len()) => Err(wrong_sub_arity(sub)),
848            Some(_) => Err(disabled()),
849            None => Err(args::unknown_subcommand(sub, "CLUSTER")),
850        };
851    }
852    let Some(n) = arity_of(sub) else {
853        return Err(args::unknown_subcommand(sub, "CLUSTER"));
854    };
855    if !arity_ok(n, args.len()) {
856        return Err(wrong_sub_arity(sub));
857    }
858    match () {
859        () if args::is(sub, b"myid") => out.bulk(server.cluster_id().as_bytes()),
860        () if args::is(sub, b"myshardid") => {
861            let map = server.cluster.map.lock();
862            out.bulk(map.nodes[0].shard.as_bytes());
863        }
864        () if args::is(sub, b"keyslot") => out.int(i64::from(key_slot(args.get(2)))),
865        () if args::is(sub, b"info") => info(server, out),
866        () if args::is(sub, b"nodes") => nodes(server, out),
867        () if args::is(sub, b"slots") => reply_slots(server, out),
868        () if args::is(sub, b"shards") => shards(server, out),
869        () if args::is(sub, b"links") => server.cluster_links(out),
870        () if args::is(sub, b"slaves") || args::is(sub, b"replicas") => {
871            replicas(server, args.get(2), out)?;
872        }
873        () if args::is(sub, b"count-failure-reports") => {
874            let map = server.cluster.map.lock();
875            let Some(at) = map.find(args.get(2)) else {
876                return Err(unknown_node(args.get(2)));
877            };
878            out.int(map.nodes[usize::from(at)].reports.len() as i64);
879        }
880        () if args::is(sub, b"countkeysinslot") => count_keys(server, session_db, args, out)?,
881        () if args::is(sub, b"getkeysinslot") => get_keys(server, session_db, args, out)?,
882        () if args::is(sub, b"addslots") => {
883            add_or_del(server, args, true, false)?;
884            out.ok();
885        }
886        () if args::is(sub, b"delslots") => {
887            add_or_del(server, args, false, false)?;
888            out.ok();
889        }
890        () if args::is(sub, b"addslotsrange") => {
891            add_or_del(server, args, true, true)?;
892            out.ok();
893        }
894        () if args::is(sub, b"delslotsrange") => {
895            add_or_del(server, args, false, true)?;
896            out.ok();
897        }
898        () if args::is(sub, b"setslot") => setslot(server, session_db, args, out)?,
899        () if args::is(sub, b"flushslots") => flushslots(server, out)?,
900        () if args::is(sub, b"bumpepoch") => bumpepoch(server, out)?,
901        () if args::is(sub, b"set-config-epoch") => set_config_epoch(server, args, out)?,
902        () if args::is(sub, b"reset") => {
903            if args.len() > 3 {
904                return Err(sub_syntax(sub));
905            }
906            reset(server, args, out)?;
907        }
908        () if args::is(sub, b"slot-stats") => slot_stats(server, session_db, args, out)?,
909        () if args::is(sub, b"migration") => migration(server, args, out)?,
910        () if args::is(sub, b"syncslots") => syncslots(server, session, args, out)?,
911        () if args::is(sub, b"saveconfig") => {
912            save(server)?;
913            out.ok();
914        }
915        () if args::is(sub, b"forget") => {
916            forget(server, args.get(2))?;
917            out.ok();
918        }
919        () if args::is(sub, b"replicate") => {
920            replicate(server, session_db, args.get(2))?;
921            out.ok();
922        }
923        () if args::is(sub, b"failover") => {
924            if args.len() > 3 {
925                return Err(sub_syntax(sub));
926            }
927            if args.len() == 3
928                && !args::is(args.get(2), b"force")
929                && !args::is(args.get(2), b"takeover")
930            {
931                return Err(args::syntax());
932            }
933            return Err(Error::new(
934                Code::Invalid,
935                "You should send CLUSTER FAILOVER to a replica",
936            ));
937        }
938        () if args::is(sub, b"meet") => {
939            if args.len() > 5 {
940                return Err(sub_syntax(sub));
941            }
942            let (host, port, bus) = meet(&args)?;
943            server.cluster_meet(&host, port, bus);
944            out.ok();
945        }
946        () if args::is(sub, b"help") => help(out),
947        _ => return Err(args::unknown_subcommand(sub, "CLUSTER")),
948    }
949    Ok(())
950}
951
952/// What every subcommand answers on a server that is not a cluster node.
953pub(super) fn disabled() -> Error {
954    Error::new(Code::Invalid, "This instance has cluster support disabled")
955}
956
957/// What atomic slot migration answers until it is in, which is D-149.
958///
959/// The sentence names the other way of moving a slot because there is one and it
960/// works, so an operator who reads this is one command away from getting on with
961/// it rather than stuck.
962fn not_yet(what: &str) -> Error {
963    Error::fmt(
964        Code::Invalid,
965        format_args!("{what} is not implemented yet, move the slot with SETSLOT and MIGRATE"),
966    )
967}
968
969/// The reference's sentence for a node id nobody has heard of.
970fn unknown_node(id: &[u8]) -> Error {
971    Error::fmt(
972        Code::Invalid,
973        format_args!("Unknown node {}", String::from_utf8_lossy(id)),
974    )
975}
976
977/// The same, in the spelling `CLUSTER SETSLOT` uses, which is a different
978/// sentence for the same thing and is the reference's.
979fn dont_know(id: &[u8]) -> Error {
980    Error::fmt(
981        Code::Invalid,
982        format_args!("I don't know about node {}", String::from_utf8_lossy(id)),
983    )
984}
985
986/// The arity of each subcommand, taken from the reference's own table, or `None`
987/// for a word that is not a subcommand.
988///
989/// A hand written list for the same reason `CONTAINERS` is one: the command
990/// table has a row for the container and none for what is behind it. It goes
991/// away with D-114.
992fn arity_of(sub: &[u8]) -> Option<i32> {
993    const TABLE: &[(&str, i32)] = &[
994        ("addslots", -3),
995        ("addslotsrange", -4),
996        ("bumpepoch", 2),
997        ("count-failure-reports", 3),
998        ("countkeysinslot", 3),
999        ("delslots", -3),
1000        ("delslotsrange", -4),
1001        ("failover", -2),
1002        ("flushslots", 2),
1003        ("forget", 3),
1004        ("getkeysinslot", 4),
1005        ("help", 2),
1006        ("info", 2),
1007        ("keyslot", 3),
1008        ("links", 2),
1009        ("meet", -4),
1010        ("migration", -4),
1011        ("myid", 2),
1012        ("myshardid", 2),
1013        ("nodes", 2),
1014        ("replicas", 3),
1015        ("replicate", 3),
1016        ("reset", -2),
1017        ("saveconfig", 2),
1018        ("set-config-epoch", 3),
1019        ("setslot", -4),
1020        ("shards", 2),
1021        ("slaves", 3),
1022        ("slot-stats", -4),
1023        ("slots", 2),
1024        ("syncslots", -3),
1025    ];
1026    TABLE
1027        .iter()
1028        .find(|(name, _)| args::is(sub, name.as_bytes()))
1029        .map(|(_, arity)| *arity)
1030}
1031
1032/// Redis's arity rule: a positive number is exact and a negative one is a floor.
1033fn arity_ok(arity: i32, len: usize) -> bool {
1034    let len = len as i32;
1035    if arity >= 0 {
1036        len == arity
1037    } else {
1038        len >= -arity
1039    }
1040}
1041
1042/// What a subcommand says when its own parsing gave up.
1043///
1044/// The container's arity is a floor, so a subcommand that takes a fixed number
1045/// of words past that floor has to check the count itself, and when it fails the
1046/// reference does not say the arity was wrong, it says this. The subcommand is
1047/// echoed back as the caller typed it and the container is upper cased, which is
1048/// `addReplySubcommandSyntaxError` word for word.
1049fn sub_syntax(sub: &[u8]) -> Error {
1050    Error::fmt(
1051        Code::Unsupported,
1052        format_args!(
1053            "unknown subcommand or wrong number of arguments for '{}'. Try CLUSTER HELP.",
1054            String::from_utf8_lossy(sub)
1055        ),
1056    )
1057}
1058
1059/// The wrong arity complaint, which names the subcommand and not the container.
1060fn wrong_sub_arity(sub: &[u8]) -> Error {
1061    Error::fmt(
1062        Code::Invalid,
1063        format_args!(
1064            "wrong number of arguments for 'cluster|{}' command",
1065            String::from_utf8_lossy(sub).to_lowercase()
1066        ),
1067    )
1068}
1069
1070// -------------------------------------------------------------- the reports
1071
1072/// `CLUSTER INFO`, which is the same field names in the same order a real server
1073/// prints, as one bulk string rather than as a map.
1074fn info(server: &Server, out: &mut Out) {
1075    let (assigned, size, known, my_epoch) = {
1076        let map = server.cluster.map.lock();
1077        (
1078            map.assigned(),
1079            map.size(),
1080            map.nodes.len(),
1081            map.nodes[0].epoch,
1082        )
1083    };
1084    let state = if server.cluster_up() { "ok" } else { "fail" };
1085    let text = yo_alloc::allow(|| {
1086        let mut s = String::with_capacity(512);
1087        let _ = write!(
1088            s,
1089            "cluster_state:{state}\r\ncluster_slots_assigned:{assigned}\r\n\
1090             cluster_slots_ok:{assigned}\r\ncluster_slots_pfail:0\r\ncluster_slots_fail:0\r\n\
1091             cluster_known_nodes:{known}\r\ncluster_size:{size}\r\n\
1092             cluster_current_epoch:{}\r\ncluster_my_epoch:{my_epoch}\r\n\
1093             cluster_stats_messages_sent:0\r\ncluster_stats_messages_received:0\r\n\
1094             total_cluster_links_buffer_limit_exceeded:0\r\n\
1095             cluster_slot_migration_active_tasks:0\r\n\
1096             cluster_slot_migration_active_trim_running:0\r\n\
1097             cluster_slot_migration_active_trim_current_job_keys:0\r\n\
1098             cluster_slot_migration_active_trim_current_job_trimmed:0\r\n\
1099             cluster_slot_migration_stats_active_trim_started:0\r\n\
1100             cluster_slot_migration_stats_active_trim_completed:0\r\n\
1101             cluster_slot_migration_stats_active_trim_cancelled:0\r\n",
1102            server.cluster.epoch.load(Relaxed),
1103        );
1104        s
1105    });
1106    out.verbatim(b"txt", text.as_bytes());
1107}
1108
1109/// `CLUSTER NODES`, which is the same text this node writes to its config file.
1110fn nodes(server: &Server, out: &mut Out) {
1111    let text = yo_alloc::allow(|| lines(server, false));
1112    out.verbatim(b"txt", text.as_bytes());
1113}
1114
1115/// One line per node, in the reference's field order.
1116///
1117/// `<id> <ip:port@bus,aux> <flags> <master> <ping-sent> <pong-recv> <epoch>
1118/// <link-state> <slot> ...`, with the migrating and importing slots on the end
1119/// of the owner's line in square brackets.
1120fn lines(server: &Server, on_disk: bool) -> String {
1121    let map = server.cluster.map.lock();
1122    let mut s = String::with_capacity(256);
1123    for at in 0..map.nodes.len() as u16 {
1124        describe(&map, at, on_disk, &mut s);
1125        s.push('\n');
1126    }
1127    s
1128}
1129
1130/// One node's line, without the newline, which `CLUSTER REPLICAS` wants one at a
1131/// time and `CLUSTER NODES` wants all of.
1132fn describe(map: &Map, at: u16, on_disk: bool, s: &mut String) {
1133    let node = &map.nodes[usize::from(at)];
1134    s.push_str(&node.id);
1135    s.push(' ');
1136    if on_disk {
1137        node.address_on_disk(s);
1138    } else {
1139        node.address(s);
1140    }
1141    s.push(' ');
1142    node.flag_names(s);
1143    s.push(' ');
1144    match node.master.and_then(|m| map.nodes.get(usize::from(m))) {
1145        Some(master) => s.push_str(&master.id),
1146        None => s.push('-'),
1147    }
1148    // A replica reports its master's epoch rather than its own, which is what a
1149    // client reading the line is actually asking about.
1150    let epoch = match node.master.and_then(|m| map.nodes.get(usize::from(m))) {
1151        Some(master) => master.epoch,
1152        None => node.epoch,
1153    };
1154    let link = if node.linked || at == 0 {
1155        "connected"
1156    } else {
1157        "disconnected"
1158    };
1159    let _ = write!(s, " {} {} {epoch} {link}", node.ping_sent, node.pong_recv);
1160    for (from, to) in map.runs(at) {
1161        if from == to {
1162            let _ = write!(s, " {from}");
1163        } else {
1164            let _ = write!(s, " {from}-{to}");
1165        }
1166    }
1167    if at == 0 {
1168        for slot in 0..SLOTS {
1169            if let Some(to) = map.migrating[slot] {
1170                let _ = write!(s, " [{slot}->-{}]", map.nodes[to as usize].id);
1171            }
1172            if let Some(from) = map.importing[slot] {
1173                let _ = write!(s, " [{slot}-<-{}]", map.nodes[from as usize].id);
1174            }
1175        }
1176    }
1177}
1178
1179/// `CLUSTER SLOTS`, which is one entry per run of slots one node owns.
1180///
1181/// The runs come out in slot order and not in node order, because that is the
1182/// order the reference walks and a client that caches this reply by position
1183/// will notice the difference.
1184fn reply_slots(server: &Server, out: &mut Out) {
1185    let mine = server.repl_offset();
1186    let map = server.cluster.map.lock();
1187    let at = out.len();
1188    let mut n = 0;
1189    let mut run: Option<(u16, u16)> = None;
1190    for slot in 0..=SLOTS as u16 {
1191        let owner = if slot as usize == SLOTS {
1192            None
1193        } else {
1194            map.owner[slot as usize]
1195        };
1196        match run {
1197            Some((node, _)) if owner == Some(node) => {}
1198            Some((node, from)) => {
1199                slot_run(&map, node, from, slot - 1, mine, out);
1200                n += 1;
1201                run = owner.map(|node| (node, slot));
1202            }
1203            None => run = owner.map(|node| (node, slot)),
1204        }
1205    }
1206    out.close_array(at, n);
1207}
1208
1209/// One `CLUSTER SLOTS` entry: the run, its owner, then the owner's replicas.
1210///
1211/// A replica everybody agrees is gone is left out, and so is one whose
1212/// replication offset is still zero, which is the reference's own filter and is
1213/// its way of saying this replica has never caught up with anything and is not
1214/// somewhere to send a reader yet. This node's own offset does not come off the
1215/// table, since nothing gossips a node its own offset, so it is passed in.
1216fn slot_run(map: &Map, node: u16, from: u16, to: u16, mine: u64, out: &mut Out) {
1217    let replicas: Vec<&Node> = map
1218        .nodes
1219        .iter()
1220        .enumerate()
1221        .filter(|(at, n)| {
1222            let offset = if *at == 0 { mine } else { n.offset };
1223            n.master == Some(node) && n.flags & FLAG_FAIL == 0 && offset != 0
1224        })
1225        .map(|(_, n)| n)
1226        .collect();
1227    out.array(3 + replicas.len());
1228    out.int(i64::from(from));
1229    out.int(i64::from(to));
1230    let held = &map.nodes[node as usize];
1231    for held in std::iter::once(held).chain(replicas.iter().copied()) {
1232        out.array(4);
1233        out.bulk(held.host.as_bytes());
1234        out.int(i64::from(held.port));
1235        out.bulk(held.id.as_bytes());
1236        out.array(0);
1237    }
1238}
1239
1240/// `CLUSTER SHARDS`, which is the same information grouped by shard rather than
1241/// by run, and is what a client library reads to find the replicas of a master.
1242fn shards(server: &Server, out: &mut Out) {
1243    let map = server.cluster.map.lock();
1244    let at = out.len();
1245    let mut n = 0;
1246    let mut done: Vec<&str> = Vec::new();
1247    for node in 0..map.nodes.len() {
1248        let shard = map.nodes[node].shard.as_str();
1249        if done.contains(&shard) {
1250            continue;
1251        }
1252        done.push(shard);
1253        let members: Vec<usize> = (0..map.nodes.len())
1254            .filter(|other| map.nodes[*other].shard == shard)
1255            .collect();
1256        // The slots of a shard are the slots of whichever member is holding
1257        // them, which is the master of it except for the moment after a
1258        // failover when two members still think they are.
1259        let runs: Vec<(u16, u16)> = members
1260            .iter()
1261            .flat_map(|member| map.runs(*member as u16))
1262            .collect();
1263        out.map(2);
1264        out.bulk(b"slots");
1265        out.array(runs.len() * 2);
1266        for (from, to) in runs {
1267            out.int(i64::from(from));
1268            out.int(i64::from(to));
1269        }
1270        out.bulk(b"nodes");
1271        out.array(members.len());
1272        for member in members {
1273            let held = &map.nodes[member];
1274            out.map(7);
1275            out.bulk(b"id");
1276            out.bulk(held.id.as_bytes());
1277            out.bulk(b"port");
1278            out.int(i64::from(held.port));
1279            out.bulk(b"ip");
1280            out.bulk(held.host.as_bytes());
1281            out.bulk(b"endpoint");
1282            out.bulk(held.host.as_bytes());
1283            out.bulk(b"role");
1284            out.bulk(if held.is_master() {
1285                b"master".as_slice()
1286            } else {
1287                b"replica".as_slice()
1288            });
1289            out.bulk(b"replication-offset");
1290            out.int(if member == 0 {
1291                server.repl_offset() as i64
1292            } else {
1293                held.offset as i64
1294            });
1295            out.bulk(b"health");
1296            out.bulk(match held.flags {
1297                f if f & FLAG_FAIL != 0 => b"fail".as_slice(),
1298                f if f & FLAG_PFAIL != 0 => b"loading".as_slice(),
1299                _ => b"online".as_slice(),
1300            });
1301        }
1302        n += 1;
1303    }
1304    out.close_array(at, n);
1305}
1306
1307/// The help text, word for word from the reference.
1308fn help(out: &mut Out) {
1309    const LINES: &[&str] = &[
1310        "CLUSTER <subcommand> [<arg> [value] [opt] ...]. Subcommands are:",
1311        "COUNTKEYSINSLOT <slot>",
1312        "    Return the number of keys in <slot>.",
1313        "GETKEYSINSLOT <slot> <count>",
1314        "    Return key names stored by current node in a slot.",
1315        "INFO",
1316        "    Return information about the cluster.",
1317        "KEYSLOT <key>",
1318        "    Return the hash slot for <key>.",
1319        "MYID",
1320        "    Return the node id.",
1321        "MYSHARDID",
1322        "    Return the node's shard id.",
1323        "NODES",
1324        "    Return cluster configuration seen by node. Output format:",
1325        "    <id> <ip:port@bus-port[,hostname]> <flags> <master> <pings> <pongs> <epoch> <link> <slot> ...",
1326        "REPLICAS <node-id>",
1327        "    Return <node-id> replicas.",
1328        "SLOTS",
1329        "    Return information about slots range mappings. Each range is made of:",
1330        "    start, end, master and replicas IP addresses, ports and ids",
1331        "SLOT-STATS",
1332        "    Return an array of slot usage statistics for slots assigned to the current node.",
1333        "SHARDS",
1334        "    Return information about slot range mappings and the nodes associated with them.",
1335        "ADDSLOTS <slot> [<slot> ...]",
1336        "    Assign slots to current node.",
1337        "ADDSLOTSRANGE <start slot> <end slot> [<start slot> <end slot> ...]",
1338        "    Assign slots which are between <start-slot> and <end-slot> to current node.",
1339        "BUMPEPOCH",
1340        "    Advance the cluster config epoch.",
1341        "COUNT-FAILURE-REPORTS <node-id>",
1342        "    Return number of failure reports for <node-id>.",
1343        "DELSLOTS <slot> [<slot> ...]",
1344        "    Delete slots information from current node.",
1345        "DELSLOTSRANGE <start slot> <end slot> [<start slot> <end slot> ...]",
1346        "    Delete slots information which are between <start-slot> and <end-slot> from current node.",
1347        "FAILOVER [FORCE|TAKEOVER]",
1348        "    Promote current replica node to being a master.",
1349        "FORGET <node-id>",
1350        "    Remove a node from the cluster.",
1351        "FLUSHSLOTS",
1352        "    Delete current node own slots information.",
1353        "MEET <ip> <port> [<bus-port>]",
1354        "    Connect nodes into a working cluster.",
1355        "REPLICATE <node-id>",
1356        "    Configure current node as replica to <node-id>.",
1357        "RESET [HARD|SOFT]",
1358        "    Reset current node (default: soft).",
1359        "SET-CONFIG-EPOCH <epoch>",
1360        "    Set config epoch of current node.",
1361        "SETSLOT <slot> (IMPORTING <node-id>|MIGRATING <node-id>|STABLE|NODE <node-id>)",
1362        "    Set slot state.",
1363        "SAVECONFIG",
1364        "    Force saving cluster configuration on disk.",
1365        "LINKS",
1366        "    Return information about all network links between this node and its peers.",
1367        "    Output format is an array where each array element is a map containing attributes of a link",
1368        "MIGRATION IMPORT <start-slot end-slot [start-slot end-slot ...]> |",
1369        "          STATUS [ID <task-id> | ALL] | CANCEL [ID <task-id> | ALL]",
1370        "    Start, monitor and cancel slot migration.",
1371        "HELP",
1372        "    Print this help.",
1373    ];
1374    out.array(LINES.len());
1375    for line in LINES {
1376        out.simple(line.as_bytes());
1377    }
1378}
1379
1380// ------------------------------------------------------------ the slot moves
1381
1382/// One slot number from an argument, refused the way the reference refuses one.
1383///
1384/// One sentence covers both ways of getting it wrong, which is the reference's
1385/// `getSlotOrReply` and is not the same as what `COUNTKEYSINSLOT` says: a word
1386/// that is not a number and a number that is not a slot both come back as
1387/// `Invalid or out of range slot` here.
1388fn slot_arg(args: &Args<'_>, at: usize) -> Result<u16> {
1389    args.int(at)
1390        .ok()
1391        .and_then(|n| u16::try_from(n).ok())
1392        .filter(|s| usize::from(*s) < SLOTS)
1393        .ok_or_else(|| Error::new(Code::Invalid, "Invalid or out of range slot"))
1394}
1395
1396/// `CLUSTER ADDSLOTS`, `DELSLOTS`, `ADDSLOTSRANGE` and `DELSLOTSRANGE`.
1397///
1398/// All four in one body because all four are the same walk with a different
1399/// stride and a different word in the complaint. Every slot is checked before
1400/// any of them is moved, which is the reference's behaviour and is what makes a
1401/// list with one bad number in it change nothing at all.
1402fn add_or_del(server: &Server, args: Args<'_>, add: bool, ranged: bool) -> Result<()> {
1403    let stride = if ranged { 2 } else { 1 };
1404    if ranged && !(args.len() - 2).is_multiple_of(2) {
1405        return Err(wrong_sub_arity(args.get(1)));
1406    }
1407    let mut wanted = Vec::new();
1408    let mut at = 2;
1409    while at < args.len() {
1410        let from = slot_arg(&args, at)?;
1411        let to = if ranged {
1412            slot_arg(&args, at + 1)?
1413        } else {
1414            from
1415        };
1416        if from > to {
1417            return Err(Error::fmt(
1418                Code::Invalid,
1419                format_args!("start slot number {from} is greater than end slot number {to}"),
1420            ));
1421        }
1422        for slot in from..=to {
1423            wanted.push(slot);
1424        }
1425        at += stride;
1426    }
1427    {
1428        let mut map = server.cluster.map.lock();
1429        let mut seen = vec![false; SLOTS];
1430        for slot in &wanted {
1431            let slot = usize::from(*slot);
1432            if seen[slot] {
1433                return Err(Error::fmt(
1434                    Code::Invalid,
1435                    format_args!("Slot {slot} specified multiple times"),
1436                ));
1437            }
1438            seen[slot] = true;
1439            let busy = map.owner[slot].is_some();
1440            if add && busy {
1441                return Err(Error::fmt(
1442                    Code::Invalid,
1443                    format_args!("Slot {slot} is already busy"),
1444                ));
1445            }
1446            if !add && !busy {
1447                return Err(Error::fmt(
1448                    Code::Invalid,
1449                    format_args!("Slot {slot} is already unassigned"),
1450                ));
1451            }
1452        }
1453        for slot in &wanted {
1454            let slot = usize::from(*slot);
1455            map.owner[slot] = if add { Some(0) } else { None };
1456            map.migrating[slot] = None;
1457            map.importing[slot] = None;
1458        }
1459    }
1460    server.recount_coverage();
1461    save(server)
1462}
1463
1464/// The address `CLUSTER MEET` was given, checked the way the reference checks it.
1465///
1466/// The port is reported back as the caller typed it rather than as it parsed,
1467/// which is the reference's wording and matters for a port that parsed fine and
1468/// was out of range.
1469fn meet(args: &Args<'_>) -> Result<(String, u16, u16)> {
1470    let host = String::from_utf8_lossy(args.get(2));
1471    let typed = String::from_utf8_lossy(args.get(3));
1472    let port = args.int(3).map_err(|_| {
1473        Error::fmt(
1474            Code::Invalid,
1475            format_args!("Invalid base port specified: {typed}"),
1476        )
1477    })?;
1478    let bus = match args.opt(4) {
1479        None => port + i64::from(BUS_OFFSET),
1480        Some(word) => args.int(4).map_err(|_| {
1481            Error::fmt(
1482                Code::Invalid,
1483                format_args!(
1484                    "Invalid bus port specified: {}",
1485                    String::from_utf8_lossy(word)
1486                ),
1487            )
1488        })?,
1489    };
1490    if !(1..=65535).contains(&port) || !(0..=65535).contains(&bus) {
1491        return Err(Error::fmt(
1492            Code::Invalid,
1493            format_args!("Invalid node address specified: {host}:{typed}"),
1494        ));
1495    }
1496    let bus = if bus == 0 {
1497        port + i64::from(BUS_OFFSET)
1498    } else {
1499        bus
1500    };
1501    let host = yo_alloc::allow(|| host.into_owned());
1502    Ok((host, port as u16, bus as u16))
1503}
1504
1505/// `CLUSTER REPLICAS`, which is one `CLUSTER NODES` line per replica of a node.
1506fn replicas(server: &Server, id: &[u8], out: &mut Out) -> Result<()> {
1507    let map = server.cluster.map.lock();
1508    let Some(at) = map.find(id) else {
1509        return Err(unknown_node(id));
1510    };
1511    if !map.nodes[usize::from(at)].is_master() {
1512        return Err(Error::new(
1513            Code::Invalid,
1514            "The specified node is not a master",
1515        ));
1516    }
1517    let start = out.len();
1518    let mut n = 0;
1519    for other in 0..map.nodes.len() as u16 {
1520        if map.nodes[usize::from(other)].master != Some(at) {
1521            continue;
1522        }
1523        let line = yo_alloc::allow(|| {
1524            let mut s = String::with_capacity(256);
1525            describe(&map, other, false, &mut s);
1526            s
1527        });
1528        out.bulk(line.as_bytes());
1529        n += 1;
1530    }
1531    out.close_array(start, n);
1532    Ok(())
1533}
1534
1535/// `CLUSTER FORGET`, which drops a node and makes the drop stick.
1536///
1537/// An id nobody has heard of is an error unless it is one this node forgot on
1538/// purpose a moment ago, in which case the answer is OK, because a tool that
1539/// sends the same forget to every node in the cluster should not hear an error
1540/// from the ones that had already been told by gossip.
1541fn forget(server: &Server, id: &[u8]) -> Result<()> {
1542    let at = {
1543        let map = server.cluster.map.lock();
1544        match map.find(id) {
1545            Some(0) => {
1546                return Err(Error::new(
1547                    Code::Invalid,
1548                    "I tried hard but I can't forget myself...",
1549                ));
1550            }
1551            Some(at) if map.nodes[0].master == Some(at) => {
1552                return Err(Error::new(Code::Invalid, "Can't forget my master!"));
1553            }
1554            Some(at) => at,
1555            None => {
1556                let name = String::from_utf8_lossy(id);
1557                if server.cluster_blacklisted(&name) {
1558                    return Ok(());
1559                }
1560                return Err(unknown_node(id));
1561            }
1562        }
1563    };
1564    server.cluster_forget(at);
1565    Ok(())
1566}
1567
1568/// `CLUSTER REPLICATE`, which makes this node a replica of another.
1569///
1570/// A master with slots or keys refuses, which is the reference's rule and is the
1571/// right one: the slots would be given away silently and the keys in them would
1572/// be answered for by two nodes at once.
1573fn replicate(server: &Server, db: usize, id: &[u8]) -> Result<()> {
1574    let at = {
1575        let map = server.cluster.map.lock();
1576        match map.find(id) {
1577            None => return Err(unknown_node(id)),
1578            Some(0) => return Err(Error::new(Code::Invalid, "Can't replicate myself")),
1579            Some(at) if !map.nodes[usize::from(at)].is_master() => {
1580                return Err(Error::new(
1581                    Code::Invalid,
1582                    "I can only replicate a master, not a replica.",
1583                ));
1584            }
1585            Some(at) => {
1586                if map.nodes[0].is_master()
1587                    && (!map.runs(0).is_empty() || !server.dbs[db].is_empty())
1588                {
1589                    return Err(Error::new(
1590                        Code::Invalid,
1591                        "To set a master the node must be empty and without assigned slots.",
1592                    ));
1593                }
1594                at
1595            }
1596        }
1597    };
1598    let Some(shared) = server.myself() else {
1599        return Err(Error::new(
1600            Code::Invalid,
1601            "CLUSTER REPLICATE is not available on an embedded server",
1602        ));
1603    };
1604    shared.cluster_replicate(at);
1605    Ok(())
1606}
1607
1608/// `CLUSTER SLOT-STATS`, which is how many keys each slot this node owns holds.
1609///
1610/// A real server keeps more metrics than this one and only when
1611/// `cluster-slot-stats-enabled` is on, so the default answer is the key count on
1612/// its own and that is what is here. The count is one walk of the keyspace with a
1613/// tally per slot rather than one walk per slot, which is the same D-150 cost as
1614/// `COUNTKEYSINSLOT` paid once instead of sixteen thousand times.
1615fn slot_stats(server: &Server, db: usize, args: Args<'_>, out: &mut Out) -> Result<()> {
1616    let sub = args.get(1);
1617    let mut wanted: Vec<u16> = Vec::new();
1618    let mut limit = SLOTS;
1619    let mut ascending = false;
1620    let ordered = args::is(args.get(2), b"orderby");
1621    if args::is(args.get(2), b"slotsrange") {
1622        if args.len() != 5 {
1623            return Err(sub_syntax(sub));
1624        }
1625        let from = slot_arg(&args, 3)?;
1626        let to = slot_arg(&args, 4)?;
1627        if from > to {
1628            return Err(Error::fmt(
1629                Code::Invalid,
1630                format_args!("Start slot number {from} is greater than end slot number {to}"),
1631            ));
1632        }
1633        wanted.extend(from..=to);
1634    } else if ordered {
1635        if !args::is(args.get(3), b"key-count") {
1636            return Err(Error::new(
1637                Code::Invalid,
1638                "Unrecognized sort metric for ORDERBY.",
1639            ));
1640        }
1641        let bad_limit = || {
1642            Error::new(
1643                Code::Invalid,
1644                "Limit has to lie in between 1 and 16384 (maximum number of slots).",
1645            )
1646        };
1647        let mut at = 4;
1648        while at < args.len() {
1649            let word = args.get(at);
1650            if args::is(word, b"limit") && at + 1 < args.len() {
1651                let n = args.int(at + 1).map_err(|_| bad_limit())?;
1652                if !(1..=SLOTS as i64).contains(&n) {
1653                    return Err(bad_limit());
1654                }
1655                limit = n as usize;
1656                at += 2;
1657            } else if args::is(word, b"asc") {
1658                ascending = true;
1659                at += 1;
1660            } else if args::is(word, b"desc") {
1661                at += 1;
1662            } else {
1663                return Err(args::syntax());
1664            }
1665        }
1666        wanted.extend(0..SLOTS as u16);
1667    } else {
1668        return Err(sub_syntax(sub));
1669    }
1670    let mut counts = vec![0i64; SLOTS];
1671    server.dbs[db].keys(|key| counts[key_slot(key) as usize] += 1);
1672    let map = server.cluster.map.lock();
1673    wanted.retain(|slot| map.mine(*slot));
1674    drop(map);
1675    if ordered {
1676        // The tie is broken by slot number ascending either way round, which is
1677        // what a real server does with a table nothing has been written to yet.
1678        if ascending {
1679            wanted.sort_by_key(|slot| (counts[*slot as usize], *slot));
1680        } else {
1681            wanted.sort_by_key(|slot| (-counts[*slot as usize], *slot));
1682        }
1683        wanted.truncate(limit);
1684    }
1685    out.array(wanted.len());
1686    for slot in wanted {
1687        out.array(2);
1688        out.int(i64::from(slot));
1689        out.map(1);
1690        out.bulk(b"key-count");
1691        out.int(counts[slot as usize]);
1692    }
1693    Ok(())
1694}
1695
1696/// `CLUSTER MIGRATION IMPORT|STATUS|CANCEL`, which is the newer way of moving a
1697/// slot range: the importing node drives the whole move rather than a tool
1698/// stepping it key by key.
1699///
1700/// Nothing here can start one until the bus is in, so what is implemented is the
1701/// argument surface and the two questions that have an answer on a node with no
1702/// tasks: nothing is running, so `STATUS` is empty and `CANCEL` cancelled none.
1703fn migration(server: &Server, args: Args<'_>, out: &mut Out) -> Result<()> {
1704    let sub = args.get(1);
1705    let action = args.get(2);
1706    if args::is(action, b"status") || args::is(action, b"cancel") {
1707        let by_id = args::is(args.get(3), b"id");
1708        if by_id && args.len() != 5 {
1709            return Err(wrong_sub_arity(sub));
1710        }
1711        if !by_id && !args::is(args.get(3), b"all") {
1712            return Err(Error::new(Code::Invalid, "unknown argument"));
1713        }
1714        if !by_id && args.len() != 4 {
1715            return Err(wrong_sub_arity(sub));
1716        }
1717        if args::is(action, b"status") {
1718            out.array(0);
1719        } else {
1720            out.int(0);
1721        }
1722        return Ok(());
1723    }
1724    if !args::is(action, b"import") {
1725        return Err(Error::new(Code::Invalid, "unknown argument"));
1726    }
1727    let ranges = slot_ranges(&args, 3)?;
1728    let map = server.cluster.map.lock();
1729    if ranges
1730        .iter()
1731        .flat_map(|(from, to)| *from..=*to)
1732        .all(|slot| map.mine(slot))
1733    {
1734        return Err(Error::new(
1735            Code::Invalid,
1736            "this node is already the owner of the slot range",
1737        ));
1738    }
1739    drop(map);
1740    Err(not_yet("CLUSTER MIGRATION IMPORT"))
1741}
1742
1743/// The list of slot ranges the tail of an argument list spells out, which is the
1744/// reference's `parseSlotRangesOrReply` and the validation behind it.
1745///
1746/// Sorted and with the ranges that touch joined up, because that is what the
1747/// reference hands back and the two checks after it are made against the joined
1748/// up list rather than what the caller typed. Ranges that touch join and ranges
1749/// that overlap do not, which is what makes `1 2 2 3` a slot named twice while
1750/// `1 2 3 4` is one range of four.
1751fn slot_ranges(args: &Args<'_>, from: usize) -> Result<Vec<(u16, u16)>> {
1752    let count = args.len().saturating_sub(from);
1753    if count < 2 || !count.is_multiple_of(2) {
1754        return Err(wrong_sub_arity(args.get(1)));
1755    }
1756    if count / 2 >= SLOTS {
1757        return Err(Error::fmt(
1758            Code::Invalid,
1759            format_args!("invalid number of slot ranges: {}", count / 2),
1760        ));
1761    }
1762    let mut ranges: Vec<(u16, u16)> = Vec::with_capacity(count / 2);
1763    let mut at = from;
1764    while at < args.len() {
1765        ranges.push((slot_arg(args, at)?, slot_arg(args, at + 1)?));
1766        at += 2;
1767    }
1768    ranges.sort_unstable();
1769    let mut joined: Vec<(u16, u16)> = Vec::with_capacity(ranges.len());
1770    for range in ranges {
1771        match joined.last_mut() {
1772            Some(last) if u32::from(last.1) + 1 == u32::from(range.0) => last.1 = range.1,
1773            _ => joined.push(range),
1774        }
1775    }
1776    let mut seen = vec![false; SLOTS];
1777    for &(start, end) in &joined {
1778        if start > end {
1779            return Err(Error::fmt(
1780                Code::Invalid,
1781                format_args!("start slot number {start} is greater than end slot number {end}"),
1782            ));
1783        }
1784        for slot in start..=end {
1785            if core::mem::replace(&mut seen[usize::from(slot)], true) {
1786                return Err(Error::fmt(
1787                    Code::Invalid,
1788                    format_args!("Slot {slot} specified multiple times"),
1789                ));
1790            }
1791        }
1792    }
1793    Ok(joined)
1794}
1795
1796/// `CLUSTER SYNCSLOTS`, which is what two nodes say to each other while a slot
1797/// range moves from one to the other under atomic slot migration.
1798///
1799/// Not a command an operator ever types. The node taking the slots opens an
1800/// ordinary client connection to the node giving them up, logs in as the cluster
1801/// rather than as a user, and drives the whole move over that connection and one
1802/// more like it. So every arm here answers another node running the same code,
1803/// which is why the reference does not defend the state machine against being
1804/// driven out of order and why it hangs up on anybody who is not a node rather
1805/// than just saying no. Getting to the point of being able to send this is the
1806/// hard part and the refusal makes it worthless.
1807///
1808/// What is in so far is the gate, the argument checking and the two arms that
1809/// have a real answer on a node with no migration running, which is every node
1810/// today. Starting a migration is D-149.
1811fn syncslots(server: &Server, session: &mut Session, args: Args<'_>, out: &mut Out) -> Result<()> {
1812    if !session.internal() {
1813        // The hang up is the reference's and it is worth copying. The sentence on
1814        // its own would be enough to be correct, and dropping the connection is
1815        // what makes sitting there guessing at the protocol expensive.
1816        session.hang_up();
1817        return Err(Error::new(
1818            Code::Invalid,
1819            "CLUSTER SYNCSLOTS subcommands are only allowed for internal clients",
1820        ));
1821    }
1822    let action = args.get(2);
1823    if !server.cluster.map.lock().nodes[0].is_master() {
1824        // A replica owns nothing, so there is nothing anybody could be asking it
1825        // to hand over. The one thing it does take is `CONF`, and only from its
1826        // own master, which is how it hears about the migration its master is in
1827        // the middle of. Anything else from the master is dropped in silence
1828        // rather than refused, because an error written into the replication
1829        // stream is an error nobody reads.
1830        if !session.serving_master() {
1831            session.hang_up();
1832            return Err(Error::new(
1833                Code::Invalid,
1834                "CLUSTER SYNCSLOTS subcommands are only allowed for master",
1835            ));
1836        }
1837        if !args::is(action, b"conf") {
1838            return Ok(());
1839        }
1840    }
1841    if args::is(action, b"sync") && args.len() >= 6 {
1842        return sync(server, args);
1843    }
1844    if args::is(action, b"rdbchannel") && args.len() == 4 {
1845        if args.get(3).len() != ID_LEN {
1846            return Err(Error::new(Code::Invalid, "Invalid task id"));
1847        }
1848        return Err(Error::new(
1849            Code::Invalid,
1850            "No slot migration task in progress",
1851        ));
1852    }
1853    if (args::is(action, b"snapshot-eof") || args::is(action, b"stream-eof")) && args.len() == 3 {
1854        // Both of these say a transfer has ended, and on a node with no transfer
1855        // running the reference logs that it was not expecting them and drops the
1856        // connection without writing anything back.
1857        session.hang_up();
1858        return Ok(());
1859    }
1860    if (args::is(action, b"ack") && args.len() == 5)
1861        || (args::is(action, b"fail") && args.len() == 4)
1862    {
1863        // The two arms that never answer. `ACK` moves a number on the task it
1864        // belongs to and there is no task, and `FAIL` does nothing at all on the
1865        // reference either: it is there so that the far side has something to
1866        // send that will not come back as a syntax error.
1867        return Ok(());
1868    }
1869    if args::is(action, b"conf") && args.len() >= 5 {
1870        return conf(server, args, out);
1871    }
1872    Err(args::syntax())
1873}
1874
1875/// `CLUSTER SYNCSLOTS SYNC <task-id> <start> <end> [<start> <end> ...]`, which is
1876/// the node taking the slots asking for them.
1877///
1878/// Every check the reference makes before it forks is made here, because they
1879/// are what a caller reads to find out it asked the wrong node, and only the
1880/// part that needs a snapshot to send is missing.
1881fn sync(server: &Server, args: Args<'_>) -> Result<()> {
1882    if !args.len().is_multiple_of(2) {
1883        return Err(wrong_sub_arity(args.get(1)));
1884    }
1885    let ranges = slot_ranges(&args, 4)?;
1886    {
1887        let map = server.cluster.map.lock();
1888        // The two ways of moving a slot do not mix. A slot that is half way
1889        // through the old one has keys on two nodes at once and a snapshot of it
1890        // would be a snapshot of half the data.
1891        if (0..SLOTS).any(|at| map.migrating[at].is_some() || map.importing[at].is_some()) {
1892            return Err(Error::new(
1893                Code::Invalid,
1894                "all slot states must be STABLE to start a slot migration task.",
1895            ));
1896        }
1897        let mut source = None;
1898        for slot in ranges.iter().flat_map(|(from, to)| *from..=*to) {
1899            let Some(owner) = map.owner[usize::from(slot)] else {
1900                return Err(Error::fmt(
1901                    Code::Invalid,
1902                    format_args!("slot has no owner: {slot}"),
1903                ));
1904            };
1905            if *source.get_or_insert(owner) != owner {
1906                return Err(Error::new(
1907                    Code::Invalid,
1908                    "slots belong to different source nodes",
1909                ));
1910            }
1911        }
1912        if source != Some(0) {
1913            return Err(Error::new(
1914                Code::Invalid,
1915                "This node is not the owner of the slots",
1916            ));
1917        }
1918    }
1919    Err(not_yet("CLUSTER SYNCSLOTS SYNC"))
1920}
1921
1922/// `CLUSTER SYNCSLOTS CONF <option> <value> [<option> <value> ...]`, which is
1923/// each side telling the other something it will need in a moment.
1924///
1925/// Three of the four options are the node that is talking, a hint about how big a
1926/// slot is so the receiving side can size its tables once instead of growing them
1927/// all the way up, and a capability word that is ignored on purpose so that a
1928/// newer node can say something an older one has never heard of. The fourth is a
1929/// master handing its own replicas the state of the migration it is in.
1930///
1931/// Every complaint here is written rather than returned, because the reference
1932/// keeps going after an option it did not understand and still says `OK` at the
1933/// end, so one command can answer with both an error line and an `OK`. Returning
1934/// would throw the first of those away.
1935fn conf(server: &Server, args: Args<'_>, out: &mut Out) -> Result<()> {
1936    let mut at = 3;
1937    while at < args.len() {
1938        if at + 1 >= args.len() {
1939            super::write_error(out, &wrong_sub_arity(args.get(1)));
1940            return Ok(());
1941        }
1942        let name = args.get(at);
1943        let value = args.get(at + 1);
1944        if args::is(name, b"node-id") {
1945            // Checked and then dropped, since the only thing that reads it is the
1946            // migration this cannot start yet.
1947            if value.len() != ID_LEN {
1948                let len = value.len();
1949                super::write_error(
1950                    out,
1951                    &Error::fmt(Code::Invalid, format_args!("Invalid node id length {len}")),
1952                );
1953                return Ok(());
1954            }
1955            if server.cluster.map.lock().find(value).is_none() {
1956                super::write_error(
1957                    out,
1958                    &Error::fmt(
1959                        Code::Invalid,
1960                        format_args!(
1961                            "Node {} not found in cluster",
1962                            String::from_utf8_lossy(value)
1963                        ),
1964                    ),
1965                );
1966                return Ok(());
1967            }
1968        } else if args::is(name, b"slot-info") {
1969            if !slot_info(value) {
1970                super::write_error(
1971                    out,
1972                    &Error::fmt(
1973                        Code::Invalid,
1974                        format_args!("Invalid slot info: {}", String::from_utf8_lossy(value)),
1975                    ),
1976                );
1977                return Ok(());
1978            }
1979        } else if args::is(name, b"asm-task") {
1980            if server.cluster.map.lock().nodes[0].is_master() {
1981                super::write_error(
1982                    out,
1983                    &Error::new(
1984                        Code::Invalid,
1985                        "CLUSTER SYNCSLOTS CONF ASM-TASK only allowed on replica",
1986                    ),
1987                );
1988                return Ok(());
1989            }
1990            // A replica that hears this is being told to follow along with a
1991            // migration its master is running, and there is nothing here to
1992            // follow along with yet, so it says so and carries on with the rest
1993            // of the options exactly as the reference does.
1994            super::write_error(
1995                out,
1996                &Error::fmt(
1997                    Code::Invalid,
1998                    format_args!(
1999                        "Failed to handle master task: {}",
2000                        String::from_utf8_lossy(value)
2001                    ),
2002                ),
2003            );
2004        } else if !args::is(name, b"capa") {
2005            super::write_error(
2006                out,
2007                &Error::fmt(
2008                    Code::Invalid,
2009                    format_args!("Unknown option {}", String::from_utf8_lossy(name)),
2010                ),
2011            );
2012        }
2013        at += 2;
2014    }
2015    out.ok();
2016    Ok(())
2017}
2018
2019/// Whether a `slot-info` value is one, which is `slot:key-count:expire-count`
2020/// with all three of them numbers and the first of them a slot.
2021///
2022/// What it is for is sizing: the node about to receive a slot is being told how
2023/// many keys are coming so that it can make room for them in one go. Nothing is
2024/// sized on it here, because the tables this engine keeps a slot's keys in grow
2025/// without the rehash a size hint exists to avoid, so the value is only checked.
2026fn slot_info(value: &[u8]) -> bool {
2027    let mut parts = value.split(|b| *b == b':');
2028    let Some(slot) = parts.next().and_then(yo_common::num::parse_i64) else {
2029        return false;
2030    };
2031    let Some(keys) = parts.next().and_then(yo_common::num::parse_i64) else {
2032        return false;
2033    };
2034    let Some(expires) = parts.next().and_then(yo_common::num::parse_i64) else {
2035        return false;
2036    };
2037    parts.next().is_none() && (0..SLOTS as i64).contains(&slot) && keys >= 0 && expires >= 0
2038}
2039
2040/// `CLUSTER SETSLOT <slot> IMPORTING|MIGRATING|STABLE|NODE`.
2041///
2042/// The four arms and the order of their checks are the reference's, which is
2043/// worth being exact about because a resharding tool drives this and reads the
2044/// sentences it gets back.
2045///
2046/// `NODE` is where a slot migration ends and it is the one arm that does more
2047/// than move a field. When the node being named is this one and this node was
2048/// importing the slot, the config epoch goes up and the whole cluster is told
2049/// straight away, because until that happens every other node is still pointing
2050/// clients at the node the slot came from and the higher epoch is the only thing
2051/// that makes them stop.
2052fn setslot(server: &Server, session_db: usize, args: Args<'_>, out: &mut Out) -> Result<()> {
2053    // A replica has no say in who owns what, so this is refused before the slot
2054    // number is even looked at.
2055    if !server.cluster.map.lock().nodes[0].is_master() {
2056        return Err(Error::new(
2057            Code::Invalid,
2058            "Please use SETSLOT only with masters.",
2059        ));
2060    }
2061    let slot = slot_arg(&args, 2)?;
2062    let action = args.get(3);
2063    let wrong = || {
2064        Error::new(
2065            Code::Invalid,
2066            "Invalid CLUSTER SETSLOT action or number of arguments. Try CLUSTER HELP",
2067        )
2068    };
2069    // Set when the `NODE` arm decides the rest of the cluster has to be told
2070    // now rather than at the next ping. Done once the lock is gone, since
2071    // building the packet takes the same lock.
2072    let mut announce = false;
2073    // And when this node has just given its last slot away and should follow
2074    // whoever took it. The same, and for the same reason.
2075    let mut follow: Option<u16> = None;
2076    {
2077        let mut map = server.cluster.map.lock();
2078        let at = usize::from(slot);
2079        if args::is(action, b"migrating") && args.len() == 5 {
2080            if !map.mine(slot) {
2081                return Err(Error::fmt(
2082                    Code::Invalid,
2083                    format_args!("I'm not the owner of hash slot {slot}"),
2084                ));
2085            }
2086            let Some(to) = map.find(args.get(4)) else {
2087                return Err(dont_know(args.get(4)));
2088            };
2089            map.migrating[at] = Some(to);
2090        } else if args::is(action, b"importing") && args.len() == 5 {
2091            if map.mine(slot) {
2092                return Err(Error::fmt(
2093                    Code::Invalid,
2094                    format_args!("I'm already the owner of hash slot {slot}"),
2095                ));
2096            }
2097            let Some(from) = map.find(args.get(4)) else {
2098                return Err(dont_know(args.get(4)));
2099            };
2100            map.importing[at] = Some(from);
2101        } else if args::is(action, b"stable") && args.len() == 4 {
2102            map.migrating[at] = None;
2103            map.importing[at] = None;
2104        } else if args::is(action, b"node") && args.len() == 5 {
2105            let Some(to) = map.find(args.get(4)) else {
2106                return Err(unknown_node(args.get(4)));
2107            };
2108            if !map.nodes[usize::from(to)].is_master() {
2109                return Err(Error::new(Code::Invalid, "Target node is not a master"));
2110            }
2111            let was_mine = map.owner[at] == Some(0);
2112            // The keys are counted twice here rather than once, which reads
2113            // oddly and is the reference's own shape. The first count refuses
2114            // handing the slot to somebody else while this node is still
2115            // holding keys for it, since that would leave two nodes answering
2116            // for the same data. The second clears the migrating mark, and it
2117            // is a separate question because a slot can be empty and marked
2118            // whether or not it was ever this node's.
2119            let held = keys_in_slot(server, session_db, slot);
2120            if was_mine && to != 0 && held != 0 {
2121                return Err(Error::fmt(
2122                    Code::Invalid,
2123                    format_args!(
2124                        "Can't assign hashslot {slot} to a different node while I still hold keys for this hash slot."
2125                    ),
2126                ));
2127            }
2128            if held == 0 {
2129                map.migrating[at] = None;
2130            }
2131            map.owner[at] = Some(to);
2132            // A master that has just handed over its last slot follows whoever
2133            // took it, which is what stops a resharded cluster being left with
2134            // a node that owns nothing and serves nobody.
2135            if was_mine && to != 0 && map.runs(0).is_empty() {
2136                follow = Some(to);
2137            }
2138            // The import is finished, so the epoch goes up and everybody is
2139            // told. Nothing else in this function moves the epoch, because
2140            // nothing else is this node claiming something that used to be
2141            // somebody else's.
2142            if to == 0 && map.importing[at].is_some() {
2143                bump_without_consensus(server, &mut map);
2144                map.importing[at] = None;
2145                announce = true;
2146            }
2147        } else {
2148            return Err(wrong());
2149        }
2150    }
2151    server.recount_coverage();
2152    save(server)?;
2153    // Only on a real server. An embedded one has no bus and nobody to follow,
2154    // and the slot still changes hands the same way.
2155    if let Some(to) = follow
2156        && let Some(shared) = server.myself()
2157    {
2158        shared.cluster_replicate(to);
2159    }
2160    if announce {
2161        server.cluster_broadcast_pong();
2162    }
2163    out.ok();
2164    Ok(())
2165}
2166
2167/// How many keys of a slot are here, which two of `SETSLOT NODE`'s refusals turn
2168/// on and which `CLUSTER COUNTKEYSINSLOT` answers.
2169fn keys_in_slot(server: &Server, at: usize, slot: u16) -> usize {
2170    let mut found = 0;
2171    server.dbs[at].keys(|key| {
2172        if key_slot(key) == slot {
2173            found += 1;
2174        }
2175    });
2176    found
2177}
2178
2179/// The reference's `clusterBumpConfigEpochWithoutConsensus`.
2180///
2181/// A node that has just taken a slot off somebody needs an epoch higher than
2182/// theirs, or every other node will keep the old owner: a higher epoch is the
2183/// whole of how the cluster decides which of two claims on a slot is the newer
2184/// one. Without consensus means exactly that, nobody is asked, and the comment
2185/// in the reference is worth repeating: two nodes can end up on the same epoch
2186/// this way, and the collision rule sorts that out afterwards rather than the
2187/// bump trying to avoid it.
2188///
2189/// It does nothing at all when this node already holds the largest epoch
2190/// anybody has, since there is nothing left to outrank. `false` is that case,
2191/// and it is what `CLUSTER BUMPEPOCH` answers `STILL` for.
2192fn bump_without_consensus(server: &Server, map: &mut Map) -> bool {
2193    let highest = map
2194        .nodes
2195        .iter()
2196        .map(|n| n.epoch)
2197        .max()
2198        .unwrap_or(0)
2199        .max(server.cluster.epoch.load(Relaxed));
2200    let mine = map.nodes[0].epoch;
2201    if mine != 0 && mine == highest {
2202        return false;
2203    }
2204    map.nodes[0].epoch = server.cluster.epoch.fetch_add(1, Relaxed) + 1;
2205    true
2206}
2207
2208/// `CLUSTER FLUSHSLOTS`, which drops every slot this node claims.
2209fn flushslots(server: &Server, out: &mut Out) -> Result<()> {
2210    if server.dbs.iter().any(|db| !db.is_empty()) {
2211        return Err(Error::new(
2212            Code::Invalid,
2213            "DB must be empty to perform CLUSTER FLUSHSLOTS.",
2214        ));
2215    }
2216    {
2217        let mut map = server.cluster.map.lock();
2218        for slot in 0..SLOTS {
2219            if map.owner[slot] == Some(0) {
2220                map.owner[slot] = None;
2221            }
2222            map.migrating[slot] = None;
2223            map.importing[slot] = None;
2224        }
2225    }
2226    server.recount_coverage();
2227    save(server)?;
2228    out.ok();
2229    Ok(())
2230}
2231
2232/// `CLUSTER BUMPEPOCH`, which takes an epoch above everybody else's.
2233///
2234/// Answers `BUMPED` and the new epoch when it moved and `STILL` and the current
2235/// one when there was nothing to outrank, which is what a second call in a row
2236/// gets. See [`bump_without_consensus`] for why doing nothing is the right
2237/// answer rather than a wasted epoch.
2238fn bumpepoch(server: &Server, out: &mut Out) -> Result<()> {
2239    let (moved, epoch) = {
2240        let mut map = server.cluster.map.lock();
2241        let moved = bump_without_consensus(server, &mut map);
2242        (moved, map.nodes[0].epoch)
2243    };
2244    if moved {
2245        save(server)?;
2246        server.cluster_broadcast_pong();
2247    }
2248    let word = if moved { "BUMPED" } else { "STILL" };
2249    let text = yo_alloc::allow(|| format!("{word} {epoch}"));
2250    out.simple(text.as_bytes());
2251    Ok(())
2252}
2253
2254/// `CLUSTER SET-CONFIG-EPOCH`, which is how a brand new cluster gives each node
2255/// a different epoch before anything has been agreed.
2256fn set_config_epoch(server: &Server, args: Args<'_>, out: &mut Out) -> Result<()> {
2257    let epoch = args.int(2)?;
2258    if epoch < 0 {
2259        return Err(Error::fmt(
2260            Code::Invalid,
2261            format_args!("Invalid config epoch specified: {epoch}"),
2262        ));
2263    }
2264    {
2265        let mut map = server.cluster.map.lock();
2266        if map.nodes[0].epoch != 0 {
2267            return Err(Error::new(
2268                Code::Invalid,
2269                "Node config epoch is already non-zero",
2270            ));
2271        }
2272        map.nodes[0].epoch = epoch as u64;
2273    }
2274    let epoch = epoch as u64;
2275    server.cluster.epoch.fetch_max(epoch, Relaxed);
2276    save(server)?;
2277    out.ok();
2278    Ok(())
2279}
2280
2281/// `CLUSTER RESET [HARD|SOFT]`, which gives up every slot and, when hard, takes
2282/// a new identity as well.
2283fn reset(server: &Server, args: Args<'_>, out: &mut Out) -> Result<()> {
2284    let hard = match args.opt(2) {
2285        None => false,
2286        Some(word) if args::is(word, b"hard") => true,
2287        Some(word) if args::is(word, b"soft") => false,
2288        Some(_) => return Err(args::syntax()),
2289    };
2290    if server.dbs.iter().any(|db| !db.is_empty()) {
2291        return Err(Error::new(
2292            Code::Invalid,
2293            "CLUSTER RESET can't be called with master nodes containing keys",
2294        ));
2295    }
2296    {
2297        let mut map = server.cluster.map.lock();
2298        for slot in 0..SLOTS {
2299            map.owner[slot] = None;
2300            map.migrating[slot] = None;
2301            map.importing[slot] = None;
2302        }
2303        map.nodes.truncate(1);
2304        map.nodes[0].epoch = 0;
2305        // A reset makes this node a master of nothing again, which is the half
2306        // of it a soft reset does as well: it stops following anybody and it
2307        // forgets everybody, it just keeps its name.
2308        map.nodes[0].flags = FLAG_MYSELF | FLAG_MASTER;
2309        map.nodes[0].master = None;
2310        if hard {
2311            yo_alloc::allow(|| {
2312                map.nodes[0].id = String::from_utf8_lossy(&new_id()).into_owned();
2313                map.nodes[0].shard = String::from_utf8_lossy(&new_id()).into_owned();
2314            });
2315        }
2316    }
2317    if hard {
2318        server.cluster.epoch.store(0, Relaxed);
2319    }
2320    server.recount_coverage();
2321    save(server)?;
2322    out.ok();
2323    Ok(())
2324}
2325
2326// -------------------------------------------------------------- the key sets
2327
2328/// `CLUSTER COUNTKEYSINSLOT`.
2329fn count_keys(server: &Server, at: usize, args: Args<'_>, out: &mut Out) -> Result<()> {
2330    let n = args.int(2)?;
2331    let slot = u16::try_from(n)
2332        .ok()
2333        .filter(|s| usize::from(*s) < SLOTS)
2334        .ok_or_else(|| Error::new(Code::Invalid, "Invalid slot"))?;
2335    let mut found = 0i64;
2336    server.dbs[at].keys(|key| {
2337        if key_slot(key) == slot {
2338            found += 1;
2339        }
2340    });
2341    out.int(found);
2342    Ok(())
2343}
2344
2345/// `CLUSTER GETKEYSINSLOT`.
2346fn get_keys(server: &Server, at: usize, args: Args<'_>, out: &mut Out) -> Result<()> {
2347    let slot = args.int(2)?;
2348    let count = args.int(3)?;
2349    let bad = || Error::new(Code::Invalid, "Invalid slot or number of keys");
2350    if !(0..SLOTS as i64).contains(&slot) || count < 0 {
2351        return Err(bad());
2352    }
2353    let slot = slot as u16;
2354    let want = count as usize;
2355    let start = out.len();
2356    let mut n = 0;
2357    server.dbs[at].keys(|key| {
2358        if n < want && key_slot(key) == slot {
2359            out.bulk(key);
2360            n += 1;
2361        }
2362    });
2363    out.close_array(start, n);
2364    Ok(())
2365}
2366
2367// ------------------------------------------------------------- the disk copy
2368
2369/// Write the table where a restart will find it.
2370///
2371/// The format is the reference's, line for line, so a node's file can be read by
2372/// a real server and the other way round. The write goes to a temporary name and
2373/// is renamed over the real one, the same way the snapshot writer does it, so a
2374/// reader never sees half a file.
2375fn save(server: &Server) -> Result<()> {
2376    let path = {
2377        let file = server.cluster.file.lock();
2378        if file.is_empty() {
2379            return Ok(());
2380        }
2381        yo_alloc::allow(|| file.clone())
2382    };
2383    let epoch = server.cluster.epoch.load(Relaxed);
2384    let text = yo_alloc::allow(|| {
2385        let mut s = lines(server, true);
2386        let _ = writeln!(s, "vars currentEpoch {epoch} lastVoteEpoch 0");
2387        s
2388    });
2389    yo_alloc::allow(|| {
2390        let temp = format!("{path}.tmp");
2391        let wrote =
2392            std::fs::write(&temp, text.as_bytes()).and_then(|()| std::fs::rename(&temp, &path));
2393        match wrote {
2394            Ok(()) => Ok(()),
2395            Err(e) => Err(Error::fmt(
2396                Code::Invalid,
2397                format_args!("cluster config file could not be written: {e}"),
2398            )),
2399        }
2400    })
2401}
2402
2403impl Server {
2404    /// Read the table back off disk, which is what makes a restart a restart
2405    /// rather than a node that has forgotten which half of the keyspace is its.
2406    ///
2407    /// A file that is not there is not an error, because the first start of a
2408    /// brand new node has none. A file that is there and is nonsense is, because
2409    /// the alternative is a node that comes up owning nothing and starts
2410    /// answering `CLUSTERDOWN` for keys it has on disk.
2411    fn reload_cluster(&mut self) -> Result<()> {
2412        let path = self.cluster_file();
2413        if path.is_empty() {
2414            return Ok(());
2415        }
2416        let text = match yo_alloc::allow(|| std::fs::read_to_string(&path)) {
2417            Ok(text) => text,
2418            Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(()),
2419            Err(e) => {
2420                return Err(Error::fmt(Code::Invalid, format_args!("{path}: {e}")));
2421            }
2422        };
2423        yo_alloc::allow(|| self.absorb_cluster(&text))
2424    }
2425
2426    /// The parse, split out so that a test can hand it text without a file.
2427    fn absorb_cluster(&mut self, text: &str) -> Result<()> {
2428        let bad = |what: &str| Error::fmt(Code::Invalid, format_args!("{what} in cluster config"));
2429        let now = self.now_ms();
2430        let mut nodes: Vec<Node> = Vec::new();
2431        // Slot runs are kept against the owner's id and not against its position
2432        // in the list, because this node moves to the front of the list when its
2433        // own line turns up and every position recorded before that moves down.
2434        let mut owned: Vec<(String, u16, u16)> = Vec::new();
2435        // The master field names a node that may not have been read yet, so
2436        // both ends are kept as ids and resolved once every line is in.
2437        let mut follows: Vec<(String, String)> = Vec::new();
2438        for line in text.lines() {
2439            let line = line.trim();
2440            if line.is_empty() {
2441                continue;
2442            }
2443            let mut words = line.split(' ');
2444            let first = words.next().unwrap_or_default();
2445            if first == "vars" {
2446                // `vars currentEpoch <n> lastVoteEpoch <n>`, read as pairs so
2447                // that a build which adds a third one does not break the parse.
2448                let rest: Vec<&str> = words.collect();
2449                for pair in rest.chunks(2) {
2450                    if pair.len() == 2 && pair[0] == "currentEpoch" {
2451                        let epoch = pair[1].parse::<u64>().map_err(|_| bad("bad epoch"))?;
2452                        self.cluster.epoch.store(epoch, Relaxed);
2453                    }
2454                }
2455                continue;
2456            }
2457            if first.len() != ID_LEN {
2458                return Err(bad("bad node id"));
2459            }
2460            let address = words.next().ok_or_else(|| bad("missing address"))?;
2461            let named = words.next().ok_or_else(|| bad("missing flags"))?;
2462            let master = words.next().ok_or_else(|| bad("missing master"))?;
2463            let ping = words.next().ok_or_else(|| bad("missing ping time"))?;
2464            let pong = words.next().ok_or_else(|| bad("missing pong time"))?;
2465            let epoch = words
2466                .next()
2467                .and_then(|w| w.parse::<u64>().ok())
2468                .ok_or_else(|| bad("bad config epoch"))?;
2469            // The link state field, which is read past rather than read.
2470            let _link = words.next();
2471            let (host, port, bus, shard) =
2472                split_address(address).ok_or_else(|| bad("bad address"))?;
2473            for word in words {
2474                // The migrating and importing markers are re-read from the
2475                // brackets below rather than here, since they name a node that
2476                // may not have been read yet.
2477                if word.starts_with('[') {
2478                    continue;
2479                }
2480                let (from, to) = match word.split_once('-') {
2481                    Some((a, b)) => (
2482                        a.parse::<u16>().map_err(|_| bad("bad slot"))?,
2483                        b.parse::<u16>().map_err(|_| bad("bad slot"))?,
2484                    ),
2485                    None => {
2486                        let one = word.parse::<u16>().map_err(|_| bad("bad slot"))?;
2487                        (one, one)
2488                    }
2489                };
2490                if usize::from(from) >= SLOTS || usize::from(to) >= SLOTS || from > to {
2491                    return Err(bad("slot out of range"));
2492                }
2493                owned.push((first.to_owned(), from, to));
2494            }
2495            let mut flags = 0u16;
2496            for name in named.split(',') {
2497                flags |= match name {
2498                    "myself" => FLAG_MYSELF,
2499                    "master" => FLAG_MASTER,
2500                    "slave" => FLAG_SLAVE,
2501                    "fail?" => FLAG_PFAIL,
2502                    "fail" => FLAG_FAIL,
2503                    "handshake" => FLAG_HANDSHAKE,
2504                    "noaddr" => FLAG_NOADDR,
2505                    "nofailover" => FLAG_NOFAILOVER,
2506                    _ => 0,
2507                };
2508            }
2509            if master != "-" {
2510                if master.len() != ID_LEN {
2511                    return Err(bad("bad master id"));
2512                }
2513                follows.push((first.to_owned(), master.to_owned()));
2514            }
2515            let node = Node {
2516                id: first.to_owned(),
2517                host,
2518                port,
2519                bus,
2520                shard,
2521                epoch,
2522                flags,
2523                master: None,
2524                // The two timestamps come back as now rather than as what the
2525                // file says, because a file written an hour ago would otherwise
2526                // put every node in it straight past the failure timeout. All
2527                // the file is really recording is whether a ping was in flight.
2528                ping_sent: stamp(ping, now),
2529                pong_recv: stamp(pong, now),
2530                data_recv: now,
2531                fail_time: 0,
2532                offset: 0,
2533                // The link state in the file is written for a human reading it
2534                // and is not read back. There is no link to anybody at startup,
2535                // and saying otherwise would stop the bus ever dialling out.
2536                linked: false,
2537                reports: Vec::new(),
2538            };
2539            if named.split(',').any(|f| f == "myself") {
2540                // This node goes first, and everything already read moves down.
2541                nodes.insert(0, node);
2542            } else {
2543                nodes.push(node);
2544            }
2545        }
2546        if nodes.is_empty() {
2547            return Ok(());
2548        }
2549        let mut map = Map::new(nodes.remove(0));
2550        map.nodes.append(&mut nodes);
2551        for (id, from, to) in owned {
2552            let Some(at) = map.find(id.as_bytes()) else {
2553                return Err(bad("slots for a node nobody knows"));
2554            };
2555            for slot in from..=to {
2556                map.owner[usize::from(slot)] = Some(at);
2557            }
2558        }
2559        for (who, whose) in follows {
2560            let (Some(who), Some(whose)) = (map.find(who.as_bytes()), map.find(whose.as_bytes()))
2561            else {
2562                return Err(bad("master id nobody knows"));
2563            };
2564            map.nodes[usize::from(who)].master = Some(whose);
2565            // A replica's config epoch is its master's business, so the file's
2566            // value is dropped rather than believed. Keeping it would let a
2567            // replica that was a master an hour ago outvote the node that took
2568            // its slots the moment it came back.
2569            map.nodes[usize::from(who)].epoch = 0;
2570        }
2571        *self.cluster.map.lock() = map;
2572        Ok(())
2573    }
2574}
2575
2576/// One of the two timestamps out of the node table, read as a yes or a no.
2577///
2578/// The reference does the same thing for the same reason. The number in the
2579/// file was taken from a clock that has since moved on, and all the reader can
2580/// usefully learn from it is whether there was a ping in flight when the file
2581/// was written, so a number that is not zero comes back as the time now.
2582fn stamp(field: &str, now: u64) -> u64 {
2583    match field.parse::<u64>() {
2584        Ok(0) | Err(_) => 0,
2585        Ok(_) => now,
2586    }
2587}
2588
2589/// Pull the host, the port and the shard id out of one `ip:port@bus,aux` field.
2590///
2591/// The aux fields after the first comma are a list of `name=value` pairs with an
2592/// optional hostname in front of them, and a build that adds one has to leave a
2593/// build that does not able to read the file, so everything but the shard id is
2594/// skipped rather than being counted.
2595fn split_address(field: &str) -> Option<(String, u16, u16, String)> {
2596    let (address, aux) = match field.split_once(',') {
2597        Some((address, aux)) => (address, aux),
2598        None => (field, ""),
2599    };
2600    let (host, ports) = address.rsplit_once(':')?;
2601    let (client, bus) = match ports.split_once('@') {
2602        Some((client, bus)) => (client, bus.parse::<u16>().ok()?),
2603        None => (ports, 0),
2604    };
2605    let port = client.parse::<u16>().ok()?;
2606    let bus = if bus == 0 { port + BUS_OFFSET } else { bus };
2607    let shard = aux
2608        .split(',')
2609        .find_map(|pair| pair.strip_prefix("shard-id="))
2610        .map_or_else(
2611            || String::from_utf8_lossy(&new_id()).into_owned(),
2612            str::to_owned,
2613        );
2614    Some((host.to_owned(), port, bus, shard))
2615}
2616
2617// ------------------------------------------------------- the hand filled map
2618
2619/// What a test uses to fill the table the bus will fill later.
2620///
2621/// A single node cannot make a redirection happen: it owns every slot or it
2622/// owns none, and either way there is nowhere to send anybody. So the tests for
2623/// `MOVED`, `ASK` and `TRYAGAIN` put a second node in the table by hand, which
2624/// is exactly the state the bus will produce and is the state the config file
2625/// parser already reads.
2626#[cfg(test)]
2627impl Server {
2628    /// Take every slot, and be up about it straight away.
2629    pub(super) fn cluster_own_everything(&self) {
2630        {
2631            let mut map = self.cluster.map.lock();
2632            for slot in 0..SLOTS {
2633                map.owner[slot] = Some(0);
2634            }
2635        }
2636        self.recount_coverage();
2637        self.cluster.was_down.store(false, Relaxed);
2638        self.cluster.booted_at.store(0, Relaxed);
2639    }
2640
2641    /// Put a node in the table that nobody has met, and answer its index.
2642    pub(super) fn cluster_pretend_node(&self, id: &str, host: &str, port: u16) -> u16 {
2643        let mut map = self.cluster.map.lock();
2644        let mut node = Node::new(
2645            id.to_owned(),
2646            host.to_owned(),
2647            port,
2648            port + BUS_OFFSET,
2649            FLAG_MASTER,
2650            0,
2651        );
2652        node.shard = id.to_owned();
2653        node.linked = true;
2654        map.nodes.push(node);
2655        (map.nodes.len() - 1) as u16
2656    }
2657
2658    /// Hand one slot to another node.
2659    pub(super) fn cluster_hand_over(&self, slot: u16, node: u16) {
2660        let mut map = self.cluster.map.lock();
2661        map.owner[usize::from(slot)] = Some(node);
2662    }
2663
2664    /// Make this node a replica of another one in the table.
2665    ///
2666    /// What `CLUSTER REPLICATE` does to the table, without the half of it that
2667    /// starts replicating, which an embedded server has no way of doing and
2668    /// which none of the tests that want a replica in the table care about.
2669    pub(super) fn cluster_pretend_follower(&self, of: u16) {
2670        let mut map = self.cluster.map.lock();
2671        map.nodes[0].flags &= !FLAG_MASTER;
2672        map.nodes[0].flags |= FLAG_SLAVE;
2673        map.nodes[0].master = Some(of);
2674    }
2675
2676    /// Mark one slot as on its way out to, or in from, another node.
2677    pub(super) fn cluster_moving(&self, slot: u16, to: Option<u16>, from: Option<u16>) {
2678        let mut map = self.cluster.map.lock();
2679        map.migrating[usize::from(slot)] = to;
2680        map.importing[usize::from(slot)] = from;
2681    }
2682}
2683
2684// ---------------------------------------------------------------- the tests
2685
2686#[cfg(test)]
2687mod tests {
2688    use super::{SLOTS, key_slot};
2689
2690    /// The slots the reference answered for these keys, read off a running
2691    /// 8.10.1 with cluster mode on rather than worked out from the algorithm.
2692    #[test]
2693    fn a_key_lands_in_the_slot_a_real_server_puts_it_in() {
2694        assert_eq!(key_slot(b"foo"), 12182);
2695        assert_eq!(key_slot(b"1234"), 6025);
2696        assert_eq!(key_slot(b""), 0);
2697        assert_eq!(key_slot(b"{user1000}.following"), 3443);
2698    }
2699
2700    /// The hash tag is the whole reason a command can name two keys at all, and
2701    /// its edge cases are the ones a client library gets wrong.
2702    #[test]
2703    fn the_hash_tag_rules_are_the_reference_rules() {
2704        // Two keys with the same tag are one slot, whatever else they say.
2705        assert_eq!(
2706            key_slot(b"{user1000}.following"),
2707            key_slot(b"{user1000}.followers")
2708        );
2709        // A tag with nothing in it is not a tag.
2710        assert_eq!(key_slot(b"{}foo"), key_slot(b"{}foo"));
2711        assert_ne!(key_slot(b"{}foo"), key_slot(b"foo"));
2712        // An opening brace with no closing one is not a tag either.
2713        assert_ne!(key_slot(b"{foo"), key_slot(b"foo"));
2714        // Only the first closing brace after the first opening one counts.
2715        assert_eq!(key_slot(b"{a}{b}"), key_slot(b"a"));
2716        // A tag can hold a brace, since the search is for the first close.
2717        assert_eq!(key_slot(b"foo{{bar}}zap"), key_slot(b"{bar"));
2718    }
2719
2720    /// Every slot is reachable and none is out of range, which is the only
2721    /// property the command path actually leans on.
2722    #[test]
2723    fn every_slot_is_in_range() {
2724        let mut seen = vec![false; SLOTS];
2725        for i in 0..200_000u32 {
2726            let key = i.to_string();
2727            let slot = key_slot(key.as_bytes());
2728            assert!(usize::from(slot) < SLOTS);
2729            seen[usize::from(slot)] = true;
2730        }
2731        assert!(seen.iter().all(|s| *s), "200k keys reach all 16384 slots");
2732    }
2733
2734    /// Reading the node table back has to put every run of slots on the node
2735    /// that owns it, including when this node's own line is not the first one.
2736    ///
2737    /// It is worth a test of its own because the failure is silent and only
2738    /// shows up on a cluster with three nodes in it: this node moves to the
2739    /// front of the list when its line is read, everything read before it moves
2740    /// down one, and a run recorded against a position rather than against an
2741    /// id ends up on the wrong node.
2742    #[test]
2743    fn a_config_file_puts_every_run_on_the_node_that_owns_it() {
2744        let mut server = super::Server::new();
2745        server.enable_cluster("", 7355);
2746        let text = "\
27473b80b05445f38bc7214f083696a2bbf90e3f30e3 127.0.0.1:7356@17356 master - 0 0 0 connected 10923-16383
274830d0651b0ec5e178e082634c44fb9adcc6e4021b 127.0.0.1:7355@17355 myself,master - 0 0 1 connected 5461-10922
274919a9e69b8b66016ac43c55ccdeed0283e0148e17 127.0.0.1:7354@17354 master - 0 0 2 connected 0-5460
2750vars currentEpoch 2 lastVoteEpoch 0
2751";
2752        server.absorb_cluster(text).expect("the file parses");
2753        let map = server.cluster.map.lock();
2754        let at = |id: &str| map.find(id.as_bytes()).expect("the node is in the table");
2755        assert_eq!(at("30d0651b0ec5e178e082634c44fb9adcc6e4021b"), 0, "myself");
2756        for (id, from, to) in [
2757            ("19a9e69b8b66016ac43c55ccdeed0283e0148e17", 0, 5460),
2758            ("30d0651b0ec5e178e082634c44fb9adcc6e4021b", 5461, 10922),
2759            ("3b80b05445f38bc7214f083696a2bbf90e3f30e3", 10923, 16383),
2760        ] {
2761            let owner = Some(at(id));
2762            for slot in from..=to {
2763                assert_eq!(map.owner[slot], owner, "slot {slot} belongs to {id}");
2764            }
2765        }
2766    }
2767
2768    /// Three fields in the node table say what was true when it was written and
2769    /// not what is true now, and reading them back as if they were live state
2770    /// is what stops a restarted node ever dialling anybody again.
2771    #[test]
2772    fn a_config_file_is_not_read_back_as_live_state() {
2773        let mut server = super::Server::new();
2774        server.enable_cluster("", 7357);
2775        let text = "\
27769fcbb7624dedbb2fd0020dd2fbf86a5eb8cec31b 127.0.0.1:7355@17355 master - 0 1789005531306 3 connected 5461-10922
2777ac6dd51a69741dc5130637c594866c9b7e0cfc4e 127.0.0.1:7357@17357 myself,slave 9fcbb7624dedbb2fd0020dd2fbf86a5eb8cec31b 1789005400000 1789005532315 3 connected
2778";
2779        server.absorb_cluster(text).expect("the file parses");
2780        let now = server.now_ms();
2781        let map = server.cluster.map.lock();
2782        for node in &map.nodes {
2783            assert!(!node.linked, "nothing is linked before the bus dials out");
2784        }
2785        // A ping that was in flight comes back as one sent now, and a ping that
2786        // was not stays at zero, which is the difference the cron reads.
2787        assert_eq!(map.nodes[0].ping_sent, now, "myself had a ping in flight");
2788        assert_eq!(map.nodes[1].ping_sent, 0, "the master did not");
2789        assert_eq!(map.nodes[0].pong_recv, now);
2790        // And a replica's config epoch belongs to its master, not to the file.
2791        assert_eq!(map.nodes[0].epoch, 0, "the replica's epoch is dropped");
2792        assert_eq!(map.nodes[1].epoch, 3, "the master's is kept");
2793    }
2794}