Skip to main content

yo_resp/dispatch/
clients.rs

1//! Every live connection, in one place any thread can read.
2//!
3//! `CLIENT INFO` only ever describes the connection asking, so it could be
4//! answered out of the session and was. `CLIENT LIST` and `CLIENT KILL` are
5//! about the other connections, and on a server with more than one thread the
6//! other connections belong to somebody else: their sessions are inside another
7//! thread's front, which this thread has no borrow of and must never take one
8//! of. So the part of a connection those two commands report is not kept in the
9//! session at all. It is kept here, in a row the connection owns and every
10//! thread can read.
11//!
12//! # Why the row is atomics and not a lock
13//!
14//! A row has exactly one writer, which is the thread the connection is on, and
15//! any number of readers, which is whoever ran `CLIENT LIST`. That is the
16//! cheapest shape a shared thing can have: a relaxed store is an ordinary store
17//! on every machine yo runs on, so the connection pays a store it was paying
18//! anyway and nothing else. A lock per connection would put an atomic exchange
19//! on the command path for a report almost nobody reads.
20//!
21//! The strings are the exception, because a string is not a word. The six of
22//! them sit behind one small lock per row, and it is taken when a name is set,
23//! when a library announces itself, and when a container command records its
24//! subcommand, none of which is a hot path. A plain command stores the index of
25//! its spec in the table and never goes near the lock.
26//!
27//! # Why the rows are a vector
28//!
29//! A connection opening pushes and a connection closing scans for its id and
30//! lifts that row out, keeping the ones behind it in the order they opened in,
31//! which is the order `CLIENT LIST` reports. That is linear in the number of
32//! clients on a disconnect, which sounds worse than it is: the same walk is what
33//! `CLIENT LIST` does, Redis keeps its clients in a list and walks it in the
34//! same places, and a server with ten thousand connections is doing ten thousand
35//! compares on a socket close and nothing on a command.
36//!
37//! # The pause is here too
38//!
39//! `CLIENT PAUSE` is not about one connection and does not touch a row, but it
40//! is the same shape of problem: one connection arms something that every other
41//! connection on every other thread has to see. It is one word on the server,
42//! read once per command, and it lives beside the rows because `CLIENT` is what
43//! writes it and what clears it.
44
45use std::sync::Arc;
46use std::sync::atomic::Ordering::{Acquire, Relaxed, Release};
47use std::sync::atomic::{AtomicI32, AtomicI64, AtomicU32, AtomicU64, AtomicUsize};
48use yo_common::lock::Lock;
49
50/// The bit in [`Client::flags`] for a connection subscribed to anything, which
51/// the report spells `P`.
52pub(super) const SUBSCRIBED: u32 = 1;
53/// A transaction is open, which the report spells `x`.
54pub(super) const IN_MULTI: u32 = 2;
55/// The socket is a Unix socket, which the report spells `U`.
56pub(super) const UNIX: u32 = 4;
57/// `CLIENT NO-EVICT ON`, which the report spells `e`.
58pub(super) const NO_EVICT: u32 = 8;
59/// `CLIENT NO-TOUCH ON`, which the report spells `T`.
60pub(super) const NO_TOUCH: u32 = 16;
61/// Somebody ran `CLIENT KILL` against this connection and the thread that owns
62/// it has not noticed yet.
63///
64/// Not one of the letters. It is in the same word because it is set by another
65/// thread and read by the owner on a path that is already loading this word.
66pub(super) const KILLED: u32 = 32;
67/// The connection sent `MONITOR` and is being fed every command, which the
68/// report spells `O`.
69///
70/// Redis flags a monitor a replica as well and reports only the `O`, because
71/// that is the letter for a replica that is a monitor rather than a real one.
72/// Here the two are separate bits and a monitor sets only this one, which comes
73/// to the same reported letter by a shorter route.
74pub(super) const MONITOR: u32 = 128;
75/// The connection sent `PSYNC` and is being fed the command stream, which the
76/// report spells `S`.
77///
78/// Set on the connection itself as well as recorded in the replication module,
79/// because `CLIENT LIST` and `CLIENT KILL TYPE replica` both ask the connection
80/// what it is rather than asking the module who it has.
81pub(super) const REPLICA: u32 = 256;
82/// The thread that owns the connection has seen the kill and acted on it.
83///
84/// Set by the owner and read by the owner, so that a connection which cannot be
85/// let go of on the turn it was killed, because commands framed out of its
86/// buffer are still to run, is not counted a second time by the next turn.
87pub(super) const REAPED: u32 = 64;
88
89/// The strings a connection carries, which are the only part of a row that is
90/// not a single word.
91#[derive(Default)]
92pub(super) struct Text {
93    /// Where the client is dialling from, as `ip:port`, or the socket path with
94    /// `:0` after it for a Unix connection, which is Redis's spelling for both.
95    pub(super) peer: Vec<u8>,
96    /// The address on this side, in the same two spellings.
97    pub(super) local: Vec<u8>,
98    /// The name the client gave itself with `CLIENT SETNAME`, empty if none.
99    pub(super) name: Vec<u8>,
100    /// What `CLIENT SETINFO` was told, empty until it is told.
101    pub(super) lib_name: Vec<u8>,
102    pub(super) lib_ver: Vec<u8>,
103    /// The subcommand of the last command, when it had one.
104    pub(super) sub: Vec<u8>,
105    /// The ACL user this connection authenticated as.
106    ///
107    /// Empty means the default user, which is what a connection that never sent
108    /// `AUTH` is, so the common case costs no allocation at all.
109    pub(super) user: Vec<u8>,
110}
111
112/// One connection, as everybody except the connection itself sees it.
113///
114/// Built when the connection is accepted and dropped when the last reader lets
115/// go of it, which is why it is behind an [`Arc`] rather than living in the
116/// table: a `CLIENT LIST` copies the handles out under the lock and then formats
117/// them with the lock let go of, and a connection that closes in between leaves
118/// a row that is still readable rather than a dangling one.
119pub struct Client {
120    /// The client id, which is what `CLIENT KILL ID` names and what never comes
121    /// round again.
122    pub(super) id: u64,
123    /// Which connection slot on which thread's front, so that a kill can be
124    /// carried out by the thread that owns it.
125    ///
126    /// The slot is reused and the id is not, which is why whoever acts on this
127    /// pair checks the id back against the front before it does anything.
128    pub(super) conn: AtomicU32,
129    pub(super) thread: AtomicUsize,
130    /// When the connection was accepted, for `age`.
131    pub(super) since_ms: AtomicU64,
132    /// The descriptor number, or minus one when there is no socket, which is
133    /// every embedded caller.
134    pub(super) fd: AtomicI32,
135    /// Everything about the connection that is a string.
136    pub(super) text: Lock<Text>,
137    /// When it last sent a command, for `idle`.
138    pub(super) last_ms: AtomicU64,
139    /// Bytes read off the socket and bytes handed to it.
140    pub(super) net_in: AtomicU64,
141    pub(super) net_out: AtomicU64,
142    /// Commands run for this connection, and reads that carried at least one.
143    ///
144    /// The pair behind `avg-pipeline-len-sum` and `avg-pipeline-len-cnt`, which
145    /// a client divides one by the other to see how deep the pipelining is.
146    pub(super) cmds: AtomicU64,
147    pub(super) reads: AtomicU64,
148    /// Bytes sitting in the read buffer waiting to be framed, and the room after
149    /// them, which are `qbuf` and `qbuf-free`.
150    ///
151    /// The number as of the last read or flush rather than as of this instant,
152    /// which is the only two moments it can change and so the only two worth a
153    /// store.
154    pub(super) qbuf: AtomicU64,
155    pub(super) qbuf_free: AtomicU64,
156    /// The reply buffer's room, the largest it has been at a flush, and what was
157    /// in it at the last flush, which are `rbs`, `rbp` and `obl`.
158    pub(super) rbs: AtomicU64,
159    pub(super) rbp: AtomicU64,
160    pub(super) obl: AtomicU64,
161    /// The bytes of the arguments of the last command, which is `argv-mem`.
162    pub(super) argv_mem: AtomicU64,
163    /// Where in the command table the last command was, or [`u32::MAX`] before
164    /// the connection has sent one.
165    ///
166    /// An index and not a name because an index is a word, and a word is what a
167    /// reader on another thread can take without a lock. The table outlives
168    /// every connection, so the index is good for as long as the row is.
169    pub(super) spec: AtomicU32,
170    /// Whether [`Text::sub`] is worth reading, so that the common case of a
171    /// command with no subcommand never takes the lock.
172    ///
173    /// Released after the subcommand is written and acquired before it is read,
174    /// which is what stops a reader pairing a new command with the subcommand of
175    /// an older one.
176    pub(super) has_sub: AtomicU32,
177    /// Which database, which protocol, and the four counts the report gives a
178    /// field each.
179    pub(super) db: AtomicU32,
180    pub(super) resp: AtomicU32,
181    pub(super) sub: AtomicU32,
182    pub(super) psub: AtomicU32,
183    pub(super) ssub: AtomicU32,
184    pub(super) watch: AtomicU32,
185    /// How many commands are queued behind `MULTI` and how many bytes they hold,
186    /// where minus one is Redis's spelling for no transaction at all.
187    pub(super) multi: AtomicI64,
188    pub(super) multi_mem: AtomicU64,
189    /// The letters, as bits. See the constants at the top of this module.
190    pub(super) flags: AtomicU32,
191}
192
193impl Client {
194    /// A row for a connection that has just been accepted.
195    pub(super) fn new(id: u64) -> Client {
196        Client {
197            id,
198            conn: AtomicU32::new(u32::MAX),
199            thread: AtomicUsize::new(0),
200            since_ms: AtomicU64::new(0),
201            fd: AtomicI32::new(-1),
202            text: Lock::default(),
203            last_ms: AtomicU64::new(0),
204            net_in: AtomicU64::new(0),
205            net_out: AtomicU64::new(0),
206            cmds: AtomicU64::new(0),
207            reads: AtomicU64::new(0),
208            qbuf: AtomicU64::new(0),
209            qbuf_free: AtomicU64::new(0),
210            rbs: AtomicU64::new(0),
211            rbp: AtomicU64::new(0),
212            obl: AtomicU64::new(0),
213            argv_mem: AtomicU64::new(0),
214            spec: AtomicU32::new(u32::MAX),
215            has_sub: AtomicU32::new(0),
216            db: AtomicU32::new(0),
217            resp: AtomicU32::new(2),
218            sub: AtomicU32::new(0),
219            psub: AtomicU32::new(0),
220            ssub: AtomicU32::new(0),
221            watch: AtomicU32::new(0),
222            multi: AtomicI64::new(-1),
223            multi_mem: AtomicU64::new(0),
224            flags: AtomicU32::new(0),
225        }
226    }
227
228    /// Turn one of the flag bits on or off.
229    ///
230    /// A load, a mask and a store rather than a fetch and modify, which is sound
231    /// because the only bit another thread writes is [`KILLED`] and it is only
232    /// ever turned on. The worst a lost update can do is leave a kill to the next
233    /// command, and every path that acts on a kill is one that runs again.
234    pub(super) fn set_flag(&self, bit: u32, on: bool) {
235        let was = self.flags.load(Relaxed);
236        let now = if on { was | bit } else { was & !bit };
237        if now != was {
238            self.flags.store(now, Relaxed);
239        }
240    }
241
242    /// Whether a bit is on.
243    pub(super) fn flag(&self, bit: u32) -> bool {
244        self.flags.load(Relaxed) & bit != 0
245    }
246
247    /// Ask that this connection be closed, and say whether that was news.
248    ///
249    /// Called by whichever thread ran `CLIENT KILL`, which is very often not the
250    /// thread that owns the connection. `Release` so that the owner, which
251    /// acquires the same word, is looking at a row it can act on.
252    pub(super) fn kill(&self) -> bool {
253        let was = self.flags.load(Relaxed);
254        if was & KILLED != 0 {
255            return false;
256        }
257        self.flags.store(was | KILLED, Release);
258        true
259    }
260
261    /// Whether a kill is waiting for the thread that owns this connection.
262    pub(super) fn killed(&self) -> bool {
263        self.flags.load(Acquire) & KILLED != 0
264    }
265
266    /// Note what the last command was.
267    ///
268    /// The index goes in with a `Release` when there is a subcommand, after the
269    /// subcommand itself, so a reader that acquires the pair sees them together.
270    /// A command with no subcommand does not go near the lock and does not need
271    /// the fence.
272    pub(super) fn note_command(&self, at: usize, sub: Option<&[u8]>) {
273        match sub {
274            Some(sub) => {
275                yo_alloc::allow(|| {
276                    let mut text = self.text.lock();
277                    text.sub.clear();
278                    text.sub.extend_from_slice(sub);
279                });
280                self.spec.store(at as u32, Relaxed);
281                self.has_sub.store(1, Release);
282            }
283            None => {
284                self.has_sub.store(0, Relaxed);
285                self.spec.store(at as u32, Relaxed);
286            }
287        }
288    }
289
290    /// Replace one of the strings.
291    pub(super) fn set_text(&self, pick: fn(&mut Text) -> &mut Vec<u8>, value: &[u8]) {
292        yo_alloc::allow(|| {
293            let mut text = self.text.lock();
294            let into = pick(&mut text);
295            into.clear();
296            into.extend_from_slice(value);
297        });
298    }
299}
300
301/// Every connection this server has open.
302///
303/// One list and not one per thread, because the two commands that read it want
304/// all of them in the order they were opened, and because a list per thread
305/// would still need a lock each and would give `CLIENT LIST` an interleaving to
306/// undo.
307#[derive(Default)]
308pub(super) struct Clients {
309    rows: Vec<Arc<Client>>,
310}
311
312impl Clients {
313    /// Take a new connection on.
314    fn add(&mut self, row: &Arc<Client>) {
315        yo_alloc::allow(|| self.rows.push(Arc::clone(row)));
316    }
317
318    /// Let go of one, by the id that never comes round again.
319    fn remove(&mut self, id: u64) {
320        if let Some(at) = self.rows.iter().position(|row| row.id == id) {
321            // In order rather than by swapping the last row in, because `CLIENT
322            // LIST` is read in the order the connections were opened on a real
323            // server and people do read it that way. What that costs over the
324            // swap is a move of the pointers after the hole, which is less than
325            // the scan that found the hole.
326            self.rows.remove(at);
327        }
328    }
329
330    /// How many there are.
331    fn len(&self) -> usize {
332        self.rows.len()
333    }
334}
335
336impl super::Server {
337    /// Take a connection's row into the table.
338    ///
339    /// Called once, by whoever accepted it. A session whose row was never
340    /// registered is one no other thread can see, which is every embedded
341    /// caller and every test that builds a session by hand.
342    pub(crate) fn register_client(&self, row: &Arc<Client>) {
343        row.thread.store(self.my_slot(), Relaxed);
344        self.clients.lock().add(row);
345    }
346
347    /// Take it back out, which is the connection ending.
348    pub(crate) fn forget_client(&self, id: u64) {
349        self.clients.lock().remove(id);
350    }
351
352    /// Copy out a handle to every open connection.
353    ///
354    /// The copy is the point. Formatting a report holds no lock, so a connection
355    /// that opens or closes while `CLIENT LIST` is writing does not hold up the
356    /// thread it is on, and the row of one that closed is still there to be
357    /// read.
358    pub(super) fn client_rows(&self) -> Vec<Arc<Client>> {
359        let rows = self.clients.lock();
360        yo_alloc::allow(|| rows.rows.clone())
361    }
362
363    /// How many connections are open, counted from the table.
364    #[must_use]
365    pub fn client_count(&self) -> usize {
366        self.clients.lock().len()
367    }
368
369    /// Note that `n` more connections have been asked to close.
370    pub(super) fn note_kills(&self, n: usize) {
371        if n != 0 {
372            self.kills.fetch_add(n, Release);
373        }
374    }
375
376    /// Whether any thread has a kill to carry out.
377    ///
378    /// One relaxed load, which is what every turn of every loop pays for a
379    /// command nearly nobody sends.
380    #[must_use]
381    pub fn kills(&self) -> usize {
382        self.kills.load(Acquire)
383    }
384
385    /// Note that one of them has been carried out.
386    pub fn kill_done(&self) {
387        self.kills.fetch_sub(1, Release);
388    }
389
390    /// The rows this thread owns that have been asked to close.
391    pub fn my_kills(&self) -> Vec<(u32, u64)> {
392        let mine = self.my_slot();
393        let rows = self.clients.lock();
394        yo_alloc::allow(|| {
395            rows.rows
396                .iter()
397                .filter(|row| row.thread.load(Relaxed) == mine && row.killed() && !row.flag(REAPED))
398                .inspect(|row| row.set_flag(REAPED, true))
399                .map(|row| (row.conn.load(Relaxed), row.id))
400                .collect()
401        })
402    }
403
404    /// Hold commands until `until_ms`, either all of them or only the writes.
405    ///
406    /// A pause already running is not replaced, it is widened. The later of the
407    /// two deadlines wins and the stricter of the two modes wins, so a client
408    /// that asked for everything to stop cannot have that undone by another
409    /// client asking for only the writes to stop. That is Redis's rule and it is
410    /// the one that makes the command safe to use for a failover, which is what
411    /// it is for.
412    ///
413    /// A pause whose deadline has already gone by counts as no pause, so the
414    /// widening only ever looks at one that is still running.
415    pub fn pause(&self, until_ms: u64, all: bool) {
416        // The deadline shares the word with the mode bit, so it has one bit less
417        // than a `u64` to sit in. A pause of a hundred and forty million years
418        // is the same as one of two hundred and eighty for everybody who has to
419        // live through it.
420        let until_ms = until_ms.min(u64::MAX >> 1);
421        let want = (until_ms << 1) | u64::from(all);
422        let mut have = self.pause.load(Relaxed);
423        loop {
424            let live = have != 0 && (have >> 1) > self.now_ms();
425            let next = if live {
426                ((have >> 1).max(until_ms) << 1) | (have & 1) | u64::from(all)
427            } else {
428                want
429            };
430            match self
431                .pause
432                .compare_exchange_weak(have, next, Release, Relaxed)
433            {
434                Ok(_) => return,
435                Err(seen) => have = seen,
436            }
437        }
438    }
439
440    /// Let everybody go, which is `CLIENT UNPAUSE`.
441    ///
442    /// With one exception: a pause a failover armed is not an operator's to
443    /// lift. The whole safety of that command is that no write lands here
444    /// between the moment the target replica catches up and the moment it
445    /// becomes the master, and a `CLIENT UNPAUSE` that opened that window would
446    /// lose whatever went through it. `FAILOVER ABORT` is what lifts that one.
447    /// A client's own pause underneath it goes, which is what was asked for.
448    pub fn unpause(&self) {
449        let keep = if self.failing_over() {
450            (u64::MAX >> 1) << 1
451        } else {
452            0
453        };
454        self.pause.store(keep, Release);
455    }
456
457    /// Whether commands are being held right now, and whether that is all of
458    /// them.
459    ///
460    /// `None` is the answer on a server nobody has paused, and it costs one
461    /// relaxed load and a test against zero, which is what every command pays.
462    /// The deadline is only read on a server where somebody has.
463    #[must_use]
464    pub fn paused(&self, now_ms: u64) -> Option<bool> {
465        let word = self.pause.load(Relaxed);
466        if word == 0 || (word >> 1) <= now_ms {
467            return None;
468        }
469        Some(word & 1 == 1)
470    }
471
472    /// When the pause runs out, in milliseconds, or zero if none is armed.
473    ///
474    /// Read by a test rather than by the command path, which asks the question
475    /// above instead.
476    #[must_use]
477    pub fn pause_ends(&self) -> u64 {
478        self.pause.load(Relaxed) >> 1
479    }
480}