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    /// Stop following anybody, which is `REPLICAOF NO ONE` and the two ways a
343    /// failover ends up back where it started.
344    ///
345    /// The promotion is the part that matters: the history that was being
346    /// followed is kept as the second id and a new one is taken, so a replica
347    /// that was following this server through the old master can be handed over
348    /// without a snapshot. See `repl::promote`.
349    pub(super) fn stop_following(self: &Arc<Server>) {
350        if self.following() {
351            self.promote();
352        }
353        self.follow_now(None);
354    }
355
356    /// Start following the server a failover picked.
357    ///
358    /// Two things make this different from `REPLICAOF host port`. There is no
359    /// promotion, because this server is handing its history over rather than
360    /// starting a new one, and the next `PSYNC` asks to carry on from where this
361    /// server has got to rather than starting again, because the target is
362    /// caught up to exactly there and a snapshot would be a copy of what it
363    /// already holds.
364    pub(super) fn follow_for_failover(self: &Arc<Server>, host: &str, port: u16) {
365        self.follow.resume.store(true, Relaxed);
366        let host = yo_alloc::allow(|| host.to_owned());
367        self.follow_now(Some(Upstream { host, port }));
368    }
369
370    /// Start following, or stop.
371    ///
372    /// The intent is recorded and a thread is started, and the answer goes back
373    /// before the dial has been tried, which is what a real server does: there
374    /// is no reply to hold open while a socket is opened to somewhere that might
375    /// not answer for five seconds.
376    fn follow_now(self: &Arc<Server>, to: Option<Upstream>) {
377        let epoch = self.follow.epoch.fetch_add(1, Relaxed) + 1;
378        {
379            let mut upstream = self.follow.upstream.lock();
380            yo_alloc::allow(|| *upstream = to.clone());
381        }
382        let Some(to) = to else {
383            self.follow.on.store(false, Release);
384            self.follow.state.store(State::None as u8, Relaxed);
385            self.follow.resume.store(false, Relaxed);
386            return;
387        };
388        self.follow.on.store(true, Release);
389        self.follow.state.store(State::Connect as u8, Relaxed);
390        self.follow.down_ms.store(self.clock.now_ms(), Relaxed);
391        let server = Arc::clone(self);
392        yo_alloc::allow(|| {
393            let _ = std::thread::Builder::new()
394                .name(String::from("yo-replica"))
395                .spawn(move || link(&server, epoch, &to));
396        });
397    }
398}
399
400#[cfg(test)]
401impl Server {
402    /// Say this server follows somebody, without a socket and without a thread.
403    ///
404    /// The same idea as `repl::pretend_replica` and for the same reason. What
405    /// the tests below look at is the refusal an ordinary client gets, what
406    /// `INFO` and `ROLE` say, and what a master's own session is let past, and
407    /// every one of those reads the flags rather than the link. So a server told
408    /// this is a replica in every way a test can see, and no port anywhere has
409    /// to be listening.
410    pub(super) fn pretend_following(&self, host: &str, port: u16, up: bool) {
411        {
412            let mut upstream = self.follow.upstream.lock();
413            *upstream = Some(Upstream {
414                host: host.to_owned(),
415                port,
416            });
417        }
418        self.follow.on.store(true, Release);
419        self.follow
420            .state
421            .store(if up { State::Up } else { State::Connect } as u8, Relaxed);
422        self.follow.last_io_ms.store(self.clock.now_ms(), Relaxed);
423        self.follow.down_ms.store(self.clock.now_ms(), Relaxed);
424    }
425
426    /// Stop pretending, without the promotion `REPLICAOF NO ONE` does.
427    pub(super) fn pretend_master(&self) {
428        self.follow.on.store(false, Release);
429        self.follow.state.store(State::None as u8, Relaxed);
430    }
431}
432
433// ------------------------------------------------------------- the command
434
435/// `REPLICAOF host port` and `REPLICAOF NO ONE`, and `SLAVEOF` for the same.
436///
437/// The two words are the same command under two names, which is Redis's own
438/// arrangement: `SLAVEOF` is what it was called and answering to both is what
439/// stops a decade of scripts breaking. Nothing here reads which name was used.
440pub(super) fn replicaof(server: &Server, args: Args<'_>, out: &mut Out) -> Result<()> {
441    let name = if args.name().eq_ignore_ascii_case(b"slaveof") {
442        "slaveof"
443    } else {
444        "replicaof"
445    };
446    if args.len() != 3 {
447        return Err(args::wrong_arity(name));
448    }
449    // A failover is already deciding who the master is, and two commands that
450    // both decide that are two commands that can disagree. `FAILOVER ABORT` is
451    // the way out, which is why it is the only one.
452    if server.failing_over() {
453        return Err(Error::new(
454            Code::Invalid,
455            "REPLICAOF not allowed while failing over.",
456        ));
457    }
458    let host = args.get(1);
459    let port = args.get(2);
460    // Both words are read before anything is looked up, so a caller who got the
461    // command wrong hears what was wrong with it rather than hearing about the
462    // server it was sent to.
463    let told = if host.eq_ignore_ascii_case(b"no") && port.eq_ignore_ascii_case(b"one") {
464        None
465    } else {
466        // The reference's own sentence, and it is the answer to a port that is
467        // not a number as well as to one that is out of range. So this reads the
468        // digits itself rather than going through `args.int`, whose sentence is
469        // about integers and is the wrong one here.
470        let Some(port) = core::str::from_utf8(port)
471            .ok()
472            .and_then(|w| w.parse::<u16>().ok())
473        else {
474            return Err(Error::new(Code::Invalid, "Invalid master port"));
475        };
476        Some(port)
477    };
478    let Some(shared) = server.myself() else {
479        return Err(Error::new(
480            Code::Invalid,
481            "REPLICAOF is not available on an embedded server",
482        ));
483    };
484    let Some(port) = told else {
485        shared.stop_following();
486        out.ok();
487        return Ok(());
488    };
489    let host = yo_alloc::allow(|| String::from_utf8_lossy(host).into_owned());
490    // Told to follow the master it is already following, and there is nothing to
491    // do. Starting a link anyway would drop the one that is up and take a
492    // snapshot of a server this one is already in step with, which is what a real
493    // server refuses to do and says so in the same words.
494    {
495        let upstream = server.follow.upstream.lock();
496        let same = upstream
497            .as_ref()
498            .is_some_and(|at| at.port == port && at.host == host);
499        if same && server.following() {
500            out.simple(
501                b"OK REPLICAOF would result into synchronization with the master we are already connected with. No operation performed.",
502            );
503            return Ok(());
504        }
505    }
506    shared.follow_now(Some(Upstream { host, port }));
507    out.ok();
508    Ok(())
509}
510
511/// The refusal an ordinary client's write gets on a read only replica.
512pub(super) const READONLY: &str = "READONLY You can't write against a read only replica.";
513
514// ------------------------------------------------------------- the reporting
515
516/// The replica's half of the `Replication` section of `INFO`.
517///
518/// Written between `connected_slaves` and the identity lines, which is where
519/// Redis puts it, so a tool that reads the section in order sees the same shape.
520pub(super) fn info(server: &Server, s: &mut String) {
521    use core::fmt::Write as _;
522    let state = State::from(server.follow.state.load(Relaxed));
523    if state == State::None {
524        return;
525    }
526    let (host, port) = {
527        let upstream = server.follow.upstream.lock();
528        match upstream.as_ref() {
529            Some(up) => (up.host.clone(), up.port),
530            None => (String::new(), 0),
531        }
532    };
533    let now = server.clock.now_ms();
534    let up = state == State::Up;
535    let last = now.saturating_sub(server.follow.last_io_ms.load(Relaxed)) / 1000;
536    let offset = server.repl_offset();
537    let _ = write!(
538        s,
539        "master_host:{host}\r\nmaster_port:{port}\r\n\
540         master_link_status:{}\r\nmaster_last_io_seconds_ago:{}\r\n\
541         master_sync_in_progress:{}\r\n\
542         slave_read_repl_offset:{offset}\r\nslave_repl_offset:{offset}\r\n",
543        if up { "up" } else { "down" },
544        if up { last as i64 } else { -1 },
545        usize::from(state == State::Sync),
546    );
547    if !up {
548        let down = now.saturating_sub(server.follow.down_ms.load(Relaxed)) / 1000;
549        let _ = write!(s, "master_link_down_since_seconds:{down}\r\n");
550    }
551    // Priority and announcement are settings a failover reads and nothing here
552    // acts on, so they are reported at the values a server that was never
553    // configured has. Read only is the one of the three that is real.
554    let _ = write!(
555        s,
556        "slave_priority:100\r\nslave_read_only:{}\r\nreplica_announced:1\r\n",
557        usize::from(server.follow.read_only.load(Relaxed)),
558    );
559}
560
561/// The word `INFO` and `ROLE` lead with, which is the only thing on this server
562/// that two clients could disagree about if they asked at the wrong moment.
563#[must_use]
564pub(super) fn role_word(server: &Server) -> &'static str {
565    if server.following() {
566        "slave"
567    } else {
568        "master"
569    }
570}
571
572/// What `ROLE` answers on a replica.
573///
574/// Five fields: the word, the master's host and port, the link state as one of
575/// Redis's five words, and how much of the stream has been applied. The state
576/// words are not the two `INFO` uses, which is not a tidy arrangement and is the
577/// one every client library already reads.
578pub(super) fn role(server: &Server, out: &mut Out) {
579    let (host, port) = {
580        let upstream = server.follow.upstream.lock();
581        match upstream.as_ref() {
582            Some(up) => (up.host.clone(), up.port),
583            None => (String::new(), 0),
584        }
585    };
586    out.array(5);
587    out.bulk(b"slave");
588    out.bulk(host.as_bytes());
589    out.int(i64::from(port));
590    out.bulk(match State::from(server.follow.state.load(Relaxed)) {
591        State::Up => b"connected".as_slice(),
592        State::Sync => b"sync".as_slice(),
593        _ => b"connect".as_slice(),
594    });
595    out.int(server.repl_offset() as i64);
596}
597
598// ---------------------------------------------------------------- the link
599
600/// The link thread: dial, hand shake, load, follow, and do it again.
601///
602/// Every failure lands in the same place, which is a wait and another dial. That
603/// is the right shape for this because there is nothing else it could do: the
604/// operator asked for this server to follow that one, and a master that is not
605/// answering yet is the ordinary case at startup rather than an error.
606fn link(server: &Arc<Server>, epoch: u64, to: &Upstream) {
607    while server.follow.epoch.load(Relaxed) == epoch && !server.stopping() {
608        let _ = once(server, epoch, to);
609        if server.follow.epoch.load(Relaxed) != epoch {
610            return;
611        }
612        if State::from(server.follow.state.load(Relaxed)) != State::Connect {
613            server.follow.state.store(State::Connect as u8, Relaxed);
614            server.follow.down_ms.store(server.clock.now_ms(), Relaxed);
615        }
616        std::thread::sleep(RETRY);
617    }
618}
619
620/// One attempt: connect, hand shake, take what is offered, follow until it
621/// breaks.
622fn once(server: &Arc<Server>, epoch: u64, to: &Upstream) -> std::io::Result<()> {
623    let mut wire = dial(to)?;
624    handshake(server, &mut wire)?;
625    // Written and not sent through `command`, because what comes back is not one
626    // line the way every other answer in the handshake is: a full resync answers
627    // with a line and then a snapshot, and reading the line here is the same read
628    // either way.
629    // A fourth word on a server that is handing its job over, which is what
630    // tells the other end to stop being a replica and start being the master.
631    // See the `failover` module.
632    let handing_over = server.failover_stage() == super::failover::Stage::InProgress;
633    if server.follow.resume.load(Relaxed) {
634        // The server's own id and offset, which are the master's, because the
635        // last thing applied moved them and nothing else writes them while a
636        // link is up. One past the end, because the number a master reads is the
637        // position of the first byte wanted counted from one. The other side of
638        // the step `repl::psync` takes coming the other way.
639        let id = server.repl_id();
640        let from = (server.repl_offset() + 1).to_string();
641        if handing_over {
642            wire.write(&[b"PSYNC", &id, from.as_bytes(), b"FAILOVER"])?;
643        } else {
644            wire.write(&[b"PSYNC", &id, from.as_bytes()])?;
645        }
646    } else {
647        wire.write(&[b"PSYNC", b"?", b"-1"])?;
648    }
649    // A master that has to fork before it can answer takes as long as the fork
650    // takes, so this is the long wait and not the handshake's short one.
651    let head = wire.line(SYNC_TIMEOUT)?;
652    if head.starts_with(b"+FULLRESYNC ") {
653        full_resync(server, &mut wire, &head[12..])?;
654        server.follow.state.store(State::Up as u8, Relaxed);
655        server.follow.resume.store(true, Relaxed);
656    } else if head.starts_with(b"+CONTINUE") {
657        // A master that has changed its id since we last spoke says the new one
658        // here, and everything from this point is under that id.
659        if let Some(id) = head.get(10..).and_then(fixed_id) {
660            server.adopt(id, server.repl_offset());
661        }
662        server.follow.state.store(State::Up as u8, Relaxed);
663        server.follow.resume.store(true, Relaxed);
664    } else {
665        // A target that would not take the handover, which is a failover that
666        // cannot happen. This server takes its job back and lets the writes it
667        // has been holding through, rather than sitting paused forever waiting
668        // for a server that has already said no.
669        if handing_over {
670            super::failover::abort(server);
671        }
672        return Err(broken("the master would not resynchronise"));
673    }
674    // Answered either way, so the handover is done and the pause lifts.
675    super::failover::landed(server);
676    stream(server, epoch, &mut wire)
677}
678
679/// Read the snapshot and become it.
680///
681/// The header names the history and the position this image is an image as of,
682/// and both are taken as ours: from here on this server's stream is the master's
683/// stream, at the master's offsets, which is what makes a chain underneath it
684/// hand out positions anybody else can honour.
685fn full_resync(server: &Arc<Server>, wire: &mut Link, head: &[u8]) -> std::io::Result<()> {
686    let mut words = head.split(|&b| b == b' ');
687    let id = words
688        .next()
689        .and_then(fixed_id)
690        .ok_or_else(|| broken("the master named no replication id"))?;
691    let offset = words
692        .next()
693        .and_then(|w| core::str::from_utf8(w).ok())
694        .and_then(|w| w.trim().parse::<u64>().ok())
695        .ok_or_else(|| broken("the master named no offset"))?;
696    server.follow.state.store(State::Sync as u8, Relaxed);
697    let image = wire.payload()?;
698    server
699        .load_image(&image, true)
700        .map_err(|e| broken(&format!("the snapshot would not load: {e}")))?;
701    server.adopt(id, offset);
702    Ok(())
703}
704
705/// Follow the stream until it breaks or the link is called off.
706///
707/// Everything the loop does other than run a command is on a clock: it looks at
708/// the epoch to find out whether it is still wanted and it sends an
709/// acknowledgement once a second. Both of those are why the read has a timeout
710/// on it rather than blocking forever on a socket that is quiet.
711fn stream(server: &Arc<Server>, epoch: u64, wire: &mut Link) -> std::io::Result<()> {
712    let mut session = Session::new(server.next_client());
713    session.admit(true);
714    session.serve_master(true);
715    let mut out = Out::new(Proto::Resp2);
716    let mut argv = Argv::new();
717    let limits = Limits::default();
718    let mut acked = Instant::now();
719    let mut sent = 0u64;
720    APPLYING.set(true);
721    let ended = loop {
722        if server.follow.epoch.load(Relaxed) != epoch || server.stopping() {
723            break Ok(());
724        }
725        match argv.decode(wire.held(), &limits) {
726            Err(_) => break Err(broken("the master sent something that is not a command")),
727            Ok(Step::Incomplete) => {
728                if let Err(e) = wire.fill(POLL) {
729                    break Err(e);
730                }
731            }
732            Ok(Step::Command { consumed }) => {
733                let getack = is_getack(&argv, wire.held());
734                if !getack {
735                    apply(server, &mut session, &argv, wire.held(), &mut out);
736                }
737                // Counted before the acknowledgement is written, because the
738                // number the master is asking about includes the question.
739                let bytes = wire.take(consumed);
740                repl::relayed(server, bytes, session.db());
741                server
742                    .follow
743                    .last_io_ms
744                    .store(server.clock.now_ms(), Relaxed);
745                if getack {
746                    acked = Instant::now();
747                    sent = server.repl_offset();
748                    if let Err(e) = wire.ack(sent) {
749                        break Err(e);
750                    }
751                }
752                continue;
753            }
754        }
755        let now = server.repl_offset();
756        if acked.elapsed() >= ACK_EVERY || now != sent {
757            acked = Instant::now();
758            sent = now;
759            if let Err(e) = wire.ack(now) {
760                break Err(e);
761            }
762        }
763    };
764    APPLYING.set(false);
765    super::forget_session(server, &mut session);
766    ended
767}
768
769/// Whether the command sitting at the front of the buffer is the master asking
770/// where we have got to, which is answered rather than run.
771fn is_getack(argv: &Argv, buf: &[u8]) -> bool {
772    argv.len() == 3
773        && argv
774            .arg(buf, 0)
775            .is_some_and(|w| w.eq_ignore_ascii_case(b"replconf"))
776        && argv
777            .arg(buf, 1)
778            .is_some_and(|w| w.eq_ignore_ascii_case(b"getack"))
779}
780
781/// Run one command from the master against this server.
782///
783/// A command the freeze is holding is run again rather than dropped, because a
784/// full resync for a sub-replica is a pause of this server and not a reason to
785/// lose a byte of the master's stream. It is the one place in the tree that
786/// spins, and what it spins on is a snapshot being built, which finishes.
787fn apply(server: &Server, session: &mut Session, argv: &Argv, buf: &[u8], out: &mut Out) {
788    loop {
789        out.clear();
790        let args = Args::new(argv, buf);
791        if super::execute(server, session, args, out) != Flow::Hold {
792            return;
793        }
794        std::thread::sleep(Duration::from_millis(1));
795    }
796}
797
798// --------------------------------------------------------------- the socket
799
800/// The socket and whatever has arrived on it that has not been used yet.
801struct Link {
802    sock: TcpStream,
803    buf: Vec<u8>,
804}
805
806impl Link {
807    /// What has arrived and not been used.
808    fn held(&self) -> &[u8] {
809        &self.buf
810    }
811
812    /// Take the first `n` bytes off the front and answer with them.
813    fn take(&mut self, n: usize) -> Vec<u8> {
814        self.buf.drain(..n).collect()
815    }
816
817    /// Read once, waiting at most `wait`.
818    ///
819    /// A timeout is not a failure and answers with nothing added, which is what
820    /// lets the caller look around between reads. End of file is a failure,
821    /// because a master that closed the socket is a link that has to be dialled
822    /// again.
823    fn fill(&mut self, wait: Duration) -> std::io::Result<()> {
824        self.sock.set_read_timeout(Some(wait))?;
825        let mut chunk = [0u8; 16 * 1024];
826        match self.sock.read(&mut chunk) {
827            Ok(0) => Err(broken("the master closed the link")),
828            Ok(n) => {
829                self.buf.extend_from_slice(&chunk[..n]);
830                Ok(())
831            }
832            Err(e) if soft(&e) => Ok(()),
833            Err(e) => Err(e),
834        }
835    }
836
837    /// One line, without its newline, waiting at most `wait` in total.
838    fn line(&mut self, wait: Duration) -> std::io::Result<Vec<u8>> {
839        let until = Instant::now() + wait;
840        loop {
841            if let Some(at) = self.buf.iter().position(|&b| b == b'\n') {
842                let mut line = self.take(at + 1);
843                while line.last().is_some_and(|&b| b == b'\n' || b == b'\r') {
844                    line.pop();
845                }
846                // A master preparing a snapshot sends bare newlines to keep the
847                // link warm. They are not a reply and are not counted.
848                if line.is_empty() {
849                    continue;
850                }
851                return Ok(line);
852            }
853            if self.buf.len() > LINE_MAX {
854                return Err(broken("the master sent a line with no end to it"));
855            }
856            if Instant::now() >= until {
857                return Err(broken("the master did not answer"));
858            }
859            self.fill(POLL)?;
860        }
861    }
862
863    /// Send a command and read the one line it is answered with.
864    fn command(&mut self, parts: &[&[u8]]) -> std::io::Result<Vec<u8>> {
865        self.write(parts)?;
866        self.line(HANDSHAKE_TIMEOUT)
867    }
868
869    /// Send a command and do not wait for anything.
870    fn write(&mut self, parts: &[&[u8]]) -> std::io::Result<()> {
871        let mut wire = Vec::with_capacity(32);
872        wire.extend_from_slice(b"*");
873        wire.extend_from_slice(parts.len().to_string().as_bytes());
874        wire.extend_from_slice(b"\r\n");
875        for part in parts {
876            wire.extend_from_slice(b"$");
877            wire.extend_from_slice(part.len().to_string().as_bytes());
878            wire.extend_from_slice(b"\r\n");
879            wire.extend_from_slice(part);
880            wire.extend_from_slice(b"\r\n");
881        }
882        self.sock.write_all(&wire)
883    }
884
885    /// Tell the master how far we have got.
886    fn ack(&mut self, offset: u64) -> std::io::Result<()> {
887        self.write(&[b"REPLCONF", b"ACK", offset.to_string().as_bytes()])
888    }
889
890    /// The snapshot, which is a bulk string with nothing after it.
891    ///
892    /// The one place in the protocol where a bulk string has no newline behind
893    /// it, because everything after the last byte of it is the stream. A master
894    /// that was given `capa eof` would send a different shape here, which is why
895    /// the handshake does not offer it.
896    fn payload(&mut self) -> std::io::Result<Vec<u8>> {
897        let head = self.line(SYNC_TIMEOUT)?;
898        let want = core::str::from_utf8(head.get(1..).unwrap_or_default())
899            .ok()
900            .and_then(|n| n.parse::<usize>().ok())
901            .filter(|_| head.first() == Some(&b'$'))
902            .ok_or_else(|| broken("the master did not say how long the snapshot is"))?;
903        // The clock is on the gap between reads and not on the whole transfer,
904        // because a snapshot that is genuinely large is a link that is working
905        // and a link that stopped mid snapshot is one nothing will ever finish.
906        let mut last = Instant::now();
907        while self.buf.len() < want {
908            let had = self.buf.len();
909            self.fill(POLL)?;
910            if self.buf.len() > had {
911                last = Instant::now();
912            } else if last.elapsed() >= SYNC_TIMEOUT {
913                return Err(broken("the master stopped part way through the snapshot"));
914            }
915        }
916        Ok(self.take(want))
917    }
918}
919
920/// Whether an error means nothing arrived rather than that the link is gone.
921fn soft(e: &std::io::Error) -> bool {
922    matches!(
923        e.kind(),
924        ErrorKind::WouldBlock | ErrorKind::TimedOut | ErrorKind::Interrupted
925    )
926}
927
928/// A link failure with a sentence on it, which nobody reads and which is worth
929/// writing anyway: the moment one of these needs a log line, the sentence is
930/// already there.
931fn broken(why: &str) -> std::io::Error {
932    std::io::Error::other(String::from(why))
933}
934
935/// Forty hex characters, or nothing.
936fn fixed_id(word: &[u8]) -> Option<[u8; ID_LEN]> {
937    let word = word.strip_suffix(b"\r").unwrap_or(word);
938    let word = word.get(..ID_LEN)?;
939    word.iter()
940        .all(u8::is_ascii_hexdigit)
941        .then(|| <[u8; ID_LEN]>::try_from(word).ok())
942        .flatten()
943}
944
945/// Open the socket.
946fn dial(to: &Upstream) -> std::io::Result<Link> {
947    let at = (to.host.as_str(), to.port)
948        .to_socket_addrs()?
949        .next()
950        .ok_or_else(|| broken("the master's address does not resolve"))?;
951    let sock = TcpStream::connect_timeout(&at, DIAL_TIMEOUT)?;
952    sock.set_nodelay(true)?;
953    Ok(Link {
954        sock,
955        buf: Vec::new(),
956    })
957}
958
959/// Everything before `PSYNC`.
960///
961/// The capabilities are the two a master has to be told about and no more.
962/// `psync2` says this replica understands a partial resync across a promotion,
963/// which it does. `eof` is deliberately not offered: it tells a master it may
964/// send the snapshot without knowing its length first, and the shape that
965/// arrives then is different enough that not asking for it is cheaper than
966/// reading it.
967fn handshake(server: &Server, wire: &mut Link) -> std::io::Result<()> {
968    wire.command(&[b"PING"])?;
969    let (user, pass) = {
970        let auth = server.follow.auth.lock();
971        auth.clone()
972    };
973    if !pass.is_empty() {
974        let said = if user.is_empty() {
975            wire.command(&[b"AUTH", &pass])?
976        } else {
977            wire.command(&[b"AUTH", &user, &pass])?
978        };
979        if said.first() == Some(&b'-') {
980            return Err(broken("the master would not take the password"));
981        }
982    }
983    let port = server.follow.port.load(Relaxed).to_string();
984    wire.command(&[b"REPLCONF", b"listening-port", port.as_bytes()])?;
985    wire.command(&[b"REPLCONF", b"capa", b"psync2"])?;
986    server
987        .follow
988        .last_io_ms
989        .store(server.clock.now_ms(), Relaxed);
990    Ok(())
991}
992
993#[cfg(test)]
994mod tests {
995    use super::{ID_LEN, State, fixed_id, soft};
996
997    #[test]
998    fn a_replication_id_is_forty_hex_characters_and_nothing_else() {
999        let good = b"0123456789abcdef0123456789abcdef01234567";
1000        assert_eq!(fixed_id(good).unwrap(), *good);
1001        // The trailing carriage return of the line it was read out of comes off,
1002        // because the caller splits on the space and not on the line ending.
1003        let mut with_cr = good.to_vec();
1004        with_cr.push(b'\r');
1005        assert_eq!(fixed_id(&with_cr).unwrap(), *good);
1006        // Anything longer is read as the first forty, which is what a master
1007        // that appends something we do not know about would send.
1008        let mut longer = good.to_vec();
1009        longer.extend_from_slice(b"more");
1010        assert_eq!(fixed_id(&longer).unwrap(), *good);
1011        // Too short is nothing, and so is the right length with a character in
1012        // it that is not hex.
1013        assert!(fixed_id(&good[..ID_LEN - 1]).is_none());
1014        let mut wrong = good.to_vec();
1015        wrong[7] = b'z';
1016        assert!(fixed_id(&wrong).is_none());
1017        assert!(fixed_id(b"").is_none());
1018    }
1019
1020    #[test]
1021    fn a_state_that_is_not_one_of_the_four_reads_as_no_master() {
1022        assert!(State::from(1) == State::Connect);
1023        assert!(State::from(2) == State::Sync);
1024        assert!(State::from(3) == State::Up);
1025        assert!(State::from(0) == State::None);
1026        assert!(State::from(99) == State::None);
1027    }
1028
1029    #[test]
1030    fn a_read_that_timed_out_is_not_a_broken_link() {
1031        use std::io::{Error, ErrorKind};
1032        assert!(soft(&Error::from(ErrorKind::WouldBlock)));
1033        assert!(soft(&Error::from(ErrorKind::TimedOut)));
1034        assert!(soft(&Error::from(ErrorKind::Interrupted)));
1035        // Everything else is, including the one that means the master hung up.
1036        assert!(!soft(&Error::from(ErrorKind::ConnectionReset)));
1037        assert!(!soft(&Error::from(ErrorKind::UnexpectedEof)));
1038    }
1039}