Skip to main content

yo_resp/dispatch/
blocking.rs

1//! The six list commands that wait, and the machinery that lets a client wait.
2//!
3//! `BLPOP` is `LPOP` with one difference: when there is nothing to pop, the
4//! client waits instead of being told no. Everything here is about that wait,
5//! and nothing here knows anything about lists that [`super::lists`] does not
6//! already know.
7//!
8//! # The command is kept, not the client
9//!
10//! A parked client is a [`Waiter`]: the keys it named, what it wanted to do with
11//! them, and when to give up. It is not a suspended stack and it is not a task.
12//! Answering it later is running the same attempt again against a database that
13//! has changed since, which is why [`Want::attempt`] is the whole of both paths.
14//! The command handler calls it once to see whether the client has to wait at
15//! all, and the retry calls it again each time something might have arrived.
16//!
17//! That is also why the six commands cost nothing when they do not block. A
18//! `BLPOP` on a list with something in it runs the same three lines `LPOP` runs
19//! and never touches the waiter list.
20//!
21//! # A slot is reused and a client id is not
22//!
23//! A waiter remembers both. The slot is where that connection's reply buffer
24//! is, and the client id is what says the connection sitting on that slot is
25//! still the one that blocked. The engine takes a waiter off the list when its
26//! connection closes, so the check should never fail, and it is there because
27//! the cost of being wrong about it is a reply written into somebody else's
28//! socket.
29//!
30//! # What wakes a waiter
31//!
32//! Any command at all, which is more than is needed and is not the cost it
33//! sounds like: the engine looks at whether anybody is parked before it looks at
34//! anything else, so a server with no blocked clients pays one load and one
35//! branch per command and nothing more. Narrowing it to writes would save
36//! nothing measurable and would need a rule about which commands can put a list
37//! under a key, which `RENAME`, `COPY` and `RESTORE` all make longer than it
38//! looks.
39//!
40//! What is left is that the waiter list is walked rather than indexed by key, so
41//! a server with a thousand parked workers walks a thousand entries per command.
42//! The fix when that matters is an index from key to waiter, not a different
43//! rule about when to look.
44
45use std::sync::atomic::Ordering::Relaxed;
46use yo_common::lock::Held;
47use yo_common::{Code, Error, Result, num};
48use yo_kv::{Db, End, Entry, Member, Movem, ZEnd};
49
50use super::args::{self, Args, NOT_AN_INT};
51use super::lists::{self, BAD_MPOP_COUNT, BAD_NUMKEYS, end_of, movem_options};
52use super::notify::{self, class};
53use super::repl;
54use super::streams;
55use super::table::Spec;
56use super::zsets;
57use super::{Flow, Server, Session};
58use crate::reply::Out;
59
60/// What Redis says about a timeout it cannot read as a number.
61const NOT_A_FLOAT: &str = "timeout is not a float or out of range";
62/// What it says about one it can read and will not take.
63const NEGATIVE: &str = "timeout is negative";
64/// And about one so far away that milliseconds do not fit in an `i64`.
65const OUT_OF_RANGE: &str = "timeout is out of range";
66/// `WAIT` and `WAITAOF` take their timeout in whole milliseconds rather than in
67/// seconds, so a timeout they cannot read is a different complaint again.
68const TIMEOUT_NOT_AN_INT: &str = "timeout is not an integer or out of range";
69/// What `WAITAOF` says about a `numlocal` that is neither of the two it takes.
70const NOT_ZERO_OR_ONE: &str = "value is out of range, value must between 0 and 1";
71/// And about a negative `numreplicas`.
72const NOT_POSITIVE: &str = "value is out of range, must be positive";
73/// And what it says when asked to wait for a file the server does not keep. The
74/// full stop at the end is Redis's and is the one message in the group that has
75/// one, which is why it is worth writing down rather than tidying up.
76const NO_AOF: &str = "WAITAOF cannot be used when numlocal is set but appendonly is disabled.";
77
78/// Run one blocking command.
79///
80/// `Flow::Block` means nothing was written and the client is on the waiter
81/// list. The engine is what knows which socket that client is on, so it is the
82/// engine that finishes the registration and the engine that stops reading
83/// commands from a connection that is now waiting for one.
84///
85/// # Errors
86///
87/// A timeout that is not a timeout, a direction that is not a direction, and a
88/// key holding something that is not a list.
89pub(super) fn execute(
90    server: &Server,
91    session: &Session,
92    spec: &Spec,
93    args: Args<'_>,
94    out: &mut Out,
95) -> Result<Flow> {
96    // Nothing crosses to a replica unless one of the arms below says otherwise.
97    // A blocking command that parks has done nothing yet, and sending it on as
98    // it arrived would have the replica stop and wait on the master's own link,
99    // which is the one connection that must never stop. The arms that do
100    // something replace this with what they did.
101    if repl::armed() {
102        repl::nothing();
103    }
104    // The two that wait on replication rather than on a key. They are here
105    // because they carry the blocking flag and that flag is what routes a
106    // command to this file, and they leave immediately because there is nothing
107    // for them to wait for yet. See [`replication`] for what they answer.
108    if spec.name == "wait" || spec.name == "waitaof" {
109        return replication(server, spec.name, args, out).map(|()| Flow::Continue);
110    }
111    let now = server.now_ms();
112    // The two stream reads, which are here for the same reason the list six
113    // are and leave through a different door. `BLOCK` is optional on both, so
114    // where `BLPOP` always has a timeout to read, `XREAD` may have been told to
115    // answer now and take nothing for an answer. That is the difference between
116    // parking and writing the null, and it cannot be said with a deadline of
117    // `None`, which already means wait for as long as it takes.
118    if spec.name == "xread" || spec.name == "xreadgroup" {
119        let db = session.db();
120        let want = streams::parse_read(spec.name, args, server.striped(db), now)?;
121        let block = Block::xread(want.keys, want.reads);
122        if block.now(server.striped(db), db, now, out)? {
123            return Ok(Flow::Continue);
124        }
125        // A script is the other way there is nothing to wait for. It cannot
126        // park, because the thing it would be waiting for is a command from
127        // another client and the script is what that client is queued behind,
128        // so the wait would never end. Timing out at once is what a real server
129        // does and it answers the same null a full timeout would have.
130        let Some(deadline) = want.wait.filter(|_| !session.scripted()) else {
131            // No `BLOCK` at all, so nothing arriving is the answer and not a
132            // reason to wait for it. A null array on both protocols, which is
133            // also what a `BLOCK` that runs out sends.
134            out.nil_array();
135            return Ok(Flow::Continue);
136        };
137        server.park(session.id(), db, deadline, block);
138        return Ok(Flow::Block);
139    }
140    let last = args.len() - 1;
141    let (deadline, block) = match spec.name {
142        // The keys are everything between the name and the timeout, so `BLPOP a
143        // b c 0` waits on three keys and answers with whichever one arrives
144        // first rather than with the first one named.
145        "blpop" | "brpop" => {
146            let end = if spec.name == "blpop" {
147                End::Left
148            } else {
149                End::Right
150            };
151            let deadline = timeout(args.get(last), now)?;
152            (deadline, Block::pop((1..last).map(|i| args.get(i)), end))
153        }
154        // The directions before the timeout, which is the order Redis checks
155        // them in, so `BLMOVE a b UP DOWN nonsense` is a syntax error and not a
156        // complaint about the timeout.
157        "blmove" => {
158            let (from, to) = (end_of(args.get(3))?, end_of(args.get(4))?);
159            let deadline = timeout(args.get(5), now)?;
160            (deadline, Block::moved(args.get(1), args.get(2), from, to))
161        }
162        // The same order again with one more thing to read: ends, then timeout,
163        // then the options behind it. `BLMOVEM s d UP DOWN abc` complains about
164        // the directions and `BLMOVEM s d LEFT RIGHT abc COUNT abc BULK` about
165        // the timeout, both measured against 8.10.1 rather than assumed, because
166        // a line wrong in two places has exactly one right answer.
167        "blmovem" => {
168            let (from, to) = (end_of(args.get(3))?, end_of(args.get(4))?);
169            let deadline = timeout(args.get(5), now)?;
170            let mv = movem_options(args, 6, from, to)?;
171            (deadline, Block::movem(args.get(1), args.get(2), mv))
172        }
173        "brpoplpush" => {
174            let deadline = timeout(args.get(3), now)?;
175            (
176                deadline,
177                Block::moved(args.get(1), args.get(2), End::Right, End::Left),
178            )
179        }
180        "blmpop" => mpop(args, now)?,
181        // The sorted set three, which are the same three shapes again with a
182        // different collection under them. `BZPOPMIN` reads its keys up to the
183        // timeout the way `BLPOP` does, and `BZMPOP` counts them the way
184        // `BLMPOP` does.
185        "bzpopmin" | "bzpopmax" => {
186            let end = zsets::end_of_name(spec.name);
187            let deadline = timeout(args.get(last), now)?;
188            (deadline, Block::zpop((1..last).map(|i| args.get(i)), end))
189        }
190        "bzmpop" => {
191            let deadline = timeout(args.get(1), now)?;
192            let (end, from, to, count) = zsets::parse_mpop(args, 2)?;
193            (
194                deadline,
195                Block::zmpop((from..to).map(|i| args.get(i)), end, count),
196            )
197        }
198        // The table and this match are checked against each other by
199        // `cargo xtask check`, so a name reaching here is a table row without a
200        // handler and there is nothing sensible to answer.
201        _ => return Err(args::syntax()),
202    };
203
204    let db = session.db();
205    if block.now(server.striped(db), db, now, out)? {
206        return Ok(Flow::Continue);
207    }
208    // Called from a script, so there is nobody left to deliver what it is
209    // waiting for. The same null a timeout writes, for the reason the stream
210    // reads above give.
211    if session.scripted() {
212        out.nil_array();
213        return Ok(Flow::Continue);
214    }
215    server.park(session.id(), db, deadline, block);
216    Ok(Flow::Block)
217}
218
219/// `BLMPOP timeout numkeys key [key ...] LEFT|RIGHT [COUNT count]`.
220///
221/// The same parse as `LMPOP` shifted along by one, including the check that the
222/// key count leaves room for the direction behind it. `BLMPOP 0 2 k LEFT` names
223/// two keys and only gives one, so the word that should have been the direction
224/// is a key and there is no direction left, which Redis calls a syntax error
225/// rather than anything about counts.
226fn mpop(args: Args<'_>, now: u64) -> Result<(Option<u64>, Block)> {
227    let deadline = timeout(args.get(1), now)?;
228    let numkeys = match args.int(2) {
229        Ok(n) if n > 0 => usize::try_from(n).unwrap_or(usize::MAX),
230        _ => return Err(Error::new(Code::Invalid, BAD_NUMKEYS)),
231    };
232    if numkeys >= args.len() - 3 {
233        return Err(args::syntax());
234    }
235    let at = 3 + numkeys;
236    let end = end_of(args.get(at))?;
237    let mut want = 1usize;
238    if at + 1 < args.len() {
239        if args.len() != at + 3 || !args::is(args.get(at + 1), b"count") {
240            return Err(args::syntax());
241        }
242        want = match args.int(at + 2) {
243            Ok(n) if n > 0 => usize::try_from(n).unwrap_or(usize::MAX),
244            _ => return Err(Error::new(Code::Invalid, BAD_MPOP_COUNT)),
245        };
246    }
247    Ok((
248        deadline,
249        Block::mpop((3..at).map(|i| args.get(i)), end, want),
250    ))
251}
252
253/// `WAIT numreplicas timeout` and `WAITAOF numlocal numreplicas timeout`.
254///
255/// Both of them ask the same question, which is whether this connection's writes
256/// have got somewhere durable. `WAIT` answers how many replicas have said they
257/// are level, which is a real count taken from what each one last acknowledged,
258/// and `WAITAOF` answers two noughts, because `appendonly` is fixed at `no` so
259/// there is no local file to be behind and a replica acknowledges bytes received
260/// rather than bytes written to a file it does not keep either.
261///
262/// Neither of them waits. A client that asked for more replicas than are level
263/// is given the smaller number at once instead of the timeout, which is D-25.
264/// Waiting means holding a connection until an acknowledgement arrives, and the
265/// waiter list here is woken by a key changing and by nothing else, so it is the
266/// same wakeup D-115 is about.
267///
268/// What is not a formality is the argument checking, because that is what a
269/// client sees when it gets something wrong, and the three numbers are read by
270/// three different Redis helpers with three different complaints. `numlocal` is
271/// a range and says so. `numreplicas` is a positive number for `WAITAOF` and any
272/// number at all for `WAIT`, where a negative one is accepted and satisfied on
273/// the spot because any count at all is more than it asked for. The timeout
274/// is milliseconds here and not the seconds the list commands take, so it does
275/// not go through [`timeout`] above, and a negative one is refused with its own
276/// message rather than the range one.
277fn replication(server: &Server, name: &str, args: Args<'_>, out: &mut Out) -> Result<()> {
278    let aof = name == "waitaof";
279    // `WAITAOF` has one number in front of the two `WAIT` has, and everything
280    // after it is in the same place, so the offset is the whole difference.
281    let at = usize::from(aof);
282    let mut wants_local = false;
283    if aof {
284        let local = whole(args.get(1))?;
285        if !(0..=1).contains(&local) {
286            return Err(Error::new(Code::Invalid, NOT_ZERO_OR_ONE));
287        }
288        wants_local = local == 1;
289    }
290    let replicas = whole(args.get(at + 1))?;
291    if aof && replicas < 0 {
292        return Err(Error::new(Code::Invalid, NOT_POSITIVE));
293    }
294    let ms = whole(args.get(at + 2)).map_err(|_| Error::new(Code::Invalid, TIMEOUT_NOT_AN_INT))?;
295    if ms < 0 {
296        return Err(Error::new(Code::Invalid, NEGATIVE));
297    }
298    // The one complaint here that is about the server rather than about the
299    // arguments, and the reason it comes last is that Redis reads all three
300    // arguments before it looks at itself. `appendonly` is `no` here and cannot
301    // be set, so asking to wait for a local copy is asking for something that
302    // cannot happen rather than something that has not happened yet.
303    if wants_local {
304        return Err(Error::new(Code::Invalid, NO_AOF));
305    }
306    if aof {
307        // Two integers and not a map, whichever protocol is in use. The local
308        // count is first and it is zero for the same reason the other one is:
309        // this server has no append only file to be behind.
310        out.array(2);
311        out.int(0);
312        out.int(0);
313    } else {
314        // Whoever is already level, and never fewer than asked for by waiting:
315        // see [`repl::caught_up`] for why the waiting half is not here yet.
316        let _ = replicas;
317        out.int(repl::caught_up(server) as i64);
318    }
319    Ok(())
320}
321
322/// A whole number argument, with the message Redis gives when it is not one.
323fn whole(arg: &[u8]) -> Result<i64> {
324    num::parse_i64(arg).ok_or_else(|| Error::new(Code::Invalid, NOT_AN_INT))
325}
326
327/// The moment to give up at, or `None` for a wait with no end to it.
328///
329/// Seconds as a float on the wire and a millisecond deadline here. Redis reads
330/// it as a long double, refuses a negative one, multiplies by a thousand and
331/// refuses what will not fit in an `i64`, and treats a timeout of exactly zero
332/// as no timeout at all. All four of those are visible from a client:
333///
334/// - `-0.0` is not negative, so it is accepted, and it is zero, so it waits
335///   forever. `-0.1` is refused.
336/// - `1e400` and `inf` parse, so they are not the not-a-float error, and both
337///   are further away than an `i64` of milliseconds reaches, so they are the out
338///   of range one.
339/// - `0.0000001` is a real timeout however small, so it expires on the next turn
340///   of the loop rather than waiting for anything.
341fn timeout(arg: &[u8], now: u64) -> Result<Option<u64>> {
342    let Some(secs) = num::parse_f64(arg) else {
343        return Err(Error::new(Code::Invalid, NOT_A_FLOAT));
344    };
345    if secs < 0.0 {
346        return Err(Error::new(Code::Invalid, NEGATIVE));
347    }
348    let ms = secs * 1000.0;
349    // `>` rather than a negated `<=`, and the two are not the same: an infinite
350    // timeout is greater than the bound and lands here, while a NaN would be
351    // neither, which is why the parse refuses one before this line is reached.
352    if ms > i64::MAX as f64 {
353        return Err(Error::new(Code::Invalid, OUT_OF_RANGE));
354    }
355    if ms <= 0.0 {
356        return Ok(None);
357    }
358    Ok(Some(now.saturating_add(ms as u64)))
359}
360
361/// What a parked client is still trying to do.
362enum Want {
363    /// `BLPOP` and `BRPOP`: one element off the first key that has one, with the
364    /// reply saying which key that turned out to be.
365    Pop { end: End },
366    /// `BLMOVE` and `BRPOPLPUSH`: one element, onto an end of another list.
367    Move { dst: Vec<u8>, from: End, to: End },
368    /// `BLMOVEM`: a block of them, onto an end of another list.
369    ///
370    /// The only want in this file where how many elements are there decides
371    /// whether the client is ready, rather than just whether any are. `COUNT`
372    /// takes what has arrived and so wakes on the first push, and `EXACTLY`
373    /// waits until the source actually holds the whole block.
374    MoveM { dst: Vec<u8>, mv: Movem },
375    /// `BLMPOP`: up to `count` elements off the first key that has any.
376    Mpop { end: End, count: usize },
377    /// `BZPOPMIN` and `BZPOPMAX`: one member and its score off the first sorted
378    /// set that has one, with the reply saying which key that turned out to be.
379    ZPop { end: ZEnd },
380    /// `BZMPOP`: up to `count` members off the first sorted set that has any.
381    ZMpop { end: ZEnd, count: usize },
382    /// `XREAD BLOCK` and `XREADGROUP BLOCK`: whatever has arrived on any of the
383    /// streams since the ID this asked from.
384    ///
385    /// Unlike the other six this takes nothing away, so several clients parked
386    /// on one stream all get the same entry rather than one of them getting it.
387    /// That is the whole point of a stream over a list, and it costs nothing
388    /// here because the attempt is a read.
389    XRead(streams::Reads),
390}
391
392impl Want {
393    /// Try to do it now.
394    ///
395    /// `Ok(true)` means a reply was written and the client is finished with.
396    /// `Ok(false)` means there was nothing to take and nothing was written.
397    ///
398    /// `strict` is the difference between the two callers. The command handler
399    /// passes `true`, so `BLPOP string 0` is a `WRONGTYPE` on the spot the way
400    /// `LPOP string` is. The retry passes `false`, so a key somebody has since
401    /// made into a set is skipped rather than turned into an error on a command
402    /// that was accepted seconds ago. That is what a running Redis does: a
403    /// `SADD` to a key a client is blocked on leaves it blocked, and it times
404    /// out in its own time.
405    ///
406    /// # Errors
407    ///
408    /// Whatever the keyspace says, which under `strict` includes a key of
409    /// another type.
410    fn attempt(
411        &self,
412        keys: &[Vec<u8>],
413        db: &Db,
414        on: usize,
415        now: u64,
416        out: &mut Out,
417        strict: bool,
418    ) -> Result<bool> {
419        match self {
420            // The one arm that needs to know what time it is, because a group
421            // read records when each entry was handed out. The other six take
422            // an element off a collection and the clock does not come into it.
423            Want::XRead(r) => streams::read(streams::On { db, at: on }, keys, r, now, strict, out),
424            Want::Pop { end } => {
425                for key in keys {
426                    if !ready(db, key, strict)? {
427                        continue;
428                    }
429                    out.array(2);
430                    out.bulk(key);
431                    db.hold(key).pop_into(key, *end, 1, |e| element(out, e))?;
432                    // The key it settled on, which is the first of the ones it
433                    // was given that had anything in it and is not something a
434                    // replica running the same command would arrive at, since by
435                    // then the answer is on its way to a client and the list is
436                    // one shorter. The same reason applies to all five below.
437                    copy(&[lists::popped(*end).as_bytes(), key]);
438                    notify::fire(on, class::LIST, lists::popped(*end), key);
439                    notify::emptied(db, on, key);
440                    return Ok(true);
441                }
442                Ok(false)
443            }
444            Want::Mpop { end, count } => {
445                for key in keys {
446                    if !ready(db, key, strict)? {
447                        continue;
448                    }
449                    out.array(2);
450                    out.bulk(key);
451                    let mark = out.len();
452                    let n = db
453                        .hold(key)
454                        .pop_into(key, *end, *count, |e| element(out, e))?;
455                    out.close_array(mark, n);
456                    copy(&[
457                        lists::popped(*end).as_bytes(),
458                        key,
459                        n.to_string().as_bytes(),
460                    ]);
461                    notify::fire(on, class::LIST, lists::popped(*end), key);
462                    notify::emptied(db, on, key);
463                    return Ok(true);
464                }
465                Ok(false)
466            }
467            // Three elements and not two, because `BZPOPMIN` puts the key, the
468            // member and the score side by side rather than pairing the last
469            // two. That is Redis's shape and it is not the shape `ZPOPMIN` has.
470            Want::ZPop { end } => {
471                for key in keys {
472                    if !zready(db, key, strict)? {
473                        continue;
474                    }
475                    out.array(3);
476                    out.bulk(key);
477                    db.hold(key).zpop(key, *end, 1, |m, sc| {
478                        member(out, m);
479                        out.double(sc);
480                    })?;
481                    copy(&[zsets::popped(*end).as_bytes(), key]);
482                    notify::fire(on, class::ZSET, zsets::popped(*end), key);
483                    notify::emptied(db, on, key);
484                    return Ok(true);
485                }
486                Ok(false)
487            }
488            Want::ZMpop { end, count } => {
489                for key in keys {
490                    if !zready(db, key, strict)? {
491                        continue;
492                    }
493                    out.array(2);
494                    out.bulk(key);
495                    let mark = out.len();
496                    let n = db.hold(key).zpop(key, *end, *count, |m, sc| {
497                        out.array(2);
498                        member(out, m);
499                        out.double(sc);
500                    })?;
501                    out.close_array(mark, n);
502                    copy(&[
503                        zsets::popped(*end).as_bytes(),
504                        key,
505                        n.to_string().as_bytes(),
506                    ]);
507                    notify::fire(on, class::ZSET, zsets::popped(*end), key);
508                    notify::emptied(db, on, key);
509                    return Ok(true);
510                }
511                Ok(false)
512            }
513            // The source's length first, so that an empty source never reaches
514            // the destination's type check. `BLMOVE empty string LEFT RIGHT 0.1`
515            // times out on a running Redis rather than answering `WRONGTYPE`,
516            // because the destination is only looked at once there is something
517            // to put in it, and this order gives that answer.
518            Want::Move { dst, from, to } => {
519                let src = &keys[0];
520                if !ready(db, src, strict)? {
521                    return Ok(false);
522                }
523                match db.lmove(src, dst, *from, *to, |v| out.bulk(v)) {
524                    Ok(true) => {
525                        copy(&[b"LMOVE", src, dst, lists::word(*from), lists::word(*to)]);
526                        // The same two events `LMOVE` says, in the same order,
527                        // because this is `LMOVE` arriving late.
528                        notify::fire(on, class::LIST, lists::pushed(*to), dst);
529                        notify::fire(on, class::LIST, lists::popped(*from), src);
530                        if src != dst {
531                            notify::emptied(db, on, src);
532                        }
533                        Ok(true)
534                    }
535                    // The source had something in it a line ago and this is the
536                    // only thread that could have taken it.
537                    Ok(false) => Ok(false),
538                    Err(e) if strict => Err(e),
539                    // The destination is not a list any more. Nothing was taken,
540                    // because `lmove` checks the destination before it pops, so
541                    // the client goes back to waiting with the queue as it was.
542                    Err(_) => Ok(false),
543                }
544            }
545            // The same shape as `Move` with a different question about the
546            // source. `ready` asks whether there is anything and that is not
547            // enough here, because an `EXACTLY` client is not ready until the
548            // whole block has arrived, and asking it any earlier would take
549            // nothing and answer nothing while looking like it had tried.
550            Want::MoveM { dst, mv } => {
551                let src = &keys[0];
552                let have = match db.hold(src).llen(src) {
553                    Ok(n) => n,
554                    Err(e) if strict => return Err(e),
555                    Err(_) => return Ok(false),
556                };
557                // Not ready is not the same as nothing to do, so the
558                // destination is never looked at from here. `BLMOVEM empty
559                // string LEFT RIGHT 0.1` times out on a running 8.10.1 rather
560                // than answering `WRONGTYPE`, and so does an `EXACTLY` whose
561                // source is short, both of which were measured.
562                if have == 0 || (mv.exactly && have < mv.count) {
563                    return Ok(false);
564                }
565                let mark = out.len();
566                let mut n = 0;
567                match db.lmovem(src, dst, *mv, |v| {
568                    out.bulk(v);
569                    n += 1;
570                }) {
571                    Ok(_) => {}
572                    Err(e) if strict => return Err(e),
573                    // As `Move`: the destination stopped being a list while
574                    // this client waited, and nothing was taken.
575                    Err(_) => {
576                        out.truncate(mark);
577                        return Ok(false);
578                    }
579                }
580                out.close_array(mark, n);
581                // `COUNT` and never `EXACTLY`, because an `EXACTLY` that came up
582                // short moved nothing and never reaches this line, so by here the
583                // two mean the same thing and the plainer one travels.
584                copy(&[
585                    b"LMOVEM",
586                    src,
587                    dst,
588                    lists::word(mv.from),
589                    lists::word(mv.to),
590                    b"COUNT",
591                    n.to_string().as_bytes(),
592                    lists::order_word(mv.order),
593                ]);
594                if src == dst {
595                    notify::fire(on, class::LIST, lists::popped(mv.from), src);
596                    notify::fire(on, class::LIST, lists::pushed(mv.to), src);
597                } else {
598                    notify::fire(on, class::LIST, lists::pushed(mv.to), dst);
599                    notify::fire(on, class::LIST, lists::popped(mv.from), src);
600                    notify::emptied(db, on, src);
601                }
602                Ok(true)
603            }
604        }
605    }
606}
607
608/// Copy this to a replica instead of the command that is running.
609///
610/// Every one of these commands picks a key or a moment that the wire form does
611/// not name, and half of them are answered long after the client sent them, so
612/// none of them can travel as they arrived. The one place all seven decide what
613/// they did is here, which is why the rewrites are here too rather than spread
614/// between the funnel and the waiter list.
615fn copy(parts: &[&[u8]]) {
616    if repl::armed() {
617        repl::rewrite(parts);
618    }
619}
620
621/// Whether this key is a list with something in it.
622///
623/// A key of the wrong type is an error to the command handler and not one to the
624/// retry, which is the whole of what `strict` decides.
625fn ready(db: &Db, key: &[u8], strict: bool) -> Result<bool> {
626    match db.hold(key).llen(key) {
627        Ok(n) => Ok(n > 0),
628        Err(e) if strict => Err(e),
629        Err(_) => Ok(false),
630    }
631}
632
633/// The same for a sorted set, which has its own emptiness to ask about.
634fn zready(db: &Db, key: &[u8], strict: bool) -> Result<bool> {
635    match db.hold(key).zcard(key) {
636        Ok(n) => Ok(n > 0),
637        Err(e) if strict => Err(e),
638        Err(_) => Ok(false),
639    }
640}
641
642/// One element as the client sees it, the same as [`super::lists`] writes it.
643#[inline]
644fn element(out: &mut Out, e: Entry<'_>) {
645    match e {
646        Entry::Int(n) => out.bulk_int(n),
647        Entry::Str(s) => out.bulk(s),
648    }
649}
650
651/// One member as the client sees it, the same as [`super::zsets`] writes it.
652#[inline]
653fn member(out: &mut Out, m: Member<'_>) {
654    match m {
655        Member::Int(n) => out.bulk_int(n),
656        Member::Str(s) => out.bulk(s),
657    }
658}
659
660/// A parsed blocking command, ready to be tried or to be parked.
661pub struct Block {
662    /// The keys, already copied out of the connection's read buffer.
663    ///
664    /// This is the allocation blocking costs and it is once per block rather
665    /// than once per attempt. The arguments are slices of a buffer that is
666    /// reused as soon as the batch is over, and a waiter outlives the batch.
667    keys: Vec<Vec<u8>>,
668    want: Want,
669}
670
671impl Block {
672    /// `BLPOP` and `BRPOP`.
673    fn pop<'a>(keys: impl Iterator<Item = &'a [u8]>, end: End) -> Block {
674        Block {
675            keys: owned(keys),
676            want: Want::Pop { end },
677        }
678    }
679
680    /// `BLMPOP`.
681    fn mpop<'a>(keys: impl Iterator<Item = &'a [u8]>, end: End, count: usize) -> Block {
682        Block {
683            keys: owned(keys),
684            want: Want::Mpop { end, count },
685        }
686    }
687
688    /// `BZPOPMIN` and `BZPOPMAX`.
689    fn zpop<'a>(keys: impl Iterator<Item = &'a [u8]>, end: ZEnd) -> Block {
690        Block {
691            keys: owned(keys),
692            want: Want::ZPop { end },
693        }
694    }
695
696    /// `BZMPOP`.
697    fn zmpop<'a>(keys: impl Iterator<Item = &'a [u8]>, end: ZEnd, count: usize) -> Block {
698        Block {
699            keys: owned(keys),
700            want: Want::ZMpop { end, count },
701        }
702    }
703
704    /// `BLMOVE` and `BRPOPLPUSH`.
705    fn moved(src: &[u8], dst: &[u8], from: End, to: End) -> Block {
706        yo_alloc::allow(|| Block {
707            keys: vec![src.to_vec()],
708            want: Want::Move {
709                dst: dst.to_vec(),
710                from,
711                to,
712            },
713        })
714    }
715
716    /// `BLMOVEM`.
717    fn movem(src: &[u8], dst: &[u8], mv: Movem) -> Block {
718        yo_alloc::allow(|| Block {
719            keys: vec![src.to_vec()],
720            want: Want::MoveM {
721                dst: dst.to_vec(),
722                mv,
723            },
724        })
725    }
726
727    /// Do it now if it can be done now.
728    ///
729    /// # Errors
730    ///
731    /// A key of another type, which is an error rather than a wait.
732    fn now(&self, db: &Db, on: usize, now: u64, out: &mut Out) -> Result<bool> {
733        self.want.attempt(&self.keys, db, on, now, out, true)
734    }
735
736    /// `XREAD BLOCK` and `XREADGROUP BLOCK`, whose keys and IDs were read
737    /// together by [`streams::parse_read`] because neither makes sense alone.
738    fn xread(keys: Vec<Vec<u8>>, reads: streams::Reads) -> Block {
739        Block {
740            keys,
741            want: Want::XRead(reads),
742        }
743    }
744}
745
746/// The keys a blocking command named, copied so they outlive the read buffer.
747fn owned<'a>(keys: impl Iterator<Item = &'a [u8]>) -> Vec<Vec<u8>> {
748    yo_alloc::allow(|| keys.map(<[u8]>::to_vec).collect())
749}
750
751/// One parked client.
752struct Waiter {
753    /// The client id, which is never reused.
754    client: u64,
755    /// The slot its reply buffer is on, which is.
756    conn: u32,
757    /// The thread holding that slot.
758    ///
759    /// A reply goes into a buffer the accepting thread owns, so a waiter can
760    /// only be answered by the thread it blocked on, and a list every thread
761    /// walks has to say which entries are whose. The slot number alone will not
762    /// do it: two threads number their connections from zero.
763    thread: usize,
764    /// The database it was on when it blocked. A push into another database is
765    /// not this client's push, even when the key has the same name.
766    db: usize,
767    /// The millisecond to give up at, or `None` for `BLPOP key 0`, which waits
768    /// for as long as the connection is open.
769    deadline: Option<u64>,
770    keys: Vec<Vec<u8>>,
771    want: Want,
772}
773
774/// Every parked client, oldest first.
775///
776/// The order is the order they blocked in and it is the order they are served
777/// in, which is what makes a queue with several workers on it fair: two clients
778/// blocked on the same key take the two elements a `RPUSH q first second` adds
779/// in the order they arrived. A `Vec` is the right structure for that while the
780/// list is short, and it is short, because a waiter is a client doing nothing.
781#[derive(Default)]
782pub struct Waiters {
783    list: Vec<Waiter>,
784}
785
786/// Where the reply to a parked client has to go.
787#[derive(Debug, Clone, Copy)]
788pub struct Parked {
789    /// The slot holding its reply buffer.
790    pub conn: u32,
791    /// The client that was on that slot when it blocked.
792    pub client: u64,
793}
794
795impl Waiters {
796    /// Whether anybody is waiting.
797    #[must_use]
798    #[inline]
799    pub fn is_empty(&self) -> bool {
800        self.list.is_empty()
801    }
802
803    /// How many clients are parked, which is what `INFO clients` calls
804    /// `blocked_clients`.
805    #[must_use]
806    #[inline]
807    pub fn len(&self) -> usize {
808        self.list.len()
809    }
810
811    /// Copy out the waiters `thread` has to answer, oldest first.
812    ///
813    /// The caller works from the copy rather than from the list, because
814    /// answering a waiter needs a connection's reply buffer and the list is
815    /// behind a lock that another thread is waiting on. It brings its own
816    /// vector, which after the first parked client is a vector it already has
817    /// the room in.
818    pub fn mine(&self, thread: usize, into: &mut Vec<Parked>) {
819        into.clear();
820        yo_alloc::allow(|| {
821            for w in self.list.iter().filter(|w| w.thread == thread) {
822                into.push(Parked {
823                    conn: w.conn,
824                    client: w.client,
825                });
826            }
827        });
828    }
829
830    /// Where a parked client sits in the list.
831    fn find(&self, client: u64) -> Option<usize> {
832        self.list.iter().position(|w| w.client == client)
833    }
834
835    /// The database the waiter at `at` blocked on.
836    fn db_of(&self, at: usize) -> usize {
837        self.list[at].db
838    }
839
840    /// Take off every waiter belonging to a client that has gone, and say how
841    /// many that was.
842    ///
843    /// Called when a connection closes rather than left for the deadline sweep
844    /// to find, because a `BLPOP key 0` on a connection nobody will ever write
845    /// to again has no deadline to be found by. The count goes back because the
846    /// caller keeps its own tally of what its thread has waiting, and a client
847    /// that was never parked has to leave that tally alone.
848    fn forget(&mut self, client: u64) -> usize {
849        let before = self.list.len();
850        self.list.retain(|w| w.client != client);
851        before - self.list.len()
852    }
853
854    /// Say which slot the waiter this client just registered is answered on.
855    ///
856    /// The command layer knows which client blocked and the engine knows which
857    /// slot that client is on, so the slot is filled in afterwards by the half
858    /// that has it. A client can only be parked once, since it is not reading
859    /// commands while it waits, so the search finds the one that was just added.
860    fn bind(&mut self, client: u64, conn: u32) {
861        if let Some(w) = self.list.iter_mut().rev().find(|w| w.client == client) {
862            w.conn = conn;
863        }
864    }
865
866    /// Park a client that could not be answered.
867    ///
868    /// The slot is filled in by [`Waiters::bind`] once the engine has it, so
869    /// this leaves it at zero rather than pretending to know.
870    fn park(&mut self, client: u64, thread: usize, db: usize, deadline: Option<u64>, block: Block) {
871        yo_alloc::allow(|| {
872            self.list.push(Waiter {
873                client,
874                conn: 0,
875                thread,
876                db,
877                deadline,
878                keys: block.keys,
879                want: block.want,
880            });
881        });
882    }
883
884    /// Try to answer the waiter at `at`, and say whether it is finished with.
885    ///
886    /// `true` means a reply is in `out` and the caller should take the waiter
887    /// off the list, which covers both a client that got what it asked for and
888    /// one that ran out of time.
889    ///
890    /// The attempt comes before the deadline, so a push that landed in the same
891    /// millisecond the client gave up in serves it rather than racing it.
892    fn try_serve(&self, at: usize, dbs: &[Db], now: u64, out: &mut Out) -> bool {
893        let w = &self.list[at];
894        let mark = out.len();
895        match w.want.attempt(&w.keys, &dbs[w.db], w.db, now, out, false) {
896            Ok(true) => return true,
897            Ok(false) => {}
898            // `strict` is off, so nothing in there returns an error today.
899            // Putting the buffer back is what makes it safe to be wrong about
900            // that later.
901            Err(_) => out.truncate(mark),
902        }
903        if w.deadline.is_some_and(|d| now >= d) {
904            // A null array for all six, `BLMOVE` and `BRPOPLPUSH` included,
905            // even though what they send when they succeed is a single element.
906            // That is Redis's and it is not what reading the reply schema would
907            // suggest: a RESP2 client sees `*-1` and not `$-1`.
908            out.nil_array();
909            return true;
910        }
911        false
912    }
913}
914
915impl Server {
916    /// The clock reading this batch is working against.
917    #[must_use]
918    pub fn now_ms(&self) -> u64 {
919        self.clock.now_ms()
920    }
921
922    /// Who is parked, for the engine walking the list.
923    ///
924    /// Takes the lock for as long as the answer is held, so a caller that only
925    /// wants to know whether anybody is waiting asks [`Server::parked`] instead
926    /// and does not take it at all.
927    #[must_use]
928    pub fn waiters(&self) -> Held<'_, Waiters> {
929        self.waiters.lock()
930    }
931
932    /// How many clients are parked, without taking the lock.
933    ///
934    /// What `INFO clients` calls `blocked_clients`, and what every command asks
935    /// before it goes looking for somebody to wake.
936    #[must_use]
937    #[inline]
938    pub fn parked(&self) -> usize {
939        self.parked.load(Relaxed)
940    }
941
942    /// How many of the calling thread's clients are parked.
943    ///
944    /// The number to branch on before reaching for the waiter list, because a
945    /// thread can only answer the waiters it parked itself. [`Server::parked`]
946    /// counts the whole server, so branching on that puts every thread through
947    /// the shared lock as soon as one client blocks anywhere.
948    #[must_use]
949    #[inline]
950    pub fn parked_here(&self) -> usize {
951        self.mine().parked.load(Relaxed)
952    }
953
954    /// Park a client on a command that could not be answered yet.
955    ///
956    /// Filed under the calling thread, which is the thread that will answer it,
957    /// because a command runs on the thread that read it and a reply goes back
958    /// into that thread's buffer for the connection.
959    pub(super) fn park(&self, client: u64, db: usize, deadline: Option<u64>, block: Block) {
960        let thread = self.my_slot();
961        let mut list = self.waiters.lock();
962        list.park(client, thread, db, deadline, block);
963        self.note(&list);
964        self.mine().blocked(1);
965    }
966
967    /// Take off every waiter belonging to a client that has gone.
968    ///
969    /// Called on the thread that parked it, which is the only thread that can
970    /// have parked it, so the count of what this thread has waiting comes down
971    /// by however many the list actually held. A thread with nothing waiting
972    /// does not take the lock, which is what keeps a server with one blocked
973    /// client from paying for it on every disconnect on every other thread.
974    pub fn forget_waiters(&self, client: u64) {
975        if self.parked_here() == 0 {
976            return;
977        }
978        let mut list = self.waiters.lock();
979        let gone = list.forget(client);
980        self.note(&list);
981        self.mine().woke(gone);
982    }
983
984    /// Say which slot the waiter this client just registered is answered on.
985    pub fn bind_waiter(&self, client: u64, conn: u32) {
986        self.waiters.lock().bind(client, conn);
987    }
988
989    /// Publish how long the list is now.
990    ///
991    /// Called with the list held and by whoever changed it, which is what keeps
992    /// the number and the list from disagreeing about anything except a change
993    /// that has not finished.
994    fn note(&self, list: &Waiters) {
995        self.parked.store(list.len(), Relaxed);
996    }
997
998    /// Try to answer a parked client, writing into the buffer the engine found
999    /// for it, and say whether it is finished with.
1000    ///
1001    /// The engine cannot reach the databases and this cannot reach the
1002    /// connections, so the two meet here: the caller hands in one connection's
1003    /// reply buffer and gets back whether to unpark the client behind it.
1004    ///
1005    /// By client and not by position, because the caller let go of the list
1006    /// between finding the client and asking about it, and in that gap another
1007    /// thread can take one of its own waiters off and move everything behind it
1008    /// up one. A client that is no longer parked answers `false`, which is the
1009    /// same answer as one that is parked and has nothing waiting for it.
1010    pub fn serve_waiter(&self, client: u64, now: u64, out: &mut Out) -> bool {
1011        let list = self.waiters.lock();
1012        let Some(at) = list.find(client) else {
1013            return false;
1014        };
1015        // Serving a waiter pops an element, which makes garbage, and it happens
1016        // outside `execute` so nothing else has marked the database for the
1017        // maintenance turn.
1018        let db = list.db_of(at);
1019        self.mine().mark(1u64 << db);
1020        // Armed here for the same reason, and it is the one place outside the
1021        // funnel that has to do it. A pop that answers a parked client is a pop
1022        // and says so, and the client whose push woke it has long since had its
1023        // own events published. The database is the waiter's own, since the
1024        // thread running this is not the one the client is on.
1025        let armed = notify::arm(self, db);
1026        // That arming covers the replicas as well, so what is left below is
1027        // sending whatever the pop pushed. A pop that answers a parked client is
1028        // the client's command finally running, so it has to cross like any
1029        // other write, and it can only say so from in here.
1030        let done = list.try_serve(at, &self.dbs, now, out);
1031        drop(list);
1032        repl::swept(self, db);
1033        repl::served(self, db);
1034        notify::drain(self, armed);
1035        done
1036    }
1037}