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}