Skip to main content

yo_resp/dispatch/
follow.rs

1//! Being a replica: the link out to a master and the stream that comes back.
2//!
3//! The other half of `repl`. That module is what a master does to feed somebody
4//! else, and this is what a server does to be fed. The two never run against
5//! each other on one server unless somebody has built a chain, and a chain is
6//! the case this file is careful about rather than the case it is written for.
7//!
8//! # What the link is
9//!
10//! One thread, one socket, and a loop that never ends until somebody says
11//! `REPLICAOF NO ONE` or the server stops. It dials the master, walks the
12//! handshake, takes the snapshot, and then reads commands off the socket and
13//! runs them against this server's own keyspace forever. A link that breaks is
14//! not an error to report to anybody, because there is nobody to report it to:
15//! the client that said `REPLICAOF` was answered `OK` the moment the intent was
16//! recorded, which is what a real server does and is the only thing it can do
17//! when the dial has not happened yet. So a broken link waits a second and dials
18//! again, and `INFO` is where an operator finds out.
19//!
20//! A thread of its own rather than a slot on the reactor. The reactor's threads
21//! are woken by clients and this has no client, the handshake is a handful of
22//! blocking round trips and the snapshot is one very large read, and all three
23//! of those are the wrong shape for an event loop that is measured in
24//! nanoseconds per command. One thread that is asleep on a socket for most of
25//! its life costs a stack.
26//!
27//! # Why the commands go through the front door
28//!
29//! What arrives is a stream of ordinary commands, so what runs them is the
30//! ordinary dispatcher, through a [`Session`] the link owns. That is not a
31//! shortcut, it is the point: a replica that applied writes through some second
32//! path would be a second implementation of every command, and the first bug in
33//! it would be a replica that quietly disagrees with its master. Going through
34//! the front door also means keyspace notifications fire on the replica, the
35//! search indexes are kept up, and `WATCH` on the replica notices, all of which
36//! a real replica does and none of which had to be written twice.
37//!
38//! Three things about that session are not ordinary. It is past the password and
39//! past the access control list, because the master is not a user and there is
40//! nobody to authenticate. It is exempt from the read only refusal, which is the
41//! whole point of the refusal. And it is exempt from `CLIENT PAUSE`, because a
42//! pause is a thing an operator does to clients and a master is not one, and a
43//! paused replica that stopped reading its socket would make the master's output
44//! buffer grow until the master dropped the link.
45//!
46//! # The offset
47//!
48//! The replica counts the bytes it has applied and tells the master about them,
49//! and the master compares that number with its own to answer `WAIT` and to fill
50//! in the lag in `INFO`. So the count has to be of the bytes as they arrived and
51//! not of anything this server decided: what is added is exactly what the
52//! decoder said it consumed, including the commands that did nothing, including
53//! the `PING`s the master sends to keep the link warm, and including the
54//! `REPLCONF GETACK` that asks for the number itself, which is why the answer to
55//! a `GETACK` is sent after its own bytes have been counted.
56//!
57//! # A replica with replicas
58//!
59//! A chain works by passing the bytes on rather than by propagating what the
60//! commands did. The two are not the same thing and only one of them can be:
61//! the offset a sub-replica acknowledges has to be a position in the master's
62//! stream, and a middle server that made up its own stream would be handing out
63//! positions in a history nobody else is writing. So while this link is
64//! applying, the ordinary propagation is turned off for the thread and the bytes
65//! that arrived are put on this server's stream unchanged, under the master's
66//! own replication id and at the master's own offsets.
67
68use core::cell::Cell;
69use std::io::{ErrorKind, Read, Write};
70use std::net::{TcpStream, ToSocketAddrs};
71use std::sync::Arc;
72use std::sync::atomic::Ordering::{Relaxed, Release};
73use std::sync::atomic::{AtomicBool, AtomicU8, AtomicU64};
74use std::time::{Duration, Instant};
75
76use yo_common::lock::Lock;
77use yo_common::{Code, Error, Result};
78
79use crate::proto::{Limits, Proto};
80use crate::reply::Out;
81use crate::request::{Argv, Step};
82
83use super::args::{self, Args};
84use super::repl::{self, ID_LEN};
85use super::{Flow, Server, Session};
86
87/// How long a dial is given before it is called a failure.
88const DIAL_TIMEOUT: Duration = Duration::from_secs(5);
89
90/// How long a read waits before the loop looks around at other things.
91///
92/// Short, because this is also how often the link notices it has been called off
93/// and how close to the second an acknowledgement lands. A tenth of a second on
94/// a socket that is usually idle is a syscall ten times a second, which is
95/// nothing next to a thread that is otherwise asleep.
96const POLL: Duration = Duration::from_millis(100);
97
98/// How often the replica tells the master where it has got to.
99///
100/// A second, which is Redis's `REPLCONF ACK` period. The master turns the gap
101/// between acknowledgements into the lag it reports, so a longer period would
102/// make every replica look worse than it is.
103const ACK_EVERY: Duration = Duration::from_secs(1);
104
105/// How long to wait before dialling again after a link went down.
106const RETRY: Duration = Duration::from_millis(500);
107
108/// How long the whole handshake is given, per attempt.
109const HANDSHAKE_TIMEOUT: Duration = Duration::from_secs(10);
110
111/// How long a master is given to start answering a `PSYNC`, and how long a gap
112/// in the snapshot is allowed to be before the link is called broken.
113///
114/// Much longer than the handshake, because what happens between the `PSYNC` and
115/// the first byte of the snapshot is a fork and a save on a machine holding a
116/// dataset this one has not seen yet. Redis's own replica gives it `repl-timeout`
117/// and that is a minute by default, so this is a minute.
118const SYNC_TIMEOUT: Duration = Duration::from_secs(60);
119
120/// The longest line the handshake will read before calling the peer broken.
121///
122/// A handshake reply is a word and at most an id and a number. Anything past
123/// this is a peer that is not answering the question that was asked, which is
124/// the same rule and the same reasoning `MIGRATE` uses.
125const LINE_MAX: usize = 1024;
126
127/// What `master_link_status` says, which is also what the link is doing.
128///
129/// Redis reports two words where there are four states, so `Connect` and `Sync`
130/// both read as `down`, and the one that tells them apart is
131/// `master_sync_in_progress` beside it.
132#[derive(Clone, Copy, PartialEq, Eq)]
133#[repr(u8)]
134enum State {
135    /// Not following anybody. This server is a master.
136    None = 0,
137    /// Following somebody and not currently connected to them.
138    Connect = 1,
139    /// Connected and loading the snapshot.
140    Sync = 2,
141    /// Connected and applying the stream.
142    Up = 3,
143}
144
145impl State {
146    fn from(n: u8) -> State {
147        match n {
148            1 => State::Connect,
149            2 => State::Sync,
150            3 => State::Up,
151            _ => State::None,
152        }
153    }
154}
155
156/// Where this server has been told to follow.
157#[derive(Clone)]
158struct Upstream {
159    host: String,
160    port: u16,
161}
162
163/// Everything about being a replica, all of it idle on a server that is nobody's.
164pub(crate) struct Follower {
165    /// Who this server follows, and none when it is a master.
166    ///
167    /// Written by `REPLICAOF` and read by the link thread, by `INFO` and by
168    /// `ROLE`. A lock and not an atomic because it is a host name, and it is
169    /// touched once per link rather than once per command.
170    upstream: Lock<Option<Upstream>>,
171    /// Which link is the current one.
172    ///
173    /// Every `REPLICAOF` bumps this and the thread it starts carries the number
174    /// it was started with. A thread whose number is no longer the current one
175    /// has been replaced and lets itself go at the next thing it does, which is
176    /// how a link is called off without anything having to reach into a blocking
177    /// socket read.
178    epoch: AtomicU64,
179    /// What the link is doing, one of [`State`].
180    state: AtomicU8,
181    /// Whether an ordinary client's write is refused, which is Redis's
182    /// `replica-read-only` and is on by default.
183    ///
184    /// Read once per write command on a server that is not a replica, next to
185    /// the pause check and the freeze check that are already there, and the load
186    /// it reads is of a bool that is false.
187    read_only: AtomicBool,
188    /// Whether this server is following anybody at all, so the read only check
189    /// above is one load rather than a lock.
190    on: AtomicBool,
191    /// The user and password the link authenticates with, both empty when the
192    /// master wants neither.
193    auth: Lock<(Vec<u8>, Vec<u8>)>,
194    /// The port to tell the master this server listens on, which is what the
195    /// master reports in its own `INFO` and in `ROLE`.
196    ///
197    /// Zero when nobody has said, which is every embedded caller and every test,
198    /// and is what a master shows for a replica that did not say either.
199    port: AtomicU64,
200    /// When the link last had anything out of the master, for
201    /// `master_last_io_seconds_ago`.
202    last_io_ms: AtomicU64,
203    /// When the link last went down, for `master_link_down_since_seconds`.
204    down_ms: AtomicU64,
205    /// Whether this server's replication id and offset came from a master, so
206    /// the next `PSYNC` may ask to carry on rather than starting again.
207    ///
208    /// The position itself is not kept here on purpose. It is the server's own
209    /// replication offset, which `adopt` sets to the master's and which every
210    /// applied byte moves along, so there is one number rather than two that
211    /// could disagree. A second copy updated at the end of the stream loop would
212    /// be a copy that is behind by whatever the link died in the middle of, and
213    /// asking to carry on from behind is asking for the same bytes twice.
214    ///
215    /// Kept across a broken link, which is the whole reason a partial resync is
216    /// possible at all, and kept across a change of master too, which is what
217    /// lets a replica be handed to a promoted one without a snapshot. Thrown
218    /// away by `REPLICAOF NO ONE`, because that takes a new id and asking a
219    /// master about a history it has never heard of is a full resync with an
220    /// extra round trip in front of it.
221    resume: AtomicBool,
222}
223
224impl Default for Follower {
225    fn default() -> Follower {
226        Follower {
227            upstream: Lock::new(None),
228            epoch: AtomicU64::new(0),
229            state: AtomicU8::new(State::None as u8),
230            read_only: AtomicBool::new(true),
231            on: AtomicBool::new(false),
232            auth: Lock::new((Vec::new(), Vec::new())),
233            port: AtomicU64::new(0),
234            last_io_ms: AtomicU64::new(0),
235            down_ms: AtomicU64::new(0),
236            resume: AtomicBool::new(false),
237        }
238    }
239}
240
241thread_local! {
242    /// Whether this thread is applying a master's stream rather than running a
243    /// client's command.
244    ///
245    /// Read by the propagation site, which has no other way to tell the two
246    /// apart and has to, because what a replica passes on is the bytes it was
247    /// given and not what running them turned out to do. See the module header.
248    static APPLYING: Cell<bool> = const { Cell::new(false) };
249}
250
251/// Whether what is running arrived from a master.
252#[must_use]
253pub(crate) fn applying() -> bool {
254    APPLYING.get()
255}
256
257impl Server {
258    /// Whether this server is following somebody, which is the whole cost of
259    /// this file on a server that is not.
260    #[must_use]
261    pub(crate) fn following(&self) -> bool {
262        self.follow.on.load(Relaxed)
263    }
264
265    /// Whether an ordinary client's write is refused here.
266    ///
267    /// Both halves, because a server that is a replica and has been told it is
268    /// writable takes writes, and a server that is not a replica at all is not
269    /// made read only by the setting sitting there at its default.
270    #[must_use]
271    pub(crate) fn read_only_replica(&self) -> bool {
272        self.follow.on.load(Relaxed) && self.follow.read_only.load(Relaxed)
273    }
274
275    /// Say what port to announce to a master, which is what it reports back.
276    ///
277    /// Called once by whoever bound the socket, which is the only place that
278    /// knows. A server nobody tells announces nothing, which is what a real
279    /// master shows for a replica that did not say.
280    pub fn announce_port(&self, port: u16) {
281        self.follow.port.store(u64::from(port), Relaxed);
282    }
283
284    /// The port whoever bound the socket said this server is on, which `INFO`
285    /// reports and a cluster node writes into its config file.
286    ///
287    /// Nought on an embedded caller that never opened a socket, which is what a
288    /// reader should see rather than a guess.
289    #[must_use]
290    pub(crate) fn announced_port(&self) -> u16 {
291        self.follow.port.load(Relaxed) as u16
292    }
293
294    /// Say what the link should authenticate with, which is Redis's
295    /// `masteruser` and `masterauth`.
296    pub fn master_auth(&self, user: &[u8], pass: &[u8]) {
297        let mut auth = self.follow.auth.lock();
298        yo_alloc::allow(|| *auth = (user.to_vec(), pass.to_vec()));
299    }
300
301    /// Read back what the link would authenticate with, for `CONFIG GET`.
302    pub(crate) fn with_master_auth<T>(&self, each: impl FnOnce(&[u8], &[u8]) -> T) -> T {
303        let auth = self.follow.auth.lock();
304        each(&auth.0, &auth.1)
305    }
306
307    /// Whether an ordinary client's write is refused while this server is a
308    /// replica, which is Redis's `replica-read-only`.
309    ///
310    /// Writable on a running server, and a change takes effect on the next
311    /// command rather than on the next link, because it is a rule about clients
312    /// and not about the master.
313    pub fn set_replica_read_only(&self, yes: bool) {
314        self.follow.read_only.store(yes, Relaxed);
315    }
316
317    /// The setting on its own, which is what `CONFIG GET` answers whether or not
318    /// this server is a replica.
319    pub(crate) fn replica_read_only_setting(&self) -> bool {
320        self.follow.read_only.load(Relaxed)
321    }
322
323    /// Start following a master, for a server that was told to at startup.
324    ///
325    /// The same thing `REPLICAOF host port` does, reachable before anything has
326    /// connected. It is a separate entry point rather than a command run against
327    /// the server, because the caller has the `Arc` in its hand and a command
328    /// body does not.
329    pub fn follow_master(self: &Arc<Server>, host: &str, port: u16) {
330        self.is_behind();
331        let host = yo_alloc::allow(|| host.to_owned());
332        self.follow_now(Some(Upstream { host, port }));
333    }
334
335    /// Whether the link to the master is up and applying, which is the question
336    /// a `PSYNC` from somebody else asks before it trusts what we would send.
337    #[must_use]
338    pub(crate) fn master_link_up(&self) -> bool {
339        State::from(self.follow.state.load(Relaxed)) == State::Up
340    }
341
342    /// How long this server has been out of touch with its master, in
343    /// milliseconds.
344    ///
345    /// While the link is up that is the time since the last byte off it, and
346    /// while it is down it is the time since it went down, which is the
347    /// reference's own pair and is what a failover measures a replica's data
348    /// against. A server following nobody has not been out of touch with
349    /// anybody, so it answers nought.
350    pub(super) fn master_silence(&self, now: u64) -> u64 {
351        if !self.following() {
352            return 0;
353        }
354        let since = if State::from(self.follow.state.load(Relaxed)) == State::Up {
355            self.follow.last_io_ms.load(Relaxed)
356        } else {
357            self.follow.down_ms.load(Relaxed)
358        };
359        now.saturating_sub(since)
360    }
361
362    /// Stop following anybody, which is `REPLICAOF NO ONE` and the two ways a
363    /// failover ends up back where it started.
364    ///
365    /// The promotion is the part that matters: the history that was being
366    /// followed is kept as the second id and a new one is taken, so a replica
367    /// that was following this server through the old master can be handed over
368    /// without a snapshot. See `repl::promote`.
369    pub(super) fn stop_following(self: &Arc<Server>) {
370        if self.following() {
371            self.promote();
372        }
373        self.follow_now(None);
374    }
375
376    /// Start following the server a failover picked.
377    ///
378    /// Two things make this different from `REPLICAOF host port`. There is no
379    /// promotion, because this server is handing its history over rather than
380    /// starting a new one, and the next `PSYNC` asks to carry on from where this
381    /// server has got to rather than starting again, because the target is
382    /// caught up to exactly there and a snapshot would be a copy of what it
383    /// already holds.
384    pub(super) fn follow_for_failover(self: &Arc<Server>, host: &str, port: u16) {
385        self.follow.resume.store(true, Relaxed);
386        let host = yo_alloc::allow(|| host.to_owned());
387        self.follow_now(Some(Upstream { host, port }));
388    }
389
390    /// Start following, or stop.
391    ///
392    /// The intent is recorded and a thread is started, and the answer goes back
393    /// before the dial has been tried, which is what a real server does: there
394    /// is no reply to hold open while a socket is opened to somewhere that might
395    /// not answer for five seconds.
396    fn follow_now(self: &Arc<Server>, to: Option<Upstream>) {
397        let epoch = self.follow.epoch.fetch_add(1, Relaxed) + 1;
398        {
399            let mut upstream = self.follow.upstream.lock();
400            yo_alloc::allow(|| *upstream = to.clone());
401        }
402        let Some(to) = to else {
403            self.follow.on.store(false, Release);
404            self.follow.state.store(State::None as u8, Relaxed);
405            self.follow.resume.store(false, Relaxed);
406            return;
407        };
408        self.follow.on.store(true, Release);
409        self.follow.state.store(State::Connect as u8, Relaxed);
410        self.follow.down_ms.store(self.clock.now_ms(), Relaxed);
411        let server = Arc::clone(self);
412        yo_alloc::allow(|| {
413            let _ = std::thread::Builder::new()
414                .name(String::from("yo-replica"))
415                .spawn(move || link(&server, epoch, &to));
416        });
417    }
418}
419
420#[cfg(test)]
421impl Server {
422    /// Say this server follows somebody, without a socket and without a thread.
423    ///
424    /// The same idea as `repl::pretend_replica` and for the same reason. What
425    /// the tests below look at is the refusal an ordinary client gets, what
426    /// `INFO` and `ROLE` say, and what a master's own session is let past, and
427    /// every one of those reads the flags rather than the link. So a server told
428    /// this is a replica in every way a test can see, and no port anywhere has
429    /// to be listening.
430    pub(super) fn pretend_following(&self, host: &str, port: u16, up: bool) {
431        {
432            let mut upstream = self.follow.upstream.lock();
433            *upstream = Some(Upstream {
434                host: host.to_owned(),
435                port,
436            });
437        }
438        self.follow.on.store(true, Release);
439        self.follow
440            .state
441            .store(if up { State::Up } else { State::Connect } as u8, Relaxed);
442        self.follow.last_io_ms.store(self.clock.now_ms(), Relaxed);
443        self.follow.down_ms.store(self.clock.now_ms(), Relaxed);
444    }
445
446    /// Say the link to the master went down at this moment, so a test can put
447    /// this server as far out of touch as it likes.
448    pub(super) fn pretend_master_down_at(&self, at: u64) {
449        self.follow.state.store(State::Connect as u8, Relaxed);
450        self.follow.down_ms.store(at, Relaxed);
451    }
452
453    /// Stop pretending, without the promotion `REPLICAOF NO ONE` does.
454    pub(super) fn pretend_master(&self) {
455        self.follow.on.store(false, Release);
456        self.follow.state.store(State::None as u8, Relaxed);
457    }
458}
459
460// ------------------------------------------------------------- the command
461
462/// `REPLICAOF host port` and `REPLICAOF NO ONE`, and `SLAVEOF` for the same.
463///
464/// The two words are the same command under two names, which is Redis's own
465/// arrangement: `SLAVEOF` is what it was called and answering to both is what
466/// stops a decade of scripts breaking. Nothing here reads which name was used.
467pub(super) fn replicaof(server: &Server, args: Args<'_>, out: &mut Out) -> Result<()> {
468    let name = if args.name().eq_ignore_ascii_case(b"slaveof") {
469        "slaveof"
470    } else {
471        "replicaof"
472    };
473    if args.len() != 3 {
474        return Err(args::wrong_arity(name));
475    }
476    // A failover is already deciding who the master is, and two commands that
477    // both decide that are two commands that can disagree. `FAILOVER ABORT` is
478    // the way out, which is why it is the only one.
479    if server.failing_over() {
480        return Err(Error::new(
481            Code::Invalid,
482            "REPLICAOF not allowed while failing over.",
483        ));
484    }
485    let host = args.get(1);
486    let port = args.get(2);
487    // Both words are read before anything is looked up, so a caller who got the
488    // command wrong hears what was wrong with it rather than hearing about the
489    // server it was sent to.
490    let told = if host.eq_ignore_ascii_case(b"no") && port.eq_ignore_ascii_case(b"one") {
491        None
492    } else {
493        // The reference's own sentence, and it is the answer to a port that is
494        // not a number as well as to one that is out of range. So this reads the
495        // digits itself rather than going through `args.int`, whose sentence is
496        // about integers and is the wrong one here.
497        let Some(port) = core::str::from_utf8(port)
498            .ok()
499            .and_then(|w| w.parse::<u16>().ok())
500        else {
501            return Err(Error::new(Code::Invalid, "Invalid master port"));
502        };
503        Some(port)
504    };
505    let Some(shared) = server.myself() else {
506        return Err(Error::new(
507            Code::Invalid,
508            "REPLICAOF is not available on an embedded server",
509        ));
510    };
511    let Some(port) = told else {
512        shared.stop_following();
513        out.ok();
514        return Ok(());
515    };
516    let host = yo_alloc::allow(|| String::from_utf8_lossy(host).into_owned());
517    // Told to follow the master it is already following, and there is nothing to
518    // do. Starting a link anyway would drop the one that is up and take a
519    // snapshot of a server this one is already in step with, which is what a real
520    // server refuses to do and says so in the same words.
521    {
522        let upstream = server.follow.upstream.lock();
523        let same = upstream
524            .as_ref()
525            .is_some_and(|at| at.port == port && at.host == host);
526        if same && server.following() {
527            out.simple(
528                b"OK REPLICAOF would result into synchronization with the master we are already connected with. No operation performed.",
529            );
530            return Ok(());
531        }
532    }
533    shared.follow_now(Some(Upstream { host, port }));
534    out.ok();
535    Ok(())
536}
537
538/// The refusal an ordinary client's write gets on a read only replica.
539pub(super) const READONLY: &str = "READONLY You can't write against a read only replica.";
540
541// ------------------------------------------------------------- the reporting
542
543/// The replica's half of the `Replication` section of `INFO`.
544///
545/// Written between `connected_slaves` and the identity lines, which is where
546/// Redis puts it, so a tool that reads the section in order sees the same shape.
547pub(super) fn info(server: &Server, s: &mut String) {
548    use core::fmt::Write as _;
549    let state = State::from(server.follow.state.load(Relaxed));
550    if state == State::None {
551        return;
552    }
553    let (host, port) = {
554        let upstream = server.follow.upstream.lock();
555        match upstream.as_ref() {
556            Some(up) => (up.host.clone(), up.port),
557            None => (String::new(), 0),
558        }
559    };
560    let now = server.clock.now_ms();
561    let up = state == State::Up;
562    let last = now.saturating_sub(server.follow.last_io_ms.load(Relaxed)) / 1000;
563    let offset = server.repl_offset();
564    let _ = write!(
565        s,
566        "master_host:{host}\r\nmaster_port:{port}\r\n\
567         master_link_status:{}\r\nmaster_last_io_seconds_ago:{}\r\n\
568         master_sync_in_progress:{}\r\n\
569         slave_read_repl_offset:{offset}\r\nslave_repl_offset:{offset}\r\n",
570        if up { "up" } else { "down" },
571        if up { last as i64 } else { -1 },
572        usize::from(state == State::Sync),
573    );
574    if !up {
575        let down = now.saturating_sub(server.follow.down_ms.load(Relaxed)) / 1000;
576        let _ = write!(s, "master_link_down_since_seconds:{down}\r\n");
577    }
578    // Priority and announcement are settings a failover reads and nothing here
579    // acts on, so they are reported at the values a server that was never
580    // configured has. Read only is the one of the three that is real.
581    let _ = write!(
582        s,
583        "slave_priority:100\r\nslave_read_only:{}\r\nreplica_announced:1\r\n",
584        usize::from(server.follow.read_only.load(Relaxed)),
585    );
586}
587
588/// The word `INFO` and `ROLE` lead with, which is the only thing on this server
589/// that two clients could disagree about if they asked at the wrong moment.
590#[must_use]
591pub(super) fn role_word(server: &Server) -> &'static str {
592    if server.following() {
593        "slave"
594    } else {
595        "master"
596    }
597}
598
599/// What `ROLE` answers on a replica.
600///
601/// Five fields: the word, the master's host and port, the link state as one of
602/// Redis's five words, and how much of the stream has been applied. The state
603/// words are not the two `INFO` uses, which is not a tidy arrangement and is the
604/// one every client library already reads.
605pub(super) fn role(server: &Server, out: &mut Out) {
606    let (host, port) = {
607        let upstream = server.follow.upstream.lock();
608        match upstream.as_ref() {
609            Some(up) => (up.host.clone(), up.port),
610            None => (String::new(), 0),
611        }
612    };
613    out.array(5);
614    out.bulk(b"slave");
615    out.bulk(host.as_bytes());
616    out.int(i64::from(port));
617    out.bulk(match State::from(server.follow.state.load(Relaxed)) {
618        State::Up => b"connected".as_slice(),
619        State::Sync => b"sync".as_slice(),
620        _ => b"connect".as_slice(),
621    });
622    out.int(server.repl_offset() as i64);
623}
624
625// ---------------------------------------------------------------- the link
626
627/// The link thread: dial, hand shake, load, follow, and do it again.
628///
629/// Every failure lands in the same place, which is a wait and another dial. That
630/// is the right shape for this because there is nothing else it could do: the
631/// operator asked for this server to follow that one, and a master that is not
632/// answering yet is the ordinary case at startup rather than an error.
633fn link(server: &Arc<Server>, epoch: u64, to: &Upstream) {
634    while server.follow.epoch.load(Relaxed) == epoch && !server.stopping() {
635        let _ = once(server, epoch, to);
636        if server.follow.epoch.load(Relaxed) != epoch {
637            return;
638        }
639        if State::from(server.follow.state.load(Relaxed)) != State::Connect {
640            server.follow.state.store(State::Connect as u8, Relaxed);
641            server.follow.down_ms.store(server.clock.now_ms(), Relaxed);
642        }
643        std::thread::sleep(RETRY);
644    }
645}
646
647/// One attempt: connect, hand shake, take what is offered, follow until it
648/// breaks.
649fn once(server: &Arc<Server>, epoch: u64, to: &Upstream) -> std::io::Result<()> {
650    let mut wire = dial(to)?;
651    handshake(server, &mut wire)?;
652    // Written and not sent through `command`, because what comes back is not one
653    // line the way every other answer in the handshake is: a full resync answers
654    // with a line and then a snapshot, and reading the line here is the same read
655    // either way.
656    // A fourth word on a server that is handing its job over, which is what
657    // tells the other end to stop being a replica and start being the master.
658    // See the `failover` module.
659    let handing_over = server.failover_stage() == super::failover::Stage::InProgress;
660    if server.follow.resume.load(Relaxed) {
661        // The server's own id and offset, which are the master's, because the
662        // last thing applied moved them and nothing else writes them while a
663        // link is up. One past the end, because the number a master reads is the
664        // position of the first byte wanted counted from one. The other side of
665        // the step `repl::psync` takes coming the other way.
666        let id = server.repl_id();
667        let from = (server.repl_offset() + 1).to_string();
668        if handing_over {
669            wire.write(&[b"PSYNC", &id, from.as_bytes(), b"FAILOVER"])?;
670        } else {
671            wire.write(&[b"PSYNC", &id, from.as_bytes()])?;
672        }
673    } else {
674        wire.write(&[b"PSYNC", b"?", b"-1"])?;
675    }
676    // A master that has to fork before it can answer takes as long as the fork
677    // takes, so this is the long wait and not the handshake's short one.
678    let head = wire.line(SYNC_TIMEOUT)?;
679    if head.starts_with(b"+FULLRESYNC ") {
680        full_resync(server, &mut wire, &head[12..])?;
681        server.follow.state.store(State::Up as u8, Relaxed);
682        server.follow.resume.store(true, Relaxed);
683    } else if head.starts_with(b"+CONTINUE") {
684        // A master that has changed its id since we last spoke says the new one
685        // here, and everything from this point is under that id.
686        if let Some(id) = head.get(10..).and_then(fixed_id) {
687            server.adopt(id, server.repl_offset());
688        }
689        server.follow.state.store(State::Up as u8, Relaxed);
690        server.follow.resume.store(true, Relaxed);
691    } else {
692        // A target that would not take the handover, which is a failover that
693        // cannot happen. This server takes its job back and lets the writes it
694        // has been holding through, rather than sitting paused forever waiting
695        // for a server that has already said no.
696        if handing_over {
697            super::failover::abort(server);
698        }
699        return Err(broken("the master would not resynchronise"));
700    }
701    // Answered either way, so the handover is done and the pause lifts.
702    super::failover::landed(server);
703    stream(server, epoch, &mut wire)
704}
705
706/// Read the snapshot and become it.
707///
708/// The header names the history and the position this image is an image as of,
709/// and both are taken as ours: from here on this server's stream is the master's
710/// stream, at the master's offsets, which is what makes a chain underneath it
711/// hand out positions anybody else can honour.
712fn full_resync(server: &Arc<Server>, wire: &mut Link, head: &[u8]) -> std::io::Result<()> {
713    let mut words = head.split(|&b| b == b' ');
714    let id = words
715        .next()
716        .and_then(fixed_id)
717        .ok_or_else(|| broken("the master named no replication id"))?;
718    let offset = words
719        .next()
720        .and_then(|w| core::str::from_utf8(w).ok())
721        .and_then(|w| w.trim().parse::<u64>().ok())
722        .ok_or_else(|| broken("the master named no offset"))?;
723    server.follow.state.store(State::Sync as u8, Relaxed);
724    let image = wire.payload()?;
725    server
726        .load_image(&image, true)
727        .map_err(|e| broken(&format!("the snapshot would not load: {e}")))?;
728    server.adopt(id, offset);
729    Ok(())
730}
731
732/// Follow the stream until it breaks or the link is called off.
733///
734/// Everything the loop does other than run a command is on a clock: it looks at
735/// the epoch to find out whether it is still wanted and it sends an
736/// acknowledgement once a second. Both of those are why the read has a timeout
737/// on it rather than blocking forever on a socket that is quiet.
738fn stream(server: &Arc<Server>, epoch: u64, wire: &mut Link) -> std::io::Result<()> {
739    let mut session = Session::new(server.next_client());
740    session.admit(true);
741    session.serve_master(true);
742    let mut out = Out::new(Proto::Resp2);
743    let mut argv = Argv::new();
744    let limits = Limits::default();
745    let mut acked = Instant::now();
746    let mut sent = 0u64;
747    APPLYING.set(true);
748    let ended = loop {
749        if server.follow.epoch.load(Relaxed) != epoch || server.stopping() {
750            break Ok(());
751        }
752        match argv.decode(wire.held(), &limits) {
753            Err(_) => break Err(broken("the master sent something that is not a command")),
754            Ok(Step::Incomplete) => {
755                if let Err(e) = wire.fill(POLL) {
756                    break Err(e);
757                }
758            }
759            Ok(Step::Command { consumed }) => {
760                let getack = is_getack(&argv, wire.held());
761                if !getack {
762                    apply(server, &mut session, &argv, wire.held(), &mut out);
763                }
764                // Counted before the acknowledgement is written, because the
765                // number the master is asking about includes the question.
766                let bytes = wire.take(consumed);
767                repl::relayed(server, bytes, session.db());
768                server
769                    .follow
770                    .last_io_ms
771                    .store(server.clock.now_ms(), Relaxed);
772                if getack {
773                    acked = Instant::now();
774                    sent = server.repl_offset();
775                    if let Err(e) = wire.ack(sent) {
776                        break Err(e);
777                    }
778                }
779                continue;
780            }
781        }
782        let now = server.repl_offset();
783        if acked.elapsed() >= ACK_EVERY || now != sent {
784            acked = Instant::now();
785            sent = now;
786            if let Err(e) = wire.ack(now) {
787                break Err(e);
788            }
789        }
790    };
791    APPLYING.set(false);
792    super::forget_session(server, &mut session);
793    ended
794}
795
796/// Whether the command sitting at the front of the buffer is the master asking
797/// where we have got to, which is answered rather than run.
798fn is_getack(argv: &Argv, buf: &[u8]) -> bool {
799    argv.len() == 3
800        && argv
801            .arg(buf, 0)
802            .is_some_and(|w| w.eq_ignore_ascii_case(b"replconf"))
803        && argv
804            .arg(buf, 1)
805            .is_some_and(|w| w.eq_ignore_ascii_case(b"getack"))
806}
807
808/// Run one command from the master against this server.
809///
810/// A command the freeze is holding is run again rather than dropped, because a
811/// full resync for a sub-replica is a pause of this server and not a reason to
812/// lose a byte of the master's stream. It is the one place in the tree that
813/// spins, and what it spins on is a snapshot being built, which finishes.
814fn apply(server: &Server, session: &mut Session, argv: &Argv, buf: &[u8], out: &mut Out) {
815    loop {
816        out.clear();
817        let args = Args::new(argv, buf);
818        if super::execute(server, session, args, out) != Flow::Hold {
819            return;
820        }
821        std::thread::sleep(Duration::from_millis(1));
822    }
823}
824
825// --------------------------------------------------------------- the socket
826
827/// The socket and whatever has arrived on it that has not been used yet.
828///
829/// Shared with the slot migration in `cluster::import`, which dials another node
830/// and reads a stream of commands off it exactly the way a replica reads its
831/// master, down to the bare newlines the far side sends while it is getting
832/// ready. Two copies of this would be two places for the same off by one.
833pub(super) struct Link {
834    sock: TcpStream,
835    buf: Vec<u8>,
836}
837
838impl Link {
839    /// What has arrived and not been used.
840    pub(super) fn held(&self) -> &[u8] {
841        &self.buf
842    }
843
844    /// Take the first `n` bytes off the front and answer with them.
845    pub(super) fn take(&mut self, n: usize) -> Vec<u8> {
846        self.buf.drain(..n).collect()
847    }
848
849    /// Read once, waiting at most `wait`.
850    ///
851    /// A timeout is not a failure and answers with nothing added, which is what
852    /// lets the caller look around between reads. End of file is a failure,
853    /// because a master that closed the socket is a link that has to be dialled
854    /// again.
855    pub(super) fn fill(&mut self, wait: Duration) -> std::io::Result<()> {
856        self.sock.set_read_timeout(Some(wait))?;
857        let mut chunk = [0u8; 16 * 1024];
858        match self.sock.read(&mut chunk) {
859            Ok(0) => Err(broken("the master closed the link")),
860            Ok(n) => {
861                self.buf.extend_from_slice(&chunk[..n]);
862                Ok(())
863            }
864            Err(e) if soft(&e) => Ok(()),
865            Err(e) => Err(e),
866        }
867    }
868
869    /// One line, without its newline, waiting at most `wait` in total.
870    pub(super) fn line(&mut self, wait: Duration) -> std::io::Result<Vec<u8>> {
871        let until = Instant::now() + wait;
872        loop {
873            if let Some(at) = self.buf.iter().position(|&b| b == b'\n') {
874                let mut line = self.take(at + 1);
875                while line.last().is_some_and(|&b| b == b'\n' || b == b'\r') {
876                    line.pop();
877                }
878                // A master preparing a snapshot sends bare newlines to keep the
879                // link warm. They are not a reply and are not counted.
880                if line.is_empty() {
881                    continue;
882                }
883                return Ok(line);
884            }
885            if self.buf.len() > LINE_MAX {
886                return Err(broken("the master sent a line with no end to it"));
887            }
888            if Instant::now() >= until {
889                return Err(broken("the master did not answer"));
890            }
891            self.fill(POLL)?;
892        }
893    }
894
895    /// Send a command and read the one line it is answered with.
896    pub(super) fn command(&mut self, parts: &[&[u8]]) -> std::io::Result<Vec<u8>> {
897        self.write(parts)?;
898        self.line(HANDSHAKE_TIMEOUT)
899    }
900
901    /// Send a command and do not wait for anything.
902    pub(super) fn write(&mut self, parts: &[&[u8]]) -> std::io::Result<()> {
903        let mut wire = Vec::with_capacity(32);
904        wire.extend_from_slice(b"*");
905        wire.extend_from_slice(parts.len().to_string().as_bytes());
906        wire.extend_from_slice(b"\r\n");
907        for part in parts {
908            wire.extend_from_slice(b"$");
909            wire.extend_from_slice(part.len().to_string().as_bytes());
910            wire.extend_from_slice(b"\r\n");
911            wire.extend_from_slice(part);
912            wire.extend_from_slice(b"\r\n");
913        }
914        self.sock.write_all(&wire)
915    }
916
917    /// Tell the master how far we have got.
918    fn ack(&mut self, offset: u64) -> std::io::Result<()> {
919        self.write(&[b"REPLCONF", b"ACK", offset.to_string().as_bytes()])
920    }
921
922    /// The snapshot, which is a bulk string with nothing after it.
923    ///
924    /// The one place in the protocol where a bulk string has no newline behind
925    /// it, because everything after the last byte of it is the stream. A master
926    /// that was given `capa eof` would send a different shape here, which is why
927    /// the handshake does not offer it.
928    fn payload(&mut self) -> std::io::Result<Vec<u8>> {
929        let head = self.line(SYNC_TIMEOUT)?;
930        let want = core::str::from_utf8(head.get(1..).unwrap_or_default())
931            .ok()
932            .and_then(|n| n.parse::<usize>().ok())
933            .filter(|_| head.first() == Some(&b'$'))
934            .ok_or_else(|| broken("the master did not say how long the snapshot is"))?;
935        // The clock is on the gap between reads and not on the whole transfer,
936        // because a snapshot that is genuinely large is a link that is working
937        // and a link that stopped mid snapshot is one nothing will ever finish.
938        let mut last = Instant::now();
939        while self.buf.len() < want {
940            let had = self.buf.len();
941            self.fill(POLL)?;
942            if self.buf.len() > had {
943                last = Instant::now();
944            } else if last.elapsed() >= SYNC_TIMEOUT {
945                return Err(broken("the master stopped part way through the snapshot"));
946            }
947        }
948        Ok(self.take(want))
949    }
950}
951
952/// Whether an error means nothing arrived rather than that the link is gone.
953fn soft(e: &std::io::Error) -> bool {
954    matches!(
955        e.kind(),
956        ErrorKind::WouldBlock | ErrorKind::TimedOut | ErrorKind::Interrupted
957    )
958}
959
960/// A link failure with a sentence on it, which nobody reads and which is worth
961/// writing anyway: the moment one of these needs a log line, the sentence is
962/// already there.
963pub(super) fn broken(why: &str) -> std::io::Error {
964    std::io::Error::other(String::from(why))
965}
966
967/// Forty hex characters, or nothing.
968fn fixed_id(word: &[u8]) -> Option<[u8; ID_LEN]> {
969    let word = word.strip_suffix(b"\r").unwrap_or(word);
970    let word = word.get(..ID_LEN)?;
971    word.iter()
972        .all(u8::is_ascii_hexdigit)
973        .then(|| <[u8; ID_LEN]>::try_from(word).ok())
974        .flatten()
975}
976
977/// Open the socket.
978fn dial(to: &Upstream) -> std::io::Result<Link> {
979    connect(&to.host, to.port)
980}
981
982/// Open a socket to a node and wrap it, which is what a slot migration does
983/// twice: once for the channel it takes the changes down and once for the
984/// channel it takes the snapshot down.
985pub(super) fn connect(host: &str, port: u16) -> std::io::Result<Link> {
986    let at = (host, port)
987        .to_socket_addrs()?
988        .next()
989        .ok_or_else(|| broken("the master's address does not resolve"))?;
990    let sock = TcpStream::connect_timeout(&at, DIAL_TIMEOUT)?;
991    sock.set_nodelay(true)?;
992    Ok(Link {
993        sock,
994        buf: Vec::new(),
995    })
996}
997
998/// Everything before `PSYNC`.
999///
1000/// The capabilities are the two a master has to be told about and no more.
1001/// `psync2` says this replica understands a partial resync across a promotion,
1002/// which it does. `eof` is deliberately not offered: it tells a master it may
1003/// send the snapshot without knowing its length first, and the shape that
1004/// arrives then is different enough that not asking for it is cheaper than
1005/// reading it.
1006fn handshake(server: &Server, wire: &mut Link) -> std::io::Result<()> {
1007    wire.command(&[b"PING"])?;
1008    let (user, pass) = {
1009        let auth = server.follow.auth.lock();
1010        auth.clone()
1011    };
1012    if !pass.is_empty() {
1013        let said = if user.is_empty() {
1014            wire.command(&[b"AUTH", &pass])?
1015        } else {
1016            wire.command(&[b"AUTH", &user, &pass])?
1017        };
1018        if said.first() == Some(&b'-') {
1019            return Err(broken("the master would not take the password"));
1020        }
1021    }
1022    let port = server.follow.port.load(Relaxed).to_string();
1023    wire.command(&[b"REPLCONF", b"listening-port", port.as_bytes()])?;
1024    wire.command(&[b"REPLCONF", b"capa", b"psync2"])?;
1025    server
1026        .follow
1027        .last_io_ms
1028        .store(server.clock.now_ms(), Relaxed);
1029    Ok(())
1030}
1031
1032#[cfg(test)]
1033mod tests {
1034    use super::{ID_LEN, State, fixed_id, soft};
1035
1036    #[test]
1037    fn a_replication_id_is_forty_hex_characters_and_nothing_else() {
1038        let good = b"0123456789abcdef0123456789abcdef01234567";
1039        assert_eq!(fixed_id(good).unwrap(), *good);
1040        // The trailing carriage return of the line it was read out of comes off,
1041        // because the caller splits on the space and not on the line ending.
1042        let mut with_cr = good.to_vec();
1043        with_cr.push(b'\r');
1044        assert_eq!(fixed_id(&with_cr).unwrap(), *good);
1045        // Anything longer is read as the first forty, which is what a master
1046        // that appends something we do not know about would send.
1047        let mut longer = good.to_vec();
1048        longer.extend_from_slice(b"more");
1049        assert_eq!(fixed_id(&longer).unwrap(), *good);
1050        // Too short is nothing, and so is the right length with a character in
1051        // it that is not hex.
1052        assert!(fixed_id(&good[..ID_LEN - 1]).is_none());
1053        let mut wrong = good.to_vec();
1054        wrong[7] = b'z';
1055        assert!(fixed_id(&wrong).is_none());
1056        assert!(fixed_id(b"").is_none());
1057    }
1058
1059    #[test]
1060    fn a_state_that_is_not_one_of_the_four_reads_as_no_master() {
1061        assert!(State::from(1) == State::Connect);
1062        assert!(State::from(2) == State::Sync);
1063        assert!(State::from(3) == State::Up);
1064        assert!(State::from(0) == State::None);
1065        assert!(State::from(99) == State::None);
1066    }
1067
1068    #[test]
1069    fn a_read_that_timed_out_is_not_a_broken_link() {
1070        use std::io::{Error, ErrorKind};
1071        assert!(soft(&Error::from(ErrorKind::WouldBlock)));
1072        assert!(soft(&Error::from(ErrorKind::TimedOut)));
1073        assert!(soft(&Error::from(ErrorKind::Interrupted)));
1074        // Everything else is, including the one that means the master hung up.
1075        assert!(!soft(&Error::from(ErrorKind::ConnectionReset)));
1076        assert!(!soft(&Error::from(ErrorKind::UnexpectedEof)));
1077    }
1078}