Skip to main content

yo_resp/dispatch/
repl.rs

1//! Being a master: the replication identity, the stream and the link.
2//!
3//! A replica is a second copy of this server's keyspace that keeps up by being
4//! told every change as it happens. Getting there is two halves. First it needs
5//! the dataset as it stands, which is a snapshot sent down the socket, and then
6//! it needs everything that happens after that snapshot, which is a stream of
7//! commands that never ends. The hard part is the seam between the two, because
8//! a change that lands in neither is lost forever and a change that lands in
9//! both is applied twice, and applying `INCR` twice is not the same as applying
10//! it once.
11//!
12//! # The handshake
13//!
14//! A replica opens an ordinary connection and sends `PING`, then
15//! `REPLCONF listening-port <port>`, then `REPLCONF capa ...`, and then `PSYNC
16//! <replid> <offset>`. Everything up to the `PSYNC` is a normal command with a
17//! normal reply. The `PSYNC` is where the connection stops being a client: the
18//! master answers `+FULLRESYNC <replid> <offset>`, then the snapshot as a bulk
19//! string with no trailing newline after it, and from then on writes only the
20//! command stream and never replies to anything the replica sends. A replica
21//! sends `REPLCONF ACK <offset>` about once a second forever, and a master that
22//! answers one of those with `+OK` has put a reply into the middle of a stream
23//! the replica is parsing as commands, which a real replica reports as
24//! `Protocol error (Master using the inline protocol. Desync?)` and then drops
25//! the link. So the silence is not an optimisation, it is the protocol.
26//!
27//! # The identity and the offset
28//!
29//! The replication id is forty hex characters naming this server's history, and
30//! the offset is how many bytes of stream have gone out under that id. Together
31//! they are a position in a history, which is what lets a replica that lost the
32//! link for a moment ask to carry on rather than start again: it sends back the
33//! id and offset it had, and if the id is still ours and the offset is still in
34//! the backlog the master answers `+CONTINUE` and replays the missing bytes.
35//!
36//! There is a second id for the same reason a chain needs one. When a replica is
37//! promoted it keeps the old master's id as `replid2` and takes a new one of its
38//! own, so the replicas that were following the old master can be handed over
39//! without all of them resyncing from nothing.
40//!
41//! # The snapshot has to be one instant
42//!
43//! `SAVE` here walks the databases one stripe at a time and does not stop the
44//! server, so the file it writes is not one instant of the keyspace: a write to
45//! database nine while database two is being walked is in the file and a write
46//! to database two after it has been walked is not. For a file on disk that is a
47//! fair trade, because the file is read by itself and nothing is going to be
48//! replayed on top of it.
49//!
50//! It is not a fair trade here. The replica loads the snapshot and then applies
51//! the stream from the offset the snapshot was stamped with, so every byte of
52//! the keyspace has to be either in the snapshot or after that offset, and never
53//! both and never neither. A real server gets that for free by forking, which
54//! hands the child an instant of the whole address space and costs the parent
55//! nothing but the page faults afterwards. There is no fork here, so a full
56//! resync takes every stripe of every database at once, stamps the offset,
57//! builds the image and lets go. The server stops for as long as that takes,
58//! which is a real cost on a large dataset and is registered as a divergence.
59//! The alternative is a replica that is quietly wrong, which is worse than a
60//! replica that took a pause to be right.
61//!
62//! # What goes on the stream
63//!
64//! Every command that changed something, in the form the replica has to be given
65//! rather than the form the client sent. Most commands are the same both ways.
66//! The ones that are not are the ones whose result depends on something the
67//! replica has not got: the clock, for anything that sets a deadline, and the
68//! server's own random state, for `SPOP`. `EXPIRE k 50` becomes `PEXPIREAT k
69//! <absolute>`, `SET k v EX 100` becomes `SET k v PXAT <absolute>`, `SPOP s`
70//! becomes `SREM s <the member that actually went>`, and `XADD s *` becomes an
71//! `XADD` naming the id that was actually made. Without the rewrite the two
72//! copies drift apart the moment either of them is asked a question.
73//!
74//! The rewrite is pushed by the command that knows, through a thread local, the
75//! same way this crate already hands keyspace events up from underneath. A
76//! command that pushes nothing is sent as it arrived, which is the common case
77//! and costs nothing to decide.
78//!
79//! # What it costs a server with no replica
80//!
81//! One relaxed load of a count that is zero, per write command. The same shape
82//! `MONITOR` and the pub/sub registry use, and for the same reason: nearly every
83//! server in the world is running on its own.
84
85use std::sync::Arc;
86use std::sync::atomic::Ordering::{Acquire, Relaxed, Release};
87use std::sync::atomic::{AtomicBool, AtomicI64, AtomicU64, AtomicUsize};
88use std::time::Duration;
89
90use core::cell::{Cell, RefCell};
91
92use yo_common::lock::Lock;
93use yo_common::{Code, Error, Result};
94
95use crate::reply::Out;
96
97use super::args::{self, Args};
98use super::clients::{self, Client};
99use super::pubsub::Envelope;
100use super::{Server, Session};
101
102/// How many characters name a history, which is Redis's forty hex digits.
103pub(super) const ID_LEN: usize = 40;
104
105/// The id a server that has never been anybody's replica reports as its second,
106/// which Redis writes as forty zeroes rather than leaving out.
107const NO_ID: &[u8; ID_LEN] = b"0000000000000000000000000000000000000000";
108
109/// How much stream is kept for a replica that dropped the link, in bytes.
110///
111/// Redis's default, and the number `repl-backlog-size` reads back as. A replica
112/// that reconnects inside this many bytes carries on where it was and one that
113/// falls further behind starts again from a snapshot.
114pub(super) const BACKLOG_BYTES: usize = 1024 * 1024;
115
116/// One connection that has stopped being a client and is now a copy of us.
117pub(super) struct Replica {
118    /// The row every other part of the server knows the connection by, so that
119    /// `CLIENT LIST` and `CLIENT KILL` see a replica the way they see anything
120    /// else.
121    row: Arc<Client>,
122    /// The port the replica says it is listening on, which is what `INFO`
123    /// reports rather than the port its outgoing connection came from. A replica
124    /// that has not said is reported as zero, which is what Redis does too.
125    port: AtomicU64,
126    /// The last offset it said it had, and when it said so.
127    ///
128    /// `WAIT` counts the replicas whose acknowledged offset has caught up, and
129    /// `INFO` turns the time into a lag in seconds.
130    ack: AtomicU64,
131    ack_ms: AtomicU64,
132    /// Whether the snapshot has gone and the stream has started.
133    online: AtomicBool,
134}
135
136impl Replica {
137    /// The address the replica is reachable at, which is its address and the
138    /// port it told us rather than the port it dialled out from.
139    fn address(&self) -> (String, u64) {
140        let text = self.row.text.lock();
141        let peer = text.peer.clone();
142        drop(text);
143        let host = match peer.iter().rposition(|&b| b == b':') {
144            Some(at) => peer[..at].to_vec(),
145            None => peer.clone(),
146        };
147        (
148            String::from_utf8_lossy(&host).into_owned(),
149            self.port.load(Relaxed),
150        )
151    }
152}
153
154/// Everything about being a master, all of it idle on a server with no replica.
155pub(crate) struct Replication {
156    /// The forty characters naming the history this server is writing.
157    id: Lock<[u8; ID_LEN]>,
158    /// The history it was writing before, for a server that was promoted.
159    id2: Lock<[u8; ID_LEN]>,
160    /// How many bytes of stream have been produced under [`Replication::id`].
161    offset: AtomicU64,
162    /// The offset the second id runs up to, or minus one when there is no
163    /// second id, which is what Redis reports on a server that was never a
164    /// replica.
165    second: AtomicI64,
166    /// What has gone out lately, so a replica that blinked can catch up without
167    /// a whole snapshot.
168    backlog: Lock<Backlog>,
169    /// The connections being fed.
170    rows: Lock<Vec<Arc<Replica>>>,
171    /// How many there are, so a write command can ask without taking the lock.
172    live: AtomicUsize,
173    /// The port a connection said it was listening on, before it asked to
174    /// become a replica.
175    ///
176    /// `REPLCONF listening-port` arrives two commands before `PSYNC`, so there
177    /// is no replica to hang it on yet and it has to be kept somewhere until
178    /// there is. Here rather than on the connection row, because a row is paid
179    /// for by every connection on the server and this is paid for only by the
180    /// few that are about to become replicas. Oldest first out at a small cap,
181    /// so a client that sends the one and never the other cannot grow it.
182    ports: Lock<Vec<(u64, u64)>>,
183    /// Whether a full resync is building an image right now.
184    ///
185    /// A write that arrives while this is set is held and run again afterwards,
186    /// which is what makes the image one instant. Read on the command path by
187    /// every write, so it is a relaxed load of a bool that is nearly always
188    /// false, next to the one `CLIENT PAUSE` already costs.
189    frozen: AtomicBool,
190    /// One resync builds at a time.
191    ///
192    /// Two threads freezing the server at once would each wait for the other's
193    /// writes to drain and neither would be able to, so the second one waits
194    /// here instead and then finds a backlog it can probably be caught up from.
195    building: Lock<()>,
196    /// Which database the stream is on, so `SELECT` goes out only when it has
197    /// to. Minus one before anything has been written, which is why the first
198    /// command on any database is preceded by a `SELECT` even for database
199    /// zero.
200    on_db: AtomicI64,
201    /// When something last went out on the stream, so the periodic `PING` a
202    /// master owes its replicas is only sent on a link that has gone quiet.
203    last_io: AtomicU64,
204}
205
206impl Default for Replication {
207    fn default() -> Replication {
208        Replication {
209            id: Lock::new(make_id()),
210            id2: Lock::new(*NO_ID),
211            offset: AtomicU64::new(0),
212            second: AtomicI64::new(-1),
213            backlog: Lock::new(Backlog::default()),
214            rows: Lock::new(Vec::new()),
215            live: AtomicUsize::new(0),
216            ports: Lock::new(Vec::new()),
217            frozen: AtomicBool::new(false),
218            building: Lock::new(()),
219            on_db: AtomicI64::new(-1),
220            last_io: AtomicU64::new(0),
221        }
222    }
223}
224
225/// The last stretch of stream, kept so a reconnect does not cost a snapshot.
226///
227/// A ring of a fixed size with the offset of its first byte beside it. `histlen`
228/// is how much of the ring is real, which is less than its size only until it
229/// has filled once.
230#[derive(Default)]
231struct Backlog {
232    ring: Vec<u8>,
233    /// Where the next byte goes.
234    at: usize,
235    /// How many bytes of the ring are real.
236    filled: usize,
237    /// The stream offset of the oldest real byte.
238    first: u64,
239}
240
241impl Backlog {
242    /// Take bytes in, dropping whatever falls off the back.
243    fn push(&mut self, bytes: &[u8], upto: u64) {
244        if self.ring.is_empty() {
245            self.ring = vec![0; BACKLOG_BYTES];
246            self.first = upto - bytes.len() as u64;
247        }
248        for &b in bytes {
249            self.ring[self.at] = b;
250            self.at = (self.at + 1) % BACKLOG_BYTES;
251            if self.filled < BACKLOG_BYTES {
252                self.filled += 1;
253            }
254        }
255        self.first = upto - self.filled as u64;
256    }
257
258    /// Everything from `from` onwards, or `None` if that much history has gone.
259    fn since(&self, from: u64, upto: u64) -> Option<Vec<u8>> {
260        if self.filled == 0 || from < self.first || from > upto {
261            return None;
262        }
263        let skip = (from - self.first) as usize;
264        let want = self.filled - skip;
265        let start = (self.at + BACKLOG_BYTES - self.filled + skip) % BACKLOG_BYTES;
266        let mut out = Vec::with_capacity(want);
267        for i in 0..want {
268            out.push(self.ring[(start + i) % BACKLOG_BYTES]);
269        }
270        Some(out)
271    }
272}
273
274#[cfg(test)]
275impl Server {
276    /// Say there is a replica, without one.
277    ///
278    /// What a test wants to look at is the byte stream and not the socket it
279    /// would have gone down, and [`emit`] writes the stream into the backlog
280    /// before it looks for anybody to post it to. So a server told this writes
281    /// everything a real master would write and posts none of it, which is the
282    /// whole of what these tests need and needs no connection at all.
283    pub(super) fn pretend_replica(&self) {
284        self.repl.live.store(1, Relaxed);
285    }
286
287    /// The stream from `from` onwards, as text, and where it has got to.
288    ///
289    /// Text because every rewrite these tests are about is text, and reading
290    /// `*3\r\n$3\r\nSET\r\n` in a failure message beats reading a byte array.
291    pub(super) fn stream_since(&self, from: u64) -> (String, u64) {
292        let upto = self.repl.offset.load(Acquire);
293        let backlog = self.repl.backlog.lock();
294        let bytes = backlog.since(from, upto).unwrap_or_default();
295        (String::from_utf8_lossy(&bytes).into_owned(), upto)
296    }
297}
298
299/// Whether a replica is the one at this host and port.
300///
301/// The host is compared as text and not as an address, which is Redis's own
302/// rule: what an operator types has to be what the server prints in `INFO`,
303/// because that is where they read it from.
304fn at(held: &Replica, host: &str, port: u16) -> bool {
305    let (theirs, their_port) = held.address();
306    theirs == host && their_port == u64::from(port)
307}
308
309/// Forty hex characters from the operating system's generator.
310///
311/// From the system and not from the engine's own seeded one, because two servers
312/// started from the same image at the same moment must not be able to claim the
313/// same history. That is the same reasoning `ACL GENPASS` is built on.
314fn make_id() -> [u8; ID_LEN] {
315    const HEX: &[u8; 16] = b"0123456789abcdef";
316    let mut raw = [0u8; ID_LEN / 2];
317    yo_common::entropy::fill(&mut raw);
318    let mut id = [0u8; ID_LEN];
319    for (i, byte) in raw.iter().enumerate() {
320        id[i * 2] = HEX[usize::from(byte >> 4)];
321        id[i * 2 + 1] = HEX[usize::from(byte & 15)];
322    }
323    id
324}
325
326// ------------------------------------------------------------- the override
327
328thread_local! {
329    /// What the running command wants sent instead of itself, if anything.
330    ///
331    /// A command that leaves this empty is propagated as it arrived. One that
332    /// puts something here is propagated as whatever it put, however many
333    /// commands that is, and a command that puts an empty list here is
334    /// propagated as nothing at all. See the module header for why a thread
335    /// local rather than a return value.
336    static INSTEAD: RefCell<Option<Vec<Vec<Vec<u8>>>>> = const { RefCell::new(None) };
337}
338
339thread_local! {
340    /// Whether anything this thread does has somewhere to be copied to.
341    ///
342    /// A flag rather than a question put to the server, because the call sites
343    /// that ask are inside the command bodies, a long way from anything holding
344    /// a `Server`, and threading one down to them for a question that is nearly
345    /// always no would be a worse trade than a thread local read. The same trade
346    /// the keyspace events make, next door.
347    ///
348    /// It says whether there is a replica and not whether a write is running,
349    /// which are two different questions and this is the wider one on purpose. A
350    /// read takes keys away, because a lookup that finds one past its deadline
351    /// removes it there and then, and `XGROUP CREATE` changes a stream while
352    /// carrying no write flag, since the flag is on the subcommand and there is
353    /// no subcommand table yet. Both have something to send and neither is a
354    /// write by the funnel's reckoning, so the funnel decides whether the
355    /// verbatim command may be sent and this decides whether anybody is
356    /// listening at all.
357    static ARMED: Cell<bool> = const { Cell::new(false) };
358
359    /// The removals nobody asked for, waiting for a lock that is not held.
360    static REAPED: RefCell<Vec<Vec<Vec<u8>>>> = const { RefCell::new(Vec::new()) };
361}
362
363/// Say whether what runs next is going to be copied, answering what the last
364/// answer was.
365///
366/// Set in front of every write on a server with a replica and put back
367/// afterwards, so that a command run by a script or by `EXEC` leaves the flag
368/// the way it found it.
369pub(super) fn arm(on: bool) -> bool {
370    ARMED.replace(on)
371}
372
373/// Whether a rewrite would be heard, which is what keeps every call site that
374/// would build one off the path of a server with no replica.
375#[must_use]
376pub(crate) fn armed() -> bool {
377    ARMED.get()
378}
379
380/// Send this instead of the command that is running.
381///
382/// Called by a command whose own arguments do not say what happened, so more
383/// than once for a command with more than one effect. The first call replaces
384/// the command and the rest are added after it.
385pub(crate) fn instead(parts: Vec<Vec<u8>>) {
386    INSTEAD.with(|cell| {
387        let mut held = cell.borrow_mut();
388        held.get_or_insert_with(Vec::new).push(parts);
389    });
390}
391
392/// The same thing said in pieces, which is how nearly every call site has it.
393///
394/// A rewrite is a command name and a key and usually a number, none of which
395/// arrive owned, so this is the shape that keeps the copying in one place rather
396/// than a `to_vec` on every argument of every site.
397pub(crate) fn rewrite(parts: &[&[u8]]) {
398    instead(parts.iter().map(|part| part.to_vec()).collect());
399}
400
401/// Send nothing at all for the command that is running.
402///
403/// For a write command that turned out to change nothing and whose verbatim form
404/// would be wrong rather than merely wasteful, which is any of the ones that are
405/// rewritten: a `SPOP` on a missing key has no `SREM` to send and must not send
406/// the `SPOP`.
407pub(crate) fn nothing() {
408    INSTEAD.with(|cell| {
409        let mut held = cell.borrow_mut();
410        held.get_or_insert_with(Vec::new);
411    });
412}
413
414/// Take whatever the command left, clearing it for the next one.
415fn taken() -> Option<Vec<Vec<Vec<u8>>>> {
416    INSTEAD.with(|cell| cell.borrow_mut().take())
417}
418
419// --------------------------------------------------------------- the server
420
421impl Server {
422    /// Whether anybody is being fed, which is the whole cost of this file on a
423    /// server that is on its own.
424    #[must_use]
425    pub(crate) fn replicated(&self) -> bool {
426        self.repl.live.load(Relaxed) != 0
427    }
428
429    /// How many of them there are, which `INFO clients` takes off its count.
430    ///
431    /// A replica is a connection and is not a client: Redis counts the sockets
432    /// and then subtracts the replicas, so `connected_clients` is the number of
433    /// people talking to the server rather than the number of file descriptors
434    /// it is holding, and the replicas are reported next door as their own
435    /// number.
436    #[must_use]
437    pub(crate) fn replica_count(&self) -> u64 {
438        self.repl.live.load(Relaxed) as u64
439    }
440
441    /// The forty characters naming this server's history.
442    pub(crate) fn repl_id(&self) -> [u8; ID_LEN] {
443        *self.repl.id.lock()
444    }
445
446    /// How many bytes of stream have gone out.
447    #[must_use]
448    pub(crate) fn repl_offset(&self) -> u64 {
449        self.repl.offset.load(Acquire)
450    }
451
452    /// Whether a replica is attached at this address, and whether it is past its
453    /// snapshot, which are the two questions `FAILOVER TO` asks in that order.
454    ///
455    /// `None` is nobody there and is a different answer from `Some(false)`,
456    /// which is somebody there who is still loading. The address is matched the
457    /// way Redis matches it, against where the connection came from and against
458    /// the port the replica said it listens on rather than the port it dialled
459    /// out from, because the second one is not something an operator can type.
460    #[must_use]
461    pub(super) fn replica_online_at(&self, host: &str, port: u16) -> Option<bool> {
462        self.replica_rows()
463            .iter()
464            .find(|held| at(held, host, port))
465            .map(|held| held.online.load(Relaxed))
466    }
467
468    /// Whether the replica at this address has acknowledged every byte written.
469    ///
470    /// The wait a failover is, put as one question. A replica that has gone away
471    /// answers no rather than yes, which is what keeps a failover to a target
472    /// that died waiting rather than handing the job to nobody.
473    #[must_use]
474    pub(super) fn replica_caught_up(&self, host: &str, port: u16) -> bool {
475        let upto = self.repl_offset();
476        self.replica_rows()
477            .iter()
478            .any(|held| at(held, host, port) && held.ack.load(Relaxed) >= upto)
479    }
480
481    /// Where the first replica that has everything is, for a failover that was
482    /// not told which one to hand the job to.
483    ///
484    /// In the order they attached, which is the order `INFO` lists them in, so
485    /// two operators reading the same server pick the same one.
486    #[must_use]
487    pub(super) fn first_caught_up(&self) -> Option<(String, u16)> {
488        let upto = self.repl_offset();
489        yo_alloc::allow(|| {
490            self.replica_rows()
491                .iter()
492                .filter(|held| held.online.load(Relaxed) && held.ack.load(Relaxed) >= upto)
493                .map(|held| held.address())
494                .find_map(|(host, port)| u16::try_from(port).ok().map(|port| (host, port)))
495        })
496    }
497
498    /// A handle to every replica, copied out so the bytes can be posted with the
499    /// lock let go of.
500    ///
501    /// The same trade `CLIENT LIST` and the monitor feed make, for the same
502    /// reason: a replica that drops while a command is being rendered leaves a
503    /// row that is still readable, and the bytes land in a mailbox that is about
504    /// to be told the slot has moved on.
505    fn replica_rows(&self) -> Vec<Arc<Replica>> {
506        let rows = self.repl.rows.lock();
507        yo_alloc::allow(|| rows.clone())
508    }
509
510    /// Remember the port a connection says it is listening on.
511    ///
512    /// See [`Replication::ports`] for why it cannot go straight on a replica.
513    fn note_replica_port(&self, id: u64, port: u64) {
514        const KEEP: usize = 64;
515        let mut ports = self.repl.ports.lock();
516        yo_alloc::allow(|| {
517            if let Some(row) = ports.iter_mut().find(|(who, _)| *who == id) {
518                row.1 = port;
519                return;
520            }
521            if ports.len() >= KEEP {
522                ports.remove(0);
523            }
524            ports.push((id, port));
525        });
526    }
527
528    /// Take the port back out, for a connection that has got as far as `PSYNC`.
529    fn take_replica_port(&self, id: u64) -> u64 {
530        let mut ports = self.repl.ports.lock();
531        match ports.iter().position(|(who, _)| *who == id) {
532            Some(at) => ports.remove(at).1,
533            None => 0,
534        }
535    }
536
537    /// Take a connection on as a replica.
538    fn take_replica(&self, row: &Arc<Client>) -> Arc<Replica> {
539        let mut rows = self.repl.rows.lock();
540        let held = yo_alloc::allow(|| {
541            let held = Arc::new(Replica {
542                row: Arc::clone(row),
543                port: AtomicU64::new(self.take_replica_port(row.id)),
544                ack: AtomicU64::new(0),
545                ack_ms: AtomicU64::new(self.clock.now_ms()),
546                online: AtomicBool::new(true),
547            });
548            rows.push(Arc::clone(&held));
549            held
550        });
551        self.repl.live.store(rows.len(), Release);
552        row.set_flag(clients::REPLICA, true);
553        self.note_here(row.thread.load(Relaxed), 1);
554        held
555    }
556
557    /// Let one go, which is the connection closing or being killed.
558    pub(crate) fn drop_replica(&self, id: u64) {
559        let mut rows = self.repl.rows.lock();
560        let Some(at) = rows.iter().position(|r| r.row.id == id) else {
561            return;
562        };
563        let gone = rows.remove(at);
564        self.repl.live.store(rows.len(), Release);
565        drop(rows);
566        gone.row.set_flag(clients::REPLICA, false);
567        self.note_here(gone.row.thread.load(Relaxed), -1);
568    }
569
570    /// Whether a full resync is holding the keyspace still.
571    ///
572    /// One relaxed load per write command on a server nobody is syncing from,
573    /// which is the same shape and the same cost as the pause check it sits
574    /// beside.
575    #[must_use]
576    pub(crate) fn frozen(&self) -> bool {
577        self.repl.frozen.load(Relaxed)
578    }
579
580    /// The whole dataset as one image, and the offset it is an image as of.
581    ///
582    /// Everything between the two has to be nothing at all, which is what the
583    /// freeze is for. Writes are held from before the barrier until after the
584    /// image is finished, so a change is either inside the image or after the
585    /// offset and never both. See the module header for why there is no fork to
586    /// get this for free.
587    pub(crate) fn snapshot_at_an_instant(&self) -> (Vec<u8>, u64) {
588        let building = self.repl.building.lock();
589        self.repl.frozen.store(true, Release);
590        // A write already running holds the stripe it is writing to, so taking
591        // every stripe once and letting it go again is a barrier: once it is
592        // through, no write is in flight and the freeze above stops any more
593        // starting. One pass is enough, in whatever order, because nothing being
594        // waited for can start again behind it.
595        for db in &self.dbs {
596            for stripe in 0..db.width() {
597                drop(db.hold_stripe(stripe));
598            }
599        }
600        let offset = self.repl.offset.load(Acquire);
601        let (image, _skipped) = super::persist::build(self);
602        self.repl.frozen.store(false, Release);
603        drop(building);
604        (image, offset)
605    }
606
607    /// Take a master's history as our own, which is what a replica does.
608    ///
609    /// Everything this server writes from here on is under the master's id and
610    /// at the master's offsets, so a sub-replica underneath is handed positions
611    /// its own master would recognise. The backlog goes with it, because what is
612    /// in it is a stretch of a history this server is no longer writing and
613    /// answering a partial resync out of it would send a replica bytes from
614    /// somebody else's stream.
615    pub(crate) fn adopt(&self, id: [u8; ID_LEN], offset: u64) {
616        let mut ours = self.repl.id.lock();
617        if *ours == id && self.repl.offset.load(Acquire) == offset {
618            return;
619        }
620        *ours = id;
621        drop(ours);
622        let mut backlog = self.repl.backlog.lock();
623        *backlog = Backlog::default();
624        self.repl.offset.store(offset, Release);
625        drop(backlog);
626        self.repl.on_db.store(-1, Relaxed);
627    }
628
629    /// Stop following and start a history of our own, keeping the old one.
630    ///
631    /// The id this server was writing under becomes the second id and the offset
632    /// it had reached becomes the second offset, and a new id is taken. That is
633    /// what lets the replicas that were following the same master be handed over
634    /// to this one without every one of them starting from a snapshot: each of
635    /// them asks about a history this server can still say it was part of, up to
636    /// the point where the two part, and the second offset is that point.
637    pub(crate) fn promote(&self) {
638        let mut id = self.repl.id.lock();
639        let mut id2 = self.repl.id2.lock();
640        *id2 = *id;
641        *id = make_id();
642        // One past the last byte of the old history, which is the first byte
643        // that is only ours, because a replica asks about the first byte it
644        // wants and counts from one.
645        self.repl
646            .second
647            .store(self.repl.offset.load(Acquire) as i64 + 1, Relaxed);
648    }
649
650    /// Take a fresh replication id and forget the old one, which is what
651    /// `DEBUG CHANGE-REPL-ID` is for.
652    ///
653    /// Not a promotion. A promotion keeps the old id as the second one so that
654    /// the replicas that shared it can carry on, and this throws it away, which
655    /// is the point: it is how a test makes two servers that were part of the
656    /// same history stop being able to prove it, so the next `PSYNC` between
657    /// them has to be a full one.
658    pub(crate) fn change_id(&self) {
659        let mut id = self.repl.id.lock();
660        let mut id2 = self.repl.id2.lock();
661        *id = make_id();
662        *id2 = [b'0'; ID_LEN];
663        self.repl.second.store(-1, Relaxed);
664    }
665
666    /// The replica row for a connection, if it is one.
667    fn replica_of(&self, id: u64) -> Option<Arc<Replica>> {
668        let rows = self.repl.rows.lock();
669        rows.iter().find(|r| r.row.id == id).map(Arc::clone)
670    }
671}
672
673// ------------------------------------------------------------ the heartbeat
674
675/// How long a quiet replication link goes before its master pings it.
676///
677/// Ten seconds is `repl-ping-replica-period`'s default. The setting itself is
678/// not in the config table yet, so this is the number rather than a lookup.
679const PING_REPLICAS_MS: u64 = 10_000;
680
681/// How often the heartbeat thread wakes up to see whether one is due.
682const HEARTBEAT_MS: u64 = 1000;
683
684impl Server {
685    /// Start the thread that pings the replicas of a quiet master.
686    ///
687    /// A master pings so that a replica can tell a link that has gone away from
688    /// a master with nothing to say, since a TCP connection that nobody writes
689    /// to gives no sign either way. It is also what moves the offset on a server
690    /// nobody is writing to, and a replica whose offset is still zero is one a
691    /// real server leaves out of `CLUSTER SLOTS` on the grounds that it has
692    /// never caught up with anything.
693    ///
694    /// Its own thread because the housekeeping the engine does runs per batch,
695    /// so a server with no traffic does none of it, and a server with no traffic
696    /// is exactly the one that owes its replicas a ping.
697    pub fn start_replica_heartbeat(self: &Arc<Server>) {
698        let beating = Arc::clone(self);
699        yo_alloc::allow(|| {
700            let _ = std::thread::Builder::new()
701                .name(String::from("yo-repl-ping"))
702                .spawn(move || heartbeat(&beating));
703        });
704    }
705}
706
707/// One ping every `PING_REPLICAS_MS` on a link that has been quiet that long.
708fn heartbeat(server: &Arc<Server>) {
709    loop {
710        std::thread::sleep(Duration::from_millis(HEARTBEAT_MS));
711        // Only a top level master. A replica passes on the stream it is given
712        // rather than writing one, so a ping from here would put bytes in the
713        // middle of somebody else's history.
714        if !server.replicated() || server.following() {
715            continue;
716        }
717        let now = server.now_ms();
718        if now.saturating_sub(server.repl.last_io.load(Relaxed)) < PING_REPLICAS_MS {
719            continue;
720        }
721        // No `SELECT` in front of it, which is what the reference's `dictid` of
722        // minus one means: a ping touches no key, so it does not move the
723        // stream's database and must not look as though it did.
724        emit(server, render(&[b"PING"]));
725    }
726}
727
728// ---------------------------------------------------------------- the feed
729
730/// One command as a RESP array, which is the only shape the stream has.
731fn render(parts: &[&[u8]]) -> Vec<u8> {
732    let mut out = Vec::with_capacity(16 + parts.iter().map(|p| p.len() + 16).sum::<usize>());
733    out.extend_from_slice(b"*");
734    out.extend_from_slice(parts.len().to_string().as_bytes());
735    out.extend_from_slice(b"\r\n");
736    for part in parts {
737        out.extend_from_slice(b"$");
738        out.extend_from_slice(part.len().to_string().as_bytes());
739        out.extend_from_slice(b"\r\n");
740        out.extend_from_slice(part);
741        out.extend_from_slice(b"\r\n");
742    }
743    out
744}
745
746/// Put bytes on the stream: into the backlog, onto the offset, out to everybody.
747///
748/// The offset moves under the backlog lock so that the backlog and the number
749/// never disagree. A replica attaching between the two would otherwise be given
750/// an offset the backlog cannot honour.
751fn emit(server: &Server, bytes: Vec<u8>) {
752    server.repl.last_io.store(server.now_ms(), Relaxed);
753    let shared = {
754        let mut backlog = server.repl.backlog.lock();
755        let upto = server.repl.offset.load(Relaxed) + bytes.len() as u64;
756        yo_alloc::allow(|| backlog.push(&bytes, upto));
757        server.repl.offset.store(upto, Release);
758        yo_alloc::allow(|| Arc::new(bytes))
759    };
760    for held in server.replica_rows() {
761        if !held.online.load(Relaxed) {
762            continue;
763        }
764        server.post(
765            held.row.thread.load(Relaxed),
766            Envelope::raw(
767                held.row.conn.load(Relaxed),
768                held.row.id,
769                Arc::clone(&shared),
770            ),
771        );
772    }
773}
774
775/// Put one command on the stream, with a `SELECT` in front of it if the stream
776/// is not on the right database.
777fn send(server: &Server, db: usize, parts: &[&[u8]]) {
778    let mut bytes = Vec::new();
779    if server.repl.on_db.load(Relaxed) != db as i64 {
780        let n = db.to_string();
781        bytes = render(&[b"SELECT", n.as_bytes()]);
782        server.repl.on_db.store(db as i64, Relaxed);
783    }
784    bytes.extend_from_slice(&render(parts));
785    emit(server, bytes);
786}
787
788/// Pass a master's bytes on, unchanged, and count them.
789///
790/// What a replica owes anybody following it is the stream it was given rather
791/// than an account of what running it did, because the offsets in that stream
792/// are positions in the master's history and this server is not writing one. So
793/// the bytes go on as they arrived. The count moves either way, because it is
794/// the number this server acknowledges to its own master and a chain of nobody
795/// still has to be able to say where it has got to.
796pub(crate) fn relayed(server: &Server, bytes: Vec<u8>, db: usize) {
797    if server.replicated() {
798        server.repl.on_db.store(db as i64, Relaxed);
799        emit(server, bytes);
800        return;
801    }
802    server.repl.offset.fetch_add(bytes.len() as u64, Release);
803}
804
805/// Report a command to every replica, in the form it has to be given.
806///
807/// The caller has already checked [`Server::replicated`] and that the command
808/// did not fail, so what is decided here is only the shape.
809///
810/// `verbatim` is whether the command as it arrived is a fair thing to send when
811/// the body said nothing. It is the write flag, and it is false for a read,
812/// which sends nothing, and for a container like `XGROUP` whose flags are on its
813/// subcommands, which sends what its body pushed and nothing otherwise.
814pub(super) fn feed(server: &Server, db: usize, args: Args<'_>, verbatim: bool) {
815    if super::follow::applying() {
816        forget();
817        return;
818    }
819    let instead = taken();
820    yo_alloc::allow(|| match instead {
821        None if !verbatim => {}
822        None => {
823            let parts: Vec<&[u8]> = (0..args.len()).map(|i| args.get(i)).collect();
824            send(server, db, &parts);
825        }
826        Some(each) => {
827            for one in &each {
828                let parts: Vec<&[u8]> = one.iter().map(Vec::as_slice).collect();
829                send(server, db, &parts);
830            }
831        }
832    });
833}
834
835/// Report what a command that nobody is running left behind.
836///
837/// A blocked client is answered by the thread that woke it, a long way outside
838/// the funnel and with no arguments to fall back on, so unlike [`feed`] there is
839/// no verbatim form here: whatever the answering left is all there is, and an
840/// answering that left nothing did nothing.
841pub(super) fn served(server: &Server, db: usize) {
842    if super::follow::applying() {
843        forget();
844        return;
845    }
846    let Some(each) = taken() else {
847        return;
848    };
849    yo_alloc::allow(|| {
850        for one in &each {
851            let parts: Vec<&[u8]> = one.iter().map(Vec::as_slice).collect();
852            send(server, db, &parts);
853        }
854    });
855}
856
857/// Throw away whatever the last command left, for a command that is not
858/// propagated at all.
859///
860/// A read that pushed a rewrite is a bug, but a rewrite left behind by a command
861/// that was refused after it pushed one is not, and it must not be handed to the
862/// next command that runs on this thread.
863pub(super) fn forget() {
864    let _ = taken();
865}
866
867/// Note a key or a field the storage layer took away on its own.
868///
869/// A replica never expires and never evicts. It cannot: the two decisions are
870/// made from a clock reading and a memory figure that are the master's and not
871/// its own, and a replica that made them itself would answer a read differently
872/// from the master for as long as the two disagreed. So it holds a key past its
873/// deadline until it is told, and being told is this. Redis sends the same thing
874/// for the same reason, and it is why a replica's `dbsize` can be ahead of the
875/// master's for a moment and never behind it.
876///
877/// Collected rather than sent, because the point at which a key goes is inside a
878/// stripe lock and the send takes a different one.
879pub(crate) fn reaped(parts: &[&[u8]]) {
880    if !ARMED.get() {
881        return;
882    }
883    REAPED.with_borrow_mut(|list| {
884        yo_alloc::allow(|| list.push(parts.iter().map(|part| part.to_vec()).collect()));
885    });
886}
887
888/// Send what [`reaped`] collected, ahead of whatever the command itself did.
889///
890/// Ahead, because that is the order it happened in and the order matters: a
891/// `SET k v` that found an expired `k` on the way in has to reach a replica as
892/// the deletion and then the write, or a replica that has kept a `k` of the
893/// wrong type refuses the write it is sent.
894pub(super) fn swept(server: &Server, db: usize) {
895    if REAPED.with_borrow(Vec::is_empty) {
896        return;
897    }
898    let each = REAPED.with_borrow_mut(core::mem::take);
899    // A replica that reaped a key on its own has nothing to tell anybody. The
900    // master is going to send the deletion in a moment and that is the copy that
901    // counts, because it is the one at the offset everybody else is counting
902    // from. See the `follow` module on why a chain passes bytes on rather than
903    // effects.
904    if super::follow::applying() {
905        return;
906    }
907    yo_alloc::allow(|| {
908        for one in &each {
909            let parts: Vec<&[u8]> = one.iter().map(Vec::as_slice).collect();
910            send(server, db, &parts);
911        }
912    });
913}
914
915// ------------------------------------------------------------- the commands
916
917/// `REPLCONF`, which is how a replica tells a master about itself.
918///
919/// Everything here is a pair, and a master answers `OK` to a pair it does not
920/// know rather than refusing it, because the whole point of the command is that
921/// a newer replica can tell an older master things it has never heard of. The
922/// one exception is `ACK`, which is not a question and is answered with silence:
923/// by the time a replica sends one the connection is a stream, and a reply on it
924/// is a protocol error at the other end.
925pub(super) fn replconf(
926    server: &Server,
927    session: &Session,
928    args: Args<'_>,
929    out: &mut Out,
930) -> Result<()> {
931    if args.len() < 3 || args.len().is_multiple_of(2) {
932        return Err(args::wrong_arity("replconf"));
933    }
934    let mut at = 1;
935    while at < args.len() {
936        let name = args.get(at).to_ascii_lowercase();
937        match name.as_slice() {
938            b"listening-port" => {
939                let port = args.int(at + 1)?;
940                server.note_replica_port(session.row().id, port.max(0) as u64);
941            }
942            b"ack" => {
943                // Not answered, ever. See the doc comment.
944                let ack = args.int(at + 1).unwrap_or(0);
945                if let Some(held) = server.replica_of(session.row().id) {
946                    held.ack.store(ack.max(0) as u64, Relaxed);
947                    held.ack_ms.store(server.clock.now_ms(), Relaxed);
948                }
949                return Ok(());
950            }
951            b"getack" => {
952                // A master sends this and a replica answers it. Reaching it here
953                // means somebody sent it to a master, which Redis refuses.
954                return Err(Error::new(
955                    Code::Invalid,
956                    "REPLCONF GETACK is only supported by a replica",
957                ));
958            }
959            _ => {}
960        }
961        at += 2;
962    }
963    out.ok();
964    Ok(())
965}
966
967/// `PSYNC <replid> <offset>`, which is where a connection stops being a client.
968///
969/// Answers `+CONTINUE` and the missing bytes when the replica is asking to carry
970/// on a history we are still writing and still hold enough of, and a full resync
971/// otherwise. `SYNC` is the same thing without the negotiation, from before
972/// Redis had one, and is a snapshot with no header in front of it.
973pub(super) fn psync(
974    server: &Server,
975    session: &mut Session,
976    args: Args<'_>,
977    out: &mut Out,
978) -> Result<()> {
979    let partial = if args.name().eq_ignore_ascii_case(b"sync") {
980        if args.len() != 1 {
981            return Err(args::wrong_arity("sync"));
982        }
983        None
984    } else {
985        // Four words when the fourth is `FAILOVER`, which is a master that has
986        // stopped writing telling us to take over from it. Three otherwise, and
987        // a fourth word that is not that one is not a spelling of anything.
988        let handover = args.len() == 4 && args.get(3).eq_ignore_ascii_case(b"failover");
989        if args.len() != 3 && !handover {
990            return Err(args::wrong_arity("psync"));
991        }
992        let asked = args.get(1);
993        // Before anything else is read, because this is the one form of the
994        // command that changes what this server is rather than asking it for
995        // something, and a master that has already stopped writing is waiting on
996        // the answer.
997        if handover {
998            take_over(server, asked)?;
999        }
1000        let from = args.int(2)?;
1001        // The number a replica sends is the position of the first byte it
1002        // wants counted from one, so a replica that has everything asks for one
1003        // past the end of the stream. Everything on this side counts bytes
1004        // written, so the two are a step apart, and the step is taken here
1005        // rather than inside the backlog, where every other caller means the
1006        // count. A nought is nobody's answer and would mean the byte before the
1007        // first, so it starts again, which is what a real master does with it.
1008        (asked != b"?" && from >= 1).then(|| (asked.to_vec(), from as u64 - 1))
1009    };
1010    // Nobody may attach to a server that is in the middle of handing its job
1011    // over, because the offsets it is about to start reporting are somebody
1012    // else's. The `FAILOVER` form above got past this, which is the point: that
1013    // one is the handover and this is everybody else.
1014    if server.failing_over() {
1015        out.error(b"NOMASTERLINK Can't SYNC while failing over");
1016        return Ok(());
1017    }
1018    // Nor to a replica whose own link is not up, because what it would be
1019    // handing out is a history it has not finished reading.
1020    if server.following() && !server.master_link_up() {
1021        out.error(b"NOMASTERLINK Can't SYNC while not connected with my master");
1022        return Ok(());
1023    }
1024    if session.running() {
1025        return Err(Error::new(
1026            Code::Invalid,
1027            "PSYNC isn't allowed for DENY BLOCKING client",
1028        ));
1029    }
1030    // A partial resync first, since it is the cheap answer and the whole reason
1031    // the replica bothered to remember where it was.
1032    if let Some((asked, from)) = partial
1033        && let Some(bytes) = catch_up(server, &asked, from)
1034    {
1035        {
1036            let id = server.repl_id();
1037            out.raw(b"+CONTINUE ");
1038            out.raw(&id);
1039            out.raw(b"\r\n");
1040            out.raw(&bytes);
1041            attach(server, session);
1042            return Ok(());
1043        }
1044    }
1045    full(server, session, out)
1046}
1047
1048/// Take the master's job, because the master asked us to.
1049///
1050/// The fourth word of a `PSYNC` is how a `FAILOVER` finishes. Everything hard
1051/// about it happened on the other side: the master stopped writing, waited until
1052/// this server had acknowledged every byte, and only then sent this. So all that
1053/// is left here is to check that it is talking to the right server and then stop
1054/// being a replica, after which the `PSYNC` carries on as an ordinary one and is
1055/// answered with a `+CONTINUE` that costs nothing, because the two sides are in
1056/// step by construction.
1057///
1058/// The promotion is `REPLICAOF NO ONE`, so the history that was being followed
1059/// is kept as the second id. That is what makes the old master's `PSYNC`, which
1060/// names that very history, something this server can answer at all.
1061fn take_over(server: &Server, asked: &[u8]) -> Result<()> {
1062    if !server.following() {
1063        return Err(Error::new(
1064            Code::Invalid,
1065            "PSYNC FAILOVER can't be sent to a master.",
1066        ));
1067    }
1068    if asked != server.repl_id() {
1069        return Err(Error::new(
1070            Code::Invalid,
1071            "PSYNC FAILOVER replid must match my replid.",
1072        ));
1073    }
1074    let Some(shared) = server.myself() else {
1075        return Err(Error::new(
1076            Code::Invalid,
1077            "PSYNC FAILOVER is not available on an embedded server",
1078        ));
1079    };
1080    shared.stop_following();
1081    Ok(())
1082}
1083
1084/// The bytes a reconnecting replica missed, or `None` if it has to start again.
1085///
1086/// A replica asking about the history we are writing is caught up from the
1087/// backlog. One asking about the history we were writing before we were promoted
1088/// is caught up too, but only up to the point where the histories part, which is
1089/// what the second offset records.
1090fn catch_up(server: &Server, asked: &[u8], from: u64) -> Option<Vec<u8>> {
1091    let ours = server.repl_id();
1092    let theirs = *server.repl.id2.lock();
1093    let second = server.repl.second.load(Relaxed);
1094    let matches = asked == ours
1095        || (asked == theirs && second >= 0 && from <= u64::try_from(second).unwrap_or(0));
1096    if !matches {
1097        return None;
1098    }
1099    let upto = server.repl.offset.load(Acquire);
1100    let backlog = server.repl.backlog.lock();
1101    yo_alloc::allow(|| backlog.since(from, upto))
1102}
1103
1104/// A full resync: the header, then the whole keyspace as one image.
1105///
1106/// The image and the offset are taken with every stripe of every database held,
1107/// so what the replica loads and what it is then told about are the two halves
1108/// of one instant and neither overlaps the other. See the module header.
1109fn full(server: &Server, session: &mut Session, out: &mut Out) -> Result<()> {
1110    let (image, offset) = server.snapshot_at_an_instant();
1111    let id = server.repl_id();
1112    out.raw(b"+FULLRESYNC ");
1113    out.raw(&id);
1114    out.raw(b" ");
1115    out.raw(offset.to_string().as_bytes());
1116    out.raw(b"\r\n");
1117    // A bulk string with no newline after it, which is the one place in the
1118    // protocol where that is so. The replica reads the length and then exactly
1119    // that many bytes, and anything after them is the stream.
1120    out.raw(b"$");
1121    out.raw(image.len().to_string().as_bytes());
1122    out.raw(b"\r\n");
1123    out.raw(&image);
1124    attach(server, session);
1125    Ok(())
1126}
1127
1128/// Turn the connection into a replica, once the answer has been written.
1129///
1130/// Everything it was holding as a client goes first: a transaction it had open,
1131/// the keys it was watching and any subscription. All three are promises of a
1132/// reply, and a connection that has become a stream has no way left to keep one.
1133fn attach(server: &Server, session: &mut Session) {
1134    super::forget_session(server, session);
1135    server.take_replica(session.row());
1136}
1137
1138/// How many replicas have acknowledged everything written so far.
1139///
1140/// What `WAIT` answers with. It is the count that is already there rather than
1141/// one waited for, which is the right answer when it is already enough and is a
1142/// divergence when it is not. Waiting here means holding a connection until an
1143/// acknowledgement arrives, which needs the waiter list to be woken by something
1144/// that is not a key changing, and that is the same wakeup D-115 is about.
1145pub(super) fn caught_up(server: &Server) -> usize {
1146    let upto = server.repl_offset();
1147    server
1148        .replica_rows()
1149        .iter()
1150        .filter(|r| r.online.load(Relaxed) && r.ack.load(Relaxed) >= upto)
1151        .count()
1152}
1153
1154// ------------------------------------------------------------------- report
1155
1156/// What `ROLE` answers on a master.
1157///
1158/// The word, then the offset as an integer, then one row per replica. Only the
1159/// replicas that are online are listed, because a replica still being sent its
1160/// snapshot has no offset of its own to report and Redis leaves it out for the
1161/// same reason. Inside a row every field is a bulk string, including the port
1162/// and the offset, which is a shape nobody would pick today and is the shape
1163/// every client library already parses.
1164pub(super) fn role(server: &Server, out: &mut Out) {
1165    if server.following() {
1166        super::follow::role(server, out);
1167        return;
1168    }
1169    let rows = server.replica_rows();
1170    out.array(3);
1171    out.bulk(b"master");
1172    out.int(server.repl_offset() as i64);
1173    let at = out.len();
1174    let mut n = 0;
1175    for held in &rows {
1176        if !held.online.load(Relaxed) {
1177            continue;
1178        }
1179        let (host, port) = held.address();
1180        out.array(3);
1181        out.bulk(host.as_bytes());
1182        out.bulk(port.to_string().as_bytes());
1183        out.bulk(held.ack.load(Relaxed).to_string().as_bytes());
1184        n += 1;
1185    }
1186    out.close_array(at, n);
1187}
1188
1189/// The `Replication` section of `INFO`.
1190///
1191/// Redis reports a dozen fields on a master and a few more on a replica. This is
1192/// the master's set, since nothing here can be a replica yet, plus one `slaveN`
1193/// row per replica in the order they attached, which is what tooling reads to
1194/// find out who is following whom.
1195pub(super) fn info(server: &Server, s: &mut String) {
1196    use core::fmt::Write as _;
1197    let rows = server.replica_rows();
1198    let id = server.repl_id();
1199    let id2 = *server.repl.id2.lock();
1200    let offset = server.repl_offset();
1201    let now = server.clock.now_ms();
1202    let _ = write!(
1203        s,
1204        "# Replication\r\nrole:{}\r\n",
1205        super::follow::role_word(server)
1206    );
1207    // The master's own fields first when there is a master, which is where Redis
1208    // puts them: a tool reading the section top to bottom finds out what this
1209    // server is, then who it follows, then who follows it.
1210    super::follow::info(server, s);
1211    let _ = write!(s, "connected_slaves:{}\r\n", rows.len());
1212    for (i, held) in rows.iter().enumerate() {
1213        let (host, port) = held.address();
1214        let state = if held.online.load(Relaxed) {
1215            "online"
1216        } else {
1217            "wait_bgsave"
1218        };
1219        let lag = now.saturating_sub(held.ack_ms.load(Relaxed)) / 1000;
1220        let _ = write!(
1221            s,
1222            "slave{i}:ip={host},port={port},state={state},offset={},lag={lag}\r\n",
1223            held.ack.load(Relaxed),
1224        );
1225    }
1226    let _ = write!(
1227        s,
1228        "master_failover_state:{}\r\n\
1229         master_replid:{}\r\nmaster_replid2:{}\r\n\
1230         master_repl_offset:{offset}\r\nsecond_repl_offset:{}\r\n",
1231        server.failover_word(),
1232        String::from_utf8_lossy(&id),
1233        String::from_utf8_lossy(&id2),
1234        server.repl.second.load(Relaxed),
1235    );
1236    // The backlog is made when the first replica attaches and is never given
1237    // back, which is what `repl_backlog_active` is saying. The histlen is how
1238    // much of it is real, so a server that has never had a replica reports a
1239    // backlog that is off and empty rather than one that is on and zero long.
1240    let (active, first, histlen) = {
1241        let backlog = server.repl.backlog.lock();
1242        (
1243            usize::from(!backlog.ring.is_empty()),
1244            // Counted from one, the way a replica counts when it asks to carry
1245            // on, because this is the number it is being compared against. A
1246            // server that has never had a replica has no backlog and reports a
1247            // nought rather than the one that would be the first byte of the
1248            // one it has not made.
1249            if backlog.ring.is_empty() {
1250                0
1251            } else {
1252                backlog.first + 1
1253            },
1254            backlog.filled,
1255        )
1256    };
1257    let _ = write!(
1258        s,
1259        "repl_backlog_active:{active}\r\nrepl_backlog_size:{BACKLOG_BYTES}\r\n\
1260         repl_backlog_first_byte_offset:{first}\r\nrepl_backlog_histlen:{histlen}\r\n\r\n",
1261    );
1262}