Skip to main content

yo_resp/
engine.rs

1//! Connections, framing and buffers: the seam between the loop and the
2//! commands.
3//!
4//! `yo-reactor` knows how to run a batch and nothing about what a command is.
5//! `dispatch` knows how to run a command and nothing about where the bytes came
6//! from. This module is the piece in between, and it is the piece a server is
7//! missing until it exists: the read buffer a command's arguments point into,
8//! the framing that says where one command ends and the next begins, the reply
9//! buffer that holds an answer until the batch is done, and the state a
10//! connection keeps between the two.
11//!
12//! # Two halves
13//!
14//! [`Wire`] is a pair rather than a thing. The connection half is the front,
15//! and it is in a module of its own that cannot name a [`Server`]: the buffers,
16//! the decoder pool, the framing, the sessions and the queue of framed work.
17//! The other half is the server, which is the databases and the numbers `INFO`
18//! reports. The line matters because it is the line a second thread runs along:
19//! a front belongs to the thread that accepted its connections and is reached by
20//! nothing else, and the server is what the threads come to share. Everything
21//! that needs both is a method on `Wire` and there are three of them, which are
22//! running a command, answering a client that blocked and forgetting a client
23//! that has gone.
24//!
25//! # What a piece of work is
26//!
27//! [`Cmd`] is three numbers: which connection, which decoder holds the
28//! arguments, and where in that connection's buffer they point. It is `Copy`
29//! and twenty four bytes, so it crosses an intake lane without touching the
30//! heap, and it carries no borrow, which is what lets the reactor hold sixty
31//! four of them while the engine owns the bytes they name.
32//!
33//! The decoders are pooled. Framing takes one out of the pool per command,
34//! `run` puts it back, and a connection with a half read command keeps hold of
35//! one so that a bulk arriving in ten reads is decoded once rather than ten
36//! times. In the steady state the pool is as large as the deepest batch and
37//! nothing here allocates at all.
38//!
39//! # One write per connection
40//!
41//! Replies accumulate in the connection's [`Out`](crate::reply::Out) and go out
42//! in [`Wire::flush`], which is one call to the sink per connection touched by
43//! the batch and never one per reply. That is the syscall shape `04` section 2
44//! asks for, and it is the one aki got wrong: its `HGETALL` profile spent 69.7
45//! percent of its time in write syscalls.
46//!
47//! # What is not here
48//!
49//! Sockets. [`Sink`] is where the bytes go and the io_uring reactor implements
50//! it later, which keeps this module testable without a network and keeps the
51//! ring out of the crate that parses the protocol.
52//!
53//! The hash the first walk computes warms the bucket and is then thrown away,
54//! because `yo-kv`'s commands take keys rather than hashes. The prefetch is the
55//! part that is worth a cache miss; hashing a short key twice is a few
56//! nanoseconds, and removing the second one means a hashed form of every
57//! command method, which is a change to make with a benchmark rather than on
58//! the way past.
59//!
60//! ```
61//! use yo_resp::engine::{Recorder, Wire, pump};
62//! use yo_reactor::Reactor;
63//!
64//! let mut r = Reactor::inline(Wire::new(Recorder::new()));
65//! let conn = r.engine_mut().accept();
66//!
67//! r.engine_mut().feed(conn, b"*3\r\n$3\r\nSET\r\n$1\r\nk\r\n$1\r\nv\r\n*2\r\n$3\r\nGET\r\n$1\r\nk\r\n");
68//! let mut batch = Vec::new();
69//! assert_eq!(pump(&mut r, &mut batch), 2);
70//!
71//! assert_eq!(r.engine().sink().sent(conn), b"+OK\r\n$1\r\nv\r\n");
72//! ```
73
74use yo_reactor::{BATCH_MAX, Engine, Reactor};
75
76use crate::dispatch::table;
77use crate::dispatch::{self, Flow, Server};
78use crate::front::{Front, Wrote};
79use crate::proto::Limits;
80use yo_kv::Keyspace;
81
82pub use crate::front::Cmd;
83
84/// Which connection. An index, reused after a connection closes.
85pub type ConnId = u32;
86
87/// Where replies go.
88///
89/// One call per connection per batch, with however many replies are waiting.
90/// The network reactor implements this over io_uring, a test implements it over
91/// a `Vec`, and neither this module nor `dispatch` has to know which.
92pub trait Sink {
93    /// Take up to all of `bytes` for `conn`, and say how many were taken.
94    ///
95    /// Fewer than were offered means the socket is full: what is left stays in
96    /// the connection's reply buffer and is offered again on the next flush.
97    fn write(&mut self, conn: ConnId, bytes: &[u8]) -> usize;
98
99    /// The connection is finished with and its id is about to be reused.
100    fn closed(&mut self, conn: ConnId) {
101        let _ = conn;
102    }
103}
104
105/// A sink that keeps everything, for tests and for a driver with no socket.
106#[derive(Debug, Default)]
107pub struct Recorder {
108    sent: Vec<Vec<u8>>,
109    closed: Vec<ConnId>,
110}
111
112impl Recorder {
113    /// An empty one.
114    #[must_use]
115    pub fn new() -> Recorder {
116        Recorder::default()
117    }
118
119    /// Everything written to a connection so far.
120    #[must_use]
121    pub fn sent(&self, conn: ConnId) -> &[u8] {
122        self.sent.get(conn as usize).map_or(&[], Vec::as_slice)
123    }
124
125    /// Whether a connection was closed.
126    #[must_use]
127    pub fn was_closed(&self, conn: ConnId) -> bool {
128        self.closed.contains(&conn)
129    }
130
131    /// Forget what was written, keeping the room it was written into.
132    pub fn clear(&mut self) {
133        for c in &mut self.sent {
134            c.clear();
135        }
136        self.closed.clear();
137    }
138}
139
140impl Sink for Recorder {
141    fn write(&mut self, conn: ConnId, bytes: &[u8]) -> usize {
142        // A test sink, so the growth here is not on anybody's data path.
143        yo_alloc::allow(|| {
144            if self.sent.len() <= conn as usize {
145                self.sent.resize_with(conn as usize + 1, Vec::new);
146            }
147            self.sent[conn as usize].extend_from_slice(bytes);
148        });
149        bytes.len()
150    }
151
152    fn closed(&mut self, conn: ConnId) {
153        yo_alloc::allow(|| self.closed.push(conn));
154    }
155}
156
157/// The engine: connections on one side, the command layer on the other.
158///
159/// One per shard thread, and it is two halves rather than one thing. The front
160/// is the connections and everything they own, which never leaves the thread
161/// that accepted them. [`Server`] is the databases, and it is what a second
162/// thread would come to share. This type is where the two meet, and every
163/// method on it that is not a one line delegation is a method that genuinely
164/// needs both: running a command, answering a client that blocked, and
165/// forgetting a client that has gone.
166pub struct Wire<S> {
167    front: Front<S>,
168    server: Server,
169}
170
171impl<S: Sink> Wire<S> {
172    /// An engine with an empty server.
173    #[must_use]
174    pub fn new(sink: S) -> Wire<S> {
175        Wire::with_server(Server::new(), sink)
176    }
177
178    /// An engine over a server the caller built, which is how a test gives it a
179    /// clock it can move by hand.
180    #[must_use]
181    pub fn with_server(server: Server, sink: S) -> Wire<S> {
182        Wire {
183            front: Front::new(sink),
184            server,
185        }
186    }
187
188    /// The databases and the numbers `INFO` reports.
189    #[must_use]
190    pub const fn server(&self) -> &Server {
191        &self.server
192    }
193
194    /// The same, for a caller that owns both ends.
195    pub const fn server_mut(&mut self) -> &mut Server {
196        &mut self.server
197    }
198
199    /// Where the replies went.
200    #[must_use]
201    pub const fn sink(&self) -> &S {
202        self.front.sink()
203    }
204
205    /// The same, mutably.
206    pub const fn sink_mut(&mut self) -> &mut S {
207        self.front.sink_mut()
208    }
209
210    /// Change the protocol limits, which is `proto-max-bulk-len` and friends.
211    pub fn set_limits(&mut self, limits: Limits) {
212        self.front.set_limits(limits);
213    }
214
215    /// Open a connection and give back its id.
216    pub fn accept(&mut self) -> ConnId {
217        self.server.stats.clients += 1;
218        self.server.stats.connections += 1;
219        let at = self.front.open();
220        self.note_buffers();
221        at
222    }
223
224    /// Tell the server what the connection buffers are holding now.
225    ///
226    /// The front cannot reach the server, so it keeps the change and this is
227    /// where it is handed over: at the end of whichever call moved a buffer.
228    fn note_buffers(&mut self) {
229        let delta = self.front.buffer_delta();
230        if delta != 0 {
231            self.server.note_conn_bytes(delta);
232        }
233    }
234
235    /// The peer went away.
236    ///
237    /// Whatever is buffered for it is dropped rather than written, and the slot
238    /// comes back as soon as the commands already framed out of its buffer have
239    /// run, because those commands' arguments still point into it.
240    pub fn hangup(&mut self, conn: ConnId) {
241        if !self.front.live(conn) {
242            return;
243        }
244        self.front.mark_gone(conn);
245        // A parked client holds its own commands, and those commands are what
246        // `pending` counts, so leaving it parked here would leave the slot owed
247        // to a connection that is never going to be answered. They go back to
248        // the queue and run as the no-ops a gone connection's commands are.
249        if self.front.blocked(conn) {
250            self.front.unpark(conn);
251        }
252        if self.front.pending(conn) == 0 {
253            self.release(conn);
254        }
255        self.note_buffers();
256    }
257
258    /// Answer everybody who can be answered, and let go of everybody whose
259    /// deadline has passed.
260    ///
261    /// The walk is over the waiter list rather than over the connections, so it
262    /// costs what blocking costs and not what the server costs. Every caller
263    /// checks that somebody is parked before calling, which is the load and the
264    /// branch a server with nobody blocked pays.
265    fn serve_waiters(&mut self) {
266        let now = self.server.now_ms();
267        let mut at = 0;
268        while at < self.server.waiters().len() {
269            let p = self.server.waiters().at(at);
270            // The slot is reused and the client id is not. `release` forgets
271            // waiters, so this should never fire; it is here because being
272            // wrong about it writes a reply into somebody else's socket rather
273            // than dropping one.
274            if !self.front.answers(p.conn, p.client) {
275                self.server.waiters_mut().drop_at(at);
276                continue;
277            }
278            // The front cannot reach the databases and the server cannot reach
279            // the connections, so the two halves are taken apart here and the
280            // one buffer this waiter needs is handed over.
281            let served = {
282                let Wire { server, front } = self;
283                server.serve_waiter(at, now, front.out(p.conn))
284            };
285            if served {
286                self.server.waiters_mut().drop_at(at);
287                self.front.unpark(p.conn);
288                self.front.soil(p.conn);
289            } else {
290                at += 1;
291            }
292        }
293    }
294
295    /// How many connections are open.
296    #[must_use]
297    pub fn clients(&self) -> usize {
298        self.front.clients()
299    }
300
301    /// Commands framed and waiting for the reactor.
302    #[must_use]
303    pub fn ready(&self) -> usize {
304        self.front.ready()
305    }
306
307    /// Connections with a reply that has not gone out yet.
308    ///
309    /// Non zero means a socket was full and what is left is being held for a
310    /// later flush, which a driver waiting on readability needs to know: there
311    /// is work here that no incoming byte will ever wake it up for.
312    #[must_use]
313    pub fn owed(&self) -> usize {
314        self.front.owed()
315    }
316
317    /// Whether a client has asked the server to stop.
318    ///
319    /// The driver reads this once a turn, next to the flag a signal sets, and
320    /// leaves its loop when either is set. Asked after the batch rather than
321    /// during it, so the `SHUTDOWN` and everything that shared its batch is
322    /// finished and written out before anything closes.
323    #[must_use]
324    pub fn stopping(&self) -> bool {
325        self.server.stopping()
326    }
327
328    /// Decoders in the pool, which is the high water mark of one batch.
329    #[must_use]
330    pub fn decoders(&self) -> usize {
331        self.front.decoders()
332    }
333
334    /// What every connection's read and reply buffers are holding.
335    #[must_use]
336    pub fn buffer_bytes(&self) -> usize {
337        self.front.buffer_bytes()
338    }
339
340    /// Take bytes off a connection and frame whatever commands they complete.
341    ///
342    /// Anything left over stays in the connection's buffer, half a command
343    /// included, so the caller hands over whatever the socket gave it without
344    /// looking at it.
345    pub fn feed(&mut self, conn: ConnId, bytes: &[u8]) {
346        self.front.feed(conn, bytes);
347        self.note_buffers();
348    }
349
350    /// Hand the slot and its buffers back, and let the server go of the client.
351    fn release(&mut self, conn: ConnId) {
352        let Some(client) = self.front.close(conn) else {
353            return;
354        };
355        self.forget(client);
356    }
357
358    /// The server side of a connection ending.
359    ///
360    /// It happens in the same call the slot was freed in, and before anything
361    /// else can run, because the slot is handed out again by the next accept
362    /// and a waiter still holding this client id would then be a waiter
363    /// pointing at somebody else's connection.
364    fn forget(&mut self, client: u64) {
365        self.server.waiters_mut().forget(client);
366        self.server.stats.clients = self.server.stats.clients.saturating_sub(1);
367    }
368
369    /// Move up to `max` framed commands into `into`.
370    ///
371    /// The reactor wants a batch it owns, and the front keeps the buffers, so
372    /// what crosses between them is this: numbers, no borrows.
373    pub fn take_ready(&mut self, into: &mut Vec<Cmd>, max: usize) -> usize {
374        self.front.take_ready(into, max)
375    }
376
377    /// Take a clock reading for the whole batch.
378    ///
379    /// `04` section 5: once per turn, never per command, so every command in a
380    /// batch compares against the same millisecond and two keys written
381    /// together expire together.
382    pub fn tick(&mut self) {
383        self.server.refresh_clock();
384    }
385
386    /// Do one batch's worth of housekeeping.
387    ///
388    /// Today that is one segment of arena compaction at most, which is what
389    /// stops a server that rewrites the same keys from holding every version of
390    /// them. It is separate from [`Wire::tick`] because the clock has to move
391    /// before a batch runs and this does not: it can wait until the replies are
392    /// out, and the driver decides when that is.
393    ///
394    /// Per batch and not per turn of the loop. A turn can carry one command or
395    /// a thousand, so a per turn call means the rate at which garbage is
396    /// collected has nothing to do with the rate at which it is made, and on a
397    /// saturated server the second one wins. That was measured: with this on
398    /// the loop's turn the server settled at seven segments for six segments'
399    /// worth of keys, which is where an unloaded process running the same
400    /// writes settled at six.
401    pub fn maintain(&mut self) -> Option<usize> {
402        // Before the compaction and not after it, because the reading the next
403        // batch judges its limit against should be the one taken after the last
404        // batch's writes rather than the one taken after this call's collecting.
405        // Both are true, and the first is the one that is a batch old at worst.
406        // Nothing at all on a server with no `maxmemory`, which is the default.
407        self.server.refresh_memory();
408        // Two fields and a return on a server that has never taken a backup,
409        // which is nearly all of them. It is here rather than on a timer for the
410        // same reason the compaction is: one loop turns everything.
411        self.server.backup_expire();
412        self.server.compact_step()
413    }
414}
415
416impl<S: Sink> Engine for Wire<S> {
417    type Work = Cmd;
418
419    fn key_hash(&self, cmd: &Cmd) -> Option<u64> {
420        // Before the argument list is built, because most of the commands that
421        // get this far and answer `None` answer it on the spec alone, and
422        // building an `Args` to then throw it away is the sort of thing that
423        // does not show up in a profile and does show up in a total.
424        let spec = table::at(cmd.spec)?;
425        if spec.first_key <= 0 {
426            return None;
427        }
428        let args = self.front.args(cmd);
429        // The first key only. A command with more than one, which is `MSET` and
430        // `MGET`, warms the first and takes the miss on the rest; warming all of
431        // them means a hash list per command and that is the batch's own job
432        // once multi key commands are worth measuring.
433        let key = args.opt(spec.first_key as usize)?;
434        Some(Keyspace::hash_of(key))
435    }
436
437    fn prefetch(&self, cmd: &Cmd, hash: u64) {
438        let db = self.front.db(cmd.conn());
439        // The hash picks the stripe as well as the record, so this warms the
440        // line the command is going to read and not a line on some other
441        // stripe. It is the same hash the command itself will route on, which
442        // is why the stripe is worked out from a hash rather than from a key.
443        self.server.striped_ref(db).prefetch_hashed(hash);
444    }
445
446    fn run(&mut self, cmd: Cmd, _hash: Option<u64>) -> yo_reactor::Flow {
447        let conn = cmd.conn();
448        // Framed with the batch that blocked, so it is a command the client sent
449        // before it knew it would be waiting. It keeps its decoder and it keeps
450        // its place in `pending`, which is what stops the buffer it points into
451        // being compacted while it waits.
452        if self.front.blocked(conn) {
453            self.front.park(conn, cmd);
454            return yo_reactor::Flow::Next;
455        }
456
457        // The one place both halves are held at once. The front hands over the
458        // arguments, the session and the reply buffer, the server hands over
459        // the databases, and the command layer sees the two as one call.
460        let flow = if self.front.start(&cmd) {
461            let Wire { front, server } = self;
462            let (args, session, out) = front.parts(&cmd);
463            let spec = table::at(cmd.spec);
464            dispatch::resolved(server, session, spec, args, out)
465        } else {
466            // Nobody to answer, or nobody who should be. The decoder still has
467            // to come back and the slot still has to be released, which is why
468            // this is not an early return.
469            Flow::Continue
470        };
471
472        self.front.done(&cmd);
473        if self.front.gone(conn) {
474            if self.front.pending(conn) == 0 {
475                self.release(conn);
476            }
477        } else {
478            match flow {
479                Flow::Close => {
480                    self.front.quit(conn);
481                    self.front.soil(conn);
482                }
483                // Nothing was written, so there is nothing to flush and no
484                // reason to put this connection on the dirty list. The waiter
485                // carries the slot from here on, and it needs to know which one:
486                // the command layer only ever saw the client id.
487                Flow::Block => {
488                    self.front.block(conn);
489                    let client = self.front.client(conn);
490                    self.server.waiters_mut().bind(client, conn);
491                }
492                Flow::Continue => self.front.soil(conn),
493            }
494        }
495
496        // After each command and not once per batch. A client blocked on two
497        // keys and woken by `RPUSH b` then `RPUSH a` in one pipeline has to
498        // answer with `b`, because that is the push that was in front of it, and
499        // it can only do that if it was served in between the two.
500        if !self.server.waiters().is_empty() {
501            self.serve_waiters();
502        }
503        yo_reactor::Flow::Next
504    }
505
506    fn flush(&mut self) {
507        // The deadline sweep, and it is here because this is the one thing the
508        // driver calls on a turn that ran nothing at all. A client whose timeout
509        // passes while the server is idle is answered within the loop's idle
510        // wait, which is 20ms and is finer than the 10hz Redis checks its own
511        // blocked clients at.
512        if !self.server.waiters().is_empty() {
513            self.server.refresh_clock();
514            self.serve_waiters();
515        }
516
517        // Taken and put back so the loop below can reach the rest of the
518        // engine. The capacity comes back with it, so this is not an
519        // allocation.
520        let mut dirty = self.front.take_dirty();
521        let mut at = 0;
522        while at < dirty.len() {
523            let conn = dirty[at];
524            match self.front.write_out(conn) {
525                // The socket was full. The connection stays on the list with
526                // what is left of its reply, and the next flush offers it
527                // again, which is the whole of the backpressure story here.
528                Wrote::Owed => at += 1,
529                Wrote::Done => {
530                    dirty.swap_remove(at);
531                }
532                Wrote::Ended(client) => {
533                    self.forget(client);
534                    dirty.swap_remove(at);
535                }
536            }
537        }
538        self.front.give_dirty(dirty);
539        self.note_buffers();
540    }
541
542    fn maintain(&mut self, budget: &mut yo_reactor::Budget) {
543        // The clock is the first thing the maintenance slice does, because
544        // everything else in it compares against a time.
545        if !budget.spend(1) {
546            return;
547        }
548        self.tick();
549        // Then the dead keys, which is what stops a cache that writes with a
550        // deadline and never reads back from holding every key it has ever
551        // written. One unit a key looked at, so the slice bounds the sweep the
552        // same way it bounds everything else in here, and a server where nothing
553        // has a deadline spends nothing at all.
554        let looks = budget.left() as usize;
555        let spent = self.server.expire_slice(looks);
556        budget.spend(u32::try_from(spent).unwrap_or(u32::MAX));
557    }
558}
559
560/// Run everything that is framed, in batches, and write the replies.
561///
562/// The inline driver: it is what a caller who is already on the shard thread
563/// uses in place of the loop, and it goes through the same two walks the loop
564/// goes through (`15` section 7). `batch` is the caller's, so a driver in a hot
565/// loop hands the same `Vec` back every time and never allocates.
566pub fn pump<S: Sink>(reactor: &mut Reactor<Wire<S>>, batch: &mut Vec<Cmd>) -> usize {
567    let mut ran = 0;
568    reactor.engine_mut().tick();
569    loop {
570        batch.clear();
571        if reactor.engine_mut().take_ready(batch, BATCH_MAX) == 0 {
572            break;
573        }
574        // The command path, and therefore the thing Y7 is about. The guard is
575        // what arms `yo-alloc`, and it covers dispatch and nothing else: framing
576        // before it and writing the replies after it are both allowed to reach
577        // for the heap, and only running the commands is not.
578        //
579        // It goes here rather than around the whole loop because `take_ready`
580        // and `flush` are on the other side of that line, and because a batch is
581        // the unit a caller can reason about. Under the default mode this is one
582        // relaxed load.
583        let armed = yo_alloc::guard();
584        ran += reactor.execute_all(batch.drain(..));
585        drop(armed);
586        reactor.engine_mut().flush();
587        // After the replies are out, so the batch that made the garbage is not
588        // the batch that waits for it to be collected.
589        reactor.engine_mut().maintain();
590    }
591    // Once more, for a connection with something to say and nothing to run: a
592    // protocol error, or a socket that was full the last time round.
593    reactor.engine_mut().flush();
594    // And once for a turn that ran nothing at all, which is where a server that
595    // has gone quiet catches up on what the last busy turn left behind.
596    reactor.engine_mut().maintain();
597    ran
598}
599
600#[cfg(test)]
601mod tests {
602    use super::*;
603
604    /// The wire bytes for a command, built the way a client would.
605    fn wire(args: &[&[u8]]) -> Vec<u8> {
606        let mut b = format!("*{}\r\n", args.len()).into_bytes();
607        for a in args {
608            b.extend_from_slice(format!("${}\r\n", a.len()).as_bytes());
609            b.extend_from_slice(a);
610            b.extend_from_slice(b"\r\n");
611        }
612        b
613    }
614
615    fn engine() -> (Reactor<Wire<Recorder>>, ConnId, Vec<Cmd>) {
616        let mut r = Reactor::inline(Wire::new(Recorder::new()));
617        let conn = r.engine_mut().accept();
618        (r, conn, Vec::new())
619    }
620
621    /// Where the fixed clock a blocking test moves by hand starts.
622    const START_MS: u64 = 1_000_000;
623
624    /// The same, on a clock the test moves rather than the system's.
625    ///
626    /// A test about a timeout cannot wait for one: waiting a hundred
627    /// milliseconds is a test that fails on a loaded machine and waiting a
628    /// hundred seconds is not a test.
629    fn timed() -> (Reactor<Wire<Recorder>>, ConnId, Vec<Cmd>) {
630        let server = crate::dispatch::Server::with_clock(yo_kv::Clock::fixed(START_MS));
631        let mut r = Reactor::inline(Wire::with_server(server, Recorder::new()));
632        let conn = r.engine_mut().accept();
633        (r, conn, Vec::new())
634    }
635
636    #[test]
637    fn a_pipelined_batch_comes_back_in_order_and_in_one_write() {
638        let (mut r, conn, mut batch) = engine();
639        let mut stream = wire(&[b"SET", b"k", b"v"]);
640        stream.extend(wire(&[b"GET", b"k"]));
641        stream.extend(wire(&[b"INCR", b"n"]));
642
643        r.engine_mut().feed(conn, &stream);
644        assert_eq!(r.engine().ready(), 3);
645        assert_eq!(pump(&mut r, &mut batch), 3);
646
647        assert_eq!(r.engine().sink().sent(conn), b"+OK\r\n$1\r\nv\r\n:1\r\n");
648        assert_eq!(r.engine().ready(), 0);
649    }
650
651    /// The framing has to survive a command arriving in pieces, because that is
652    /// what a socket does.
653    #[test]
654    fn a_command_split_across_reads_resumes_rather_than_restarts() {
655        let (mut r, conn, mut batch) = engine();
656        let bytes = wire(&[b"SET", b"key", b"value"]);
657
658        for at in 1..bytes.len() {
659            r.engine_mut().feed(conn, &bytes[at - 1..at]);
660            assert_eq!(r.engine().ready(), 0, "not a command yet at {at}");
661        }
662        r.engine_mut().feed(conn, &bytes[bytes.len() - 1..]);
663        assert_eq!(r.engine().ready(), 1);
664        assert_eq!(pump(&mut r, &mut batch), 1);
665        assert_eq!(r.engine().sink().sent(conn), b"+OK\r\n");
666
667        // And the value that arrived in single bytes is the value that was
668        // stored, which is the part a naive resume gets wrong.
669        r.engine_mut().feed(conn, &wire(&[b"GET", b"key"]));
670        pump(&mut r, &mut batch);
671        assert_eq!(r.engine().sink().sent(conn), b"+OK\r\n$5\r\nvalue\r\n");
672    }
673
674    #[test]
675    fn two_connections_are_two_sessions_over_one_server() {
676        let (mut r, a, mut batch) = engine();
677        let b = r.engine_mut().accept();
678
679        r.engine_mut().feed(a, &wire(&[b"SELECT", b"3"]));
680        r.engine_mut().feed(a, &wire(&[b"SET", b"k", b"a"]));
681        r.engine_mut().feed(b, &wire(&[b"SET", b"k", b"b"]));
682        r.engine_mut().feed(a, &wire(&[b"GET", b"k"]));
683        r.engine_mut().feed(b, &wire(&[b"GET", b"k"]));
684        pump(&mut r, &mut batch);
685
686        assert_eq!(r.engine().sink().sent(a), b"+OK\r\n+OK\r\n$1\r\na\r\n");
687        assert_eq!(r.engine().sink().sent(b), b"+OK\r\n$1\r\nb\r\n");
688        assert_eq!(r.engine().clients(), 2);
689    }
690
691    #[test]
692    fn quit_is_answered_and_then_the_connection_goes() {
693        let (mut r, conn, mut batch) = engine();
694        r.engine_mut().feed(conn, &wire(&[b"PING"]));
695        r.engine_mut().feed(conn, &wire(&[b"QUIT"]));
696        pump(&mut r, &mut batch);
697
698        assert_eq!(r.engine().sink().sent(conn), b"+PONG\r\n+OK\r\n");
699        assert!(r.engine().sink().was_closed(conn));
700        assert_eq!(r.engine().clients(), 0);
701
702        // The slot comes back, buffers and all.
703        let again = r.engine_mut().accept();
704        assert_eq!(again, conn);
705        assert_eq!(r.engine().clients(), 1);
706    }
707
708    /// Redis's own unit/quit, which caught this: we answered the `QUIT` and
709    /// then ran the `SET` behind it.
710    #[test]
711    fn what_a_client_pipelined_behind_quit_is_never_run() {
712        let (mut r, conn, mut batch) = engine();
713        let mut stream = wire(&[b"QUIT"]);
714        stream.extend(wire(&[b"SET", b"foo", b"bar"]));
715        r.engine_mut().feed(conn, &stream);
716        // Both were framed, because framing happens before anything runs.
717        assert_eq!(r.engine().ready(), 2);
718        pump(&mut r, &mut batch);
719
720        // One reply and not two, and the connection is gone.
721        assert_eq!(r.engine().sink().sent(conn), b"+OK\r\n");
722        assert!(r.engine().sink().was_closed(conn));
723
724        // And the write never happened, which is the part a client can see
725        // after it reconnects. The recorder is cleared first because the next
726        // connection lands back in the slot this one just left, and what was
727        // written to the slot before is still sitting in it.
728        r.engine_mut().sink_mut().clear();
729        let next = r.engine_mut().accept();
730        r.engine_mut().feed(next, &wire(&[b"GET", b"foo"]));
731        pump(&mut r, &mut batch);
732        assert_eq!(r.engine().sink().sent(next), b"$-1\r\n");
733    }
734
735    /// A connection that never said `HELLO` is answered in RESP2, whatever the
736    /// last client in that slot was speaking.
737    ///
738    /// The protocol is kept in the reply buffer and the reply buffer outlives
739    /// the connection, so this is the one piece of connection state that a
740    /// recycled slot used to carry over. A client got a RESP3 null back from
741    /// the first `GET` that missed and could not parse it, which is as bad as a
742    /// compatibility bug gets: nothing the client did caused it and nothing it
743    /// could send would have avoided it.
744    #[test]
745    fn a_slot_that_last_spoke_resp3_answers_the_next_client_in_resp2() {
746        let (mut r, conn, mut batch) = engine();
747        r.engine_mut().feed(conn, &wire(&[b"HELLO", b"3"]));
748        r.engine_mut().feed(conn, &wire(&[b"GET", b"nothing"]));
749        pump(&mut r, &mut batch);
750        assert!(r.engine().sink().sent(conn).ends_with(b"_\r\n"));
751        r.engine_mut().feed(conn, &wire(&[b"QUIT"]));
752        pump(&mut r, &mut batch);
753
754        r.engine_mut().sink_mut().clear();
755        let next = r.engine_mut().accept();
756        assert_eq!(next, conn, "the same slot, which is what this is about");
757        r.engine_mut().feed(next, &wire(&[b"GET", b"nothing"]));
758        pump(&mut r, &mut batch);
759        assert_eq!(r.engine().sink().sent(next), b"$-1\r\n");
760    }
761
762    /// The other way a connection ends, which does not throw anything away.
763    #[test]
764    fn commands_that_arrived_before_a_protocol_error_are_still_answered() {
765        let (mut r, conn, mut batch) = engine();
766        let mut stream = wire(&[b"SET", b"k", b"v"]);
767        stream.extend(wire(&[b"GET", b"k"]));
768        stream.extend_from_slice(b"*1\r\n+notabulk\r\n");
769        r.engine_mut().feed(conn, &stream);
770        pump(&mut r, &mut batch);
771
772        // Both good commands were complete and correct before the stream went
773        // wrong, so both are answered and the error comes after them.
774        let sent = r.engine().sink().sent(conn);
775        assert!(
776            sent.starts_with(b"+OK\r\n$1\r\nv\r\n-ERR Protocol error: "),
777            "{sent:?}"
778        );
779        assert!(r.engine().sink().was_closed(conn));
780    }
781
782    #[test]
783    fn a_protocol_error_is_written_and_closes_the_connection() {
784        let (mut r, conn, mut batch) = engine();
785        // A multibulk that says its first argument is a bulk and then does not.
786        r.engine_mut().feed(conn, b"*1\r\n+notabulk\r\n");
787        pump(&mut r, &mut batch);
788
789        let sent = r.engine().sink().sent(conn);
790        assert!(sent.starts_with(b"-ERR Protocol error: "), "{sent:?}");
791        assert!(r.engine().sink().was_closed(conn));
792        assert_eq!(r.engine().clients(), 0);
793    }
794
795    /// Redis's own `unit/protocol` walks a list of malformed frames, each on a
796    /// fresh connection, which means every one of them after the first runs on
797    /// a decoder that came back to the pool part way through a command.
798    #[test]
799    fn a_decoder_that_came_back_mid_command_starts_the_next_one_clean() {
800        let (mut r, conn, mut batch) = engine();
801        // Stops inside the third argument, on a length that is not a length.
802        r.engine_mut()
803            .feed(conn, b"*3\r\n$3\r\nSET\r\n$1\r\nx\r\n$blabla\r\n");
804        pump(&mut r, &mut batch);
805        let sent = r.engine().sink().sent(conn);
806        assert!(
807            sent.starts_with(b"-ERR Protocol error: invalid bulk length"),
808            "{sent:?}"
809        );
810
811        // The slot that decoder was in is now the slot the next connection
812        // gets, and it has to be at the start of a command and not half way
813        // through the one that went wrong.
814        r.engine_mut().sink_mut().clear();
815        let next = r.engine_mut().accept();
816        r.engine_mut().feed(next, &wire(&[b"GET", b"k"]));
817        pump(&mut r, &mut batch);
818        assert_eq!(r.engine().sink().sent(next), b"$-1\r\n");
819
820        r.engine_mut().sink_mut().clear();
821        let third = r.engine_mut().accept();
822        r.engine_mut().feed(third, b"*1\r\n+notabulk\r\n");
823        pump(&mut r, &mut batch);
824        let sent = r.engine().sink().sent(third);
825        assert!(sent.starts_with(b"-ERR Protocol error: "), "{sent:?}");
826    }
827
828    /// A client that hangs up mid batch is the case that gets a server killed:
829    /// the commands already framed still point into its buffer.
830    #[test]
831    fn a_hangup_with_commands_in_flight_waits_for_them() {
832        let (mut r, conn, mut batch) = engine();
833        r.engine_mut().feed(conn, &wire(&[b"SET", b"k", b"v"]));
834        r.engine_mut().feed(conn, &wire(&[b"GET", b"k"]));
835
836        batch.clear();
837        r.engine_mut().take_ready(&mut batch, BATCH_MAX);
838        r.engine_mut().hangup(conn);
839        assert_eq!(r.engine().clients(), 1, "still holding the buffer");
840
841        r.execute_all(batch.drain(..));
842        r.engine_mut().flush();
843        assert_eq!(r.engine().clients(), 0);
844        assert!(r.engine().sink().sent(conn).is_empty(), "nobody to answer");
845
846        // And the slot is usable again, with the decoders both back in the
847        // pool rather than lost with the connection.
848        let decoders = r.engine().decoders();
849        let again = r.engine_mut().accept();
850        assert_eq!(again, conn);
851        r.engine_mut().feed(again, &wire(&[b"PING"]));
852        pump(&mut r, &mut batch);
853        assert_eq!(r.engine().sink().sent(again), b"+PONG\r\n");
854        assert_eq!(r.engine().decoders(), decoders);
855    }
856
857    /// The claim that the steady state does not allocate, checked the only way
858    /// a library test can check it: nothing grows.
859    #[test]
860    fn the_buffers_and_the_decoder_pool_stop_growing() {
861        let (mut r, conn, mut batch) = engine();
862        let mut stream = Vec::new();
863        for i in 0..32 {
864            stream.extend(wire(&[b"SET", format!("k{i}").as_bytes(), b"v"]));
865        }
866
867        r.engine_mut().feed(conn, &stream);
868        pump(&mut r, &mut batch);
869        let decoders = r.engine().decoders();
870        let batch_cap = batch.capacity();
871
872        for _ in 0..10 {
873            r.engine_mut().feed(conn, &stream);
874            pump(&mut r, &mut batch);
875        }
876        assert_eq!(r.engine().decoders(), decoders, "the pool is reused");
877        assert_eq!(batch.capacity(), batch_cap, "the batch buffer is reused");
878        assert!(
879            decoders <= BATCH_MAX + 1,
880            "{decoders} decoders for 32 commands"
881        );
882    }
883
884    /// The read buffer holds what has not been dealt with yet and nothing else.
885    ///
886    /// A client that pipelines sixteen commands, waits for the sixteen replies
887    /// and goes again is what `redis-benchmark -P 16` does and what half of the
888    /// clients in the world do. Every one of those rounds leaves the buffer
889    /// exactly caught up, and a buffer that never drops what it has already
890    /// dealt with grows to everything the connection has ever sent: 16 MiB
891    /// apiece on server3 for four connections sending 100000 sets each.
892    #[test]
893    fn a_pipelining_client_does_not_grow_the_read_buffer() {
894        let (mut r, conn, mut batch) = engine();
895        let mut round = Vec::new();
896        for i in 0..16 {
897            round.extend(wire(&[b"SET", format!("k{i}").as_bytes(), b"v"]));
898        }
899
900        r.engine_mut().feed(conn, &round);
901        pump(&mut r, &mut batch);
902        r.engine_mut().sink_mut().clear();
903        let after_one = r.engine().buffer_bytes();
904
905        // A thousand rounds is sixteen thousand commands and about a megabyte
906        // of wire bytes, which is a hundred times what the buffer starts with.
907        for _ in 0..1000 {
908            r.engine_mut().feed(conn, &round);
909            pump(&mut r, &mut batch);
910            r.engine_mut().sink_mut().clear();
911        }
912
913        assert_eq!(
914            r.engine().buffer_bytes(),
915            after_one,
916            "the buffers grew over a thousand rounds of the same sixteen commands"
917        );
918        assert!(
919            r.engine().server().memory_bytes() >= after_one,
920            "the buffers are counted in what the server reports"
921        );
922    }
923
924    /// Half a command in the buffer is the case compaction has to be careful
925    /// about, because the decoder holding it kept offsets into those bytes.
926    #[test]
927    fn a_command_split_across_reads_survives_compaction() {
928        let (mut r, conn, mut batch) = engine();
929        let cmd = wire(&[b"SET", b"key", b"value"]);
930        let (head, tail) = cmd.split_at(cmd.len() - 4);
931
932        // A complete command, so that there is something in front to drop, then
933        // most of a second one.
934        r.engine_mut().feed(conn, &wire(&[b"PING"]));
935        r.engine_mut().feed(conn, head);
936        pump(&mut r, &mut batch);
937        assert_eq!(r.engine().sink().sent(conn), b"+PONG\r\n");
938
939        // The rest of it arrives after the buffer has been compacted under it.
940        r.engine_mut().feed(conn, tail);
941        pump(&mut r, &mut batch);
942        assert_eq!(r.engine().sink().sent(conn), b"+PONG\r\n+OK\r\n");
943
944        r.engine_mut().feed(conn, &wire(&[b"GET", b"key"]));
945        pump(&mut r, &mut batch);
946        assert!(r.engine().sink().sent(conn).ends_with(b"$5\r\nvalue\r\n"));
947    }
948
949    /// The two walks are the reactor's, not this module's, so the test is that
950    /// the engine can be driven by them at all: same commands, same replies.
951    #[test]
952    fn the_batch_goes_through_the_reactors_two_walks() {
953        let (mut r, conn, mut batch) = engine();
954        for i in 0..100 {
955            r.engine_mut()
956                .feed(conn, &wire(&[b"INCR", format!("k{}", i % 7).as_bytes()]));
957        }
958        let ran = pump(&mut r, &mut batch);
959
960        assert_eq!(ran, 100);
961        assert_eq!(r.commands(), 100);
962        // Two batches, because a hundred commands do not fit in sixty four.
963        assert_eq!(r.turns(), 2);
964        // The hundredth command is the fifteenth `INCR` of `k1`.
965        assert!(r.engine().sink().sent(conn).ends_with(b":15\r\n"));
966    }
967
968    /// A sink that takes four bytes at a time, which is what a full socket
969    /// looks like from in here.
970    #[derive(Default)]
971    struct Trickle {
972        sent: Vec<u8>,
973        writes: usize,
974    }
975
976    impl Sink for Trickle {
977        fn write(&mut self, _conn: ConnId, bytes: &[u8]) -> usize {
978            self.writes += 1;
979            let n = bytes.len().min(4);
980            self.sent.extend_from_slice(&bytes[..n]);
981            n
982        }
983    }
984
985    /// A blocking command that does not block costs nothing: no waiter, no
986    /// allocation, the same three lines the non blocking one runs.
987    #[test]
988    fn a_blpop_on_a_list_with_something_in_it_never_waits() {
989        let (mut r, conn, mut batch) = engine();
990        r.engine_mut().feed(conn, &wire(&[b"RPUSH", b"q", b"a"]));
991        r.engine_mut().feed(conn, &wire(&[b"BLPOP", b"q", b"0"]));
992        pump(&mut r, &mut batch);
993
994        assert_eq!(
995            r.engine().sink().sent(conn),
996            b":1\r\n*2\r\n$1\r\nq\r\n$1\r\na\r\n"
997        );
998        assert_eq!(r.engine().server().waiters().len(), 0);
999    }
1000
1001    /// The whole point: a client with nothing to pop is answered later, by
1002    /// somebody else's command.
1003    #[test]
1004    fn a_parked_client_is_answered_by_another_connections_push() {
1005        let (mut r, a, mut batch) = engine();
1006        let b = r.engine_mut().accept();
1007
1008        r.engine_mut().feed(a, &wire(&[b"BLPOP", b"q", b"0"]));
1009        pump(&mut r, &mut batch);
1010        assert!(r.engine().sink().sent(a).is_empty(), "nothing to say yet");
1011        assert_eq!(r.engine().server().waiters().len(), 1);
1012
1013        r.engine_mut().feed(b, &wire(&[b"RPUSH", b"q", b"one"]));
1014        pump(&mut r, &mut batch);
1015
1016        assert_eq!(r.engine().sink().sent(a), b"*2\r\n$1\r\nq\r\n$3\r\none\r\n");
1017        // The push still reports the length it made, even though the element was
1018        // gone again before the reply was written.
1019        assert_eq!(r.engine().sink().sent(b), b":1\r\n");
1020        assert_eq!(r.engine().server().waiters().len(), 0);
1021    }
1022
1023    /// A push to a key nobody named, and a key of another type on a key
1024    /// somebody did: neither is a wake up, and the client stays parked.
1025    #[test]
1026    fn only_a_list_arriving_under_a_named_key_wakes_a_waiter() {
1027        let (mut r, a, mut batch) = engine();
1028        let b = r.engine_mut().accept();
1029        r.engine_mut().feed(a, &wire(&[b"BLPOP", b"q", b"0"]));
1030        pump(&mut r, &mut batch);
1031
1032        r.engine_mut()
1033            .feed(b, &wire(&[b"RPUSH", b"elsewhere", b"x"]));
1034        r.engine_mut().feed(b, &wire(&[b"SADD", b"q", b"x"]));
1035        pump(&mut r, &mut batch);
1036
1037        assert!(r.engine().sink().sent(a).is_empty());
1038        assert_eq!(r.engine().server().waiters().len(), 1, "still waiting");
1039        // And the set is intact, so the waiter did not take anything out of it
1040        // on its way past.
1041        assert_eq!(r.engine().sink().sent(b), b":1\r\n:1\r\n");
1042    }
1043
1044    /// Two workers on one queue, which is what `BLPOP` is for. They are served
1045    /// in the order they arrived and not in whatever order the list is walked.
1046    #[test]
1047    fn two_parked_clients_are_served_in_the_order_they_arrived() {
1048        let (mut r, a, mut batch) = engine();
1049        let b = r.engine_mut().accept();
1050        let c = r.engine_mut().accept();
1051
1052        r.engine_mut().feed(a, &wire(&[b"BLPOP", b"q", b"0"]));
1053        pump(&mut r, &mut batch);
1054        r.engine_mut().feed(b, &wire(&[b"BLPOP", b"q", b"0"]));
1055        pump(&mut r, &mut batch);
1056        assert_eq!(r.engine().server().waiters().len(), 2);
1057
1058        r.engine_mut()
1059            .feed(c, &wire(&[b"RPUSH", b"q", b"first", b"second"]));
1060        pump(&mut r, &mut batch);
1061
1062        assert_eq!(
1063            r.engine().sink().sent(a),
1064            b"*2\r\n$1\r\nq\r\n$5\r\nfirst\r\n"
1065        );
1066        assert_eq!(
1067            r.engine().sink().sent(b),
1068            b"*2\r\n$1\r\nq\r\n$6\r\nsecond\r\n"
1069        );
1070        assert_eq!(r.engine().server().waiters().len(), 0);
1071    }
1072
1073    /// A client waiting for an answer is not a client that has sent another
1074    /// question, so what it pipelined behind its `BLPOP` waits for the `BLPOP`.
1075    #[test]
1076    fn what_a_client_pipelined_behind_a_block_waits_for_the_block() {
1077        let (mut r, a, mut batch) = engine();
1078        let b = r.engine_mut().accept();
1079
1080        // Framed together, so the `PING` is already on its way to the reactor
1081        // when the `BLPOP` in front of it parks.
1082        let mut stream = wire(&[b"BLPOP", b"q", b"0"]);
1083        stream.extend(wire(&[b"PING"]));
1084        r.engine_mut().feed(a, &stream);
1085        pump(&mut r, &mut batch);
1086        assert!(
1087            r.engine().sink().sent(a).is_empty(),
1088            "the PING went out in front of the answer it was sent behind"
1089        );
1090
1091        // And one that arrives while it is parked is not even framed.
1092        r.engine_mut().feed(a, &wire(&[b"ECHO", b"after"]));
1093        pump(&mut r, &mut batch);
1094        assert!(r.engine().sink().sent(a).is_empty());
1095
1096        r.engine_mut().feed(b, &wire(&[b"RPUSH", b"q", b"x"]));
1097        pump(&mut r, &mut batch);
1098        assert_eq!(
1099            r.engine().sink().sent(a),
1100            b"*2\r\n$1\r\nq\r\n$1\r\nx\r\n+PONG\r\n$5\r\nafter\r\n"
1101        );
1102    }
1103
1104    /// Redis serves parked clients after every command rather than once per
1105    /// turn of the loop, and a pipeline is where the difference shows: the
1106    /// waiter has to be served between the two pushes, so it answers with the
1107    /// key the first push filled and not with the one it named first.
1108    #[test]
1109    fn a_waiter_is_served_between_two_pipelined_pushes() {
1110        let (mut r, a, mut batch) = engine();
1111        let b = r.engine_mut().accept();
1112        r.engine_mut()
1113            .feed(a, &wire(&[b"BLPOP", b"p1", b"p2", b"0"]));
1114        pump(&mut r, &mut batch);
1115
1116        let mut stream = wire(&[b"RPUSH", b"p2", b"second"]);
1117        stream.extend(wire(&[b"RPUSH", b"p1", b"first"]));
1118        r.engine_mut().feed(b, &stream);
1119        pump(&mut r, &mut batch);
1120
1121        assert_eq!(
1122            r.engine().sink().sent(a),
1123            b"*2\r\n$2\r\np2\r\n$6\r\nsecond\r\n"
1124        );
1125        // Which leaves the key it named first holding what was pushed to it.
1126        r.engine_mut()
1127            .feed(b, &wire(&[b"LRANGE", b"p1", b"0", b"-1"]));
1128        pump(&mut r, &mut batch);
1129        assert!(
1130            r.engine()
1131                .sink()
1132                .sent(b)
1133                .ends_with(b"*1\r\n$5\r\nfirst\r\n")
1134        );
1135    }
1136
1137    /// A `BLMOVE` that serves itself is a push, so it wakes the client waiting
1138    /// on the key it pushed to, in the same moment and without a turn of the
1139    /// loop in between.
1140    #[test]
1141    fn a_waiter_woken_by_another_waiter() {
1142        let (mut r, a, mut batch) = engine();
1143        let b = r.engine_mut().accept();
1144        let c = r.engine_mut().accept();
1145
1146        r.engine_mut()
1147            .feed(a, &wire(&[b"BLMOVE", b"x", b"y", b"LEFT", b"RIGHT", b"0"]));
1148        pump(&mut r, &mut batch);
1149        r.engine_mut().feed(b, &wire(&[b"BLPOP", b"y", b"0"]));
1150        pump(&mut r, &mut batch);
1151        assert_eq!(r.engine().server().waiters().len(), 2);
1152
1153        r.engine_mut().feed(c, &wire(&[b"RPUSH", b"x", b"chain"]));
1154        pump(&mut r, &mut batch);
1155
1156        assert_eq!(r.engine().sink().sent(a), b"$5\r\nchain\r\n");
1157        assert_eq!(
1158            r.engine().sink().sent(b),
1159            b"*2\r\n$1\r\ny\r\n$5\r\nchain\r\n"
1160        );
1161        assert_eq!(r.engine().server().waiters().len(), 0);
1162    }
1163
1164    /// A waiter on one database is not woken by a push on another, even though
1165    /// the key has the same name.
1166    #[test]
1167    fn a_waiter_is_only_woken_on_the_database_it_blocked_on() {
1168        let (mut r, a, mut batch) = engine();
1169        let b = r.engine_mut().accept();
1170        r.engine_mut().feed(a, &wire(&[b"SELECT", b"3"]));
1171        r.engine_mut().feed(a, &wire(&[b"BLPOP", b"q", b"0"]));
1172        pump(&mut r, &mut batch);
1173        assert_eq!(r.engine().sink().sent(a), b"+OK\r\n");
1174
1175        r.engine_mut().feed(b, &wire(&[b"RPUSH", b"q", b"wrongdb"]));
1176        pump(&mut r, &mut batch);
1177        assert_eq!(r.engine().sink().sent(a), b"+OK\r\n", "still waiting");
1178
1179        r.engine_mut().feed(b, &wire(&[b"SELECT", b"3"]));
1180        r.engine_mut().feed(b, &wire(&[b"RPUSH", b"q", b"rightdb"]));
1181        pump(&mut r, &mut batch);
1182        assert!(r.engine().sink().sent(a).ends_with(b"$7\r\nrightdb\r\n"));
1183    }
1184
1185    /// The deadline sweep, which runs on a turn that has nothing else to do.
1186    #[test]
1187    fn a_client_that_waited_long_enough_gets_a_null_array() {
1188        let (mut r, conn, mut batch) = timed();
1189        r.engine_mut().feed(conn, &wire(&[b"BLPOP", b"q", b"30"]));
1190        pump(&mut r, &mut batch);
1191        assert!(r.engine().sink().sent(conn).is_empty());
1192
1193        r.engine_mut().server_mut().set_clock_ms(START_MS + 29_999);
1194        pump(&mut r, &mut batch);
1195        assert!(
1196            r.engine().sink().sent(conn).is_empty(),
1197            "a millisecond short"
1198        );
1199
1200        r.engine_mut().server_mut().set_clock_ms(START_MS + 30_000);
1201        pump(&mut r, &mut batch);
1202        // A null array and not a null string, which a RESP2 client can see.
1203        assert_eq!(r.engine().sink().sent(conn), b"*-1\r\n");
1204        assert_eq!(r.engine().server().waiters().len(), 0);
1205    }
1206
1207    /// The four that answer with something other than a two element array all
1208    /// answer a timeout the same way, which is not what the reply shape would
1209    /// suggest and is what Redis does.
1210    #[test]
1211    fn every_blocking_command_times_out_with_the_same_null_array() {
1212        for cmd in [
1213            &[b"BLPOP".as_slice(), b"q", b"0.001"][..],
1214            &[b"BRPOP", b"q", b"0.001"],
1215            &[b"BLMOVE", b"q", b"d", b"LEFT", b"RIGHT", b"0.001"],
1216            &[b"BRPOPLPUSH", b"q", b"d", b"0.001"],
1217            &[b"BLMPOP", b"0.001", b"1", b"q", b"LEFT"],
1218        ] {
1219            let (mut r, conn, mut batch) = timed();
1220            r.engine_mut().feed(conn, &wire(cmd));
1221            pump(&mut r, &mut batch);
1222            r.engine_mut().server_mut().set_clock_ms(START_MS + 1);
1223            pump(&mut r, &mut batch);
1224            assert_eq!(r.engine().sink().sent(conn), b"*-1\r\n", "for {cmd:?}");
1225        }
1226    }
1227
1228    /// A client that gave up does not go on holding a claim on the queue: the
1229    /// element that arrives after it stays where it was put.
1230    #[test]
1231    fn a_waiter_that_timed_out_does_not_eat_a_later_push() {
1232        let (mut r, a, mut batch) = timed();
1233        let b = r.engine_mut().accept();
1234        r.engine_mut().feed(a, &wire(&[b"BLPOP", b"q", b"1"]));
1235        pump(&mut r, &mut batch);
1236        r.engine_mut().server_mut().set_clock_ms(START_MS + 1000);
1237        pump(&mut r, &mut batch);
1238        assert_eq!(r.engine().sink().sent(a), b"*-1\r\n");
1239
1240        r.engine_mut().feed(b, &wire(&[b"RPUSH", b"q", b"late"]));
1241        r.engine_mut()
1242            .feed(b, &wire(&[b"LRANGE", b"q", b"0", b"-1"]));
1243        pump(&mut r, &mut batch);
1244        assert_eq!(r.engine().sink().sent(a), b"*-1\r\n", "nothing more");
1245        assert!(r.engine().sink().sent(b).ends_with(b"*1\r\n$4\r\nlate\r\n"));
1246    }
1247
1248    /// A `BLPOP key 0` has no deadline, so nothing but the connection closing
1249    /// will ever take it off the list. That makes the close path the one that
1250    /// has to be right, or a waiter outlives its client and the slot it names
1251    /// gets handed to somebody else.
1252    #[test]
1253    fn a_client_that_goes_away_while_it_waits_takes_its_waiter_with_it() {
1254        let (mut r, a, mut batch) = engine();
1255        let b = r.engine_mut().accept();
1256        r.engine_mut().feed(a, &wire(&[b"BLPOP", b"q", b"0"]));
1257        pump(&mut r, &mut batch);
1258        assert_eq!(r.engine().server().waiters().len(), 1);
1259
1260        r.engine_mut().hangup(a);
1261        pump(&mut r, &mut batch);
1262        assert_eq!(r.engine().server().waiters().len(), 0);
1263        assert_eq!(r.engine().clients(), 1);
1264
1265        // The slot is handed straight back out, which is what the waiter would
1266        // have been pointing at.
1267        let again = r.engine_mut().accept();
1268        assert_eq!(again, a);
1269        r.engine_mut().feed(b, &wire(&[b"RPUSH", b"q", b"x"]));
1270        r.engine_mut()
1271            .feed(again, &wire(&[b"LRANGE", b"q", b"0", b"-1"]));
1272        pump(&mut r, &mut batch);
1273        assert_eq!(r.engine().sink().sent(again), b"*1\r\n$1\r\nx\r\n");
1274    }
1275
1276    /// The same, with commands the client had already sent sitting behind the
1277    /// block. Those are what `pending` counts, so a close that forgets them is a
1278    /// connection slot that never comes back.
1279    #[test]
1280    fn a_hangup_while_parked_gives_back_the_slot_and_the_decoders() {
1281        let (mut r, a, mut batch) = engine();
1282        let mut stream = wire(&[b"BLPOP", b"q", b"0"]);
1283        stream.extend(wire(&[b"PING"]));
1284        stream.extend(wire(&[b"PING"]));
1285        r.engine_mut().feed(a, &stream);
1286        pump(&mut r, &mut batch);
1287
1288        let decoders = r.engine().decoders();
1289        r.engine_mut().hangup(a);
1290        pump(&mut r, &mut batch);
1291
1292        assert_eq!(r.engine().clients(), 0);
1293        assert!(r.engine().sink().was_closed(a));
1294        assert_eq!(r.engine().decoders(), decoders, "the pool came back whole");
1295        let again = r.engine_mut().accept();
1296        assert_eq!(again, a);
1297        r.engine_mut().feed(again, &wire(&[b"PING"]));
1298        pump(&mut r, &mut batch);
1299        assert_eq!(r.engine().sink().sent(again), b"+PONG\r\n");
1300    }
1301
1302    #[test]
1303    fn a_reply_the_socket_would_not_take_is_offered_again() {
1304        let mut r = Reactor::inline(Wire::new(Trickle::default()));
1305        let conn = r.engine_mut().accept();
1306        let mut batch = Vec::new();
1307
1308        r.engine_mut().feed(conn, &wire(&[b"PING"]));
1309        pump(&mut r, &mut batch);
1310        // Two flushes in a pump, so four bytes and then three.
1311        assert_eq!(r.engine().sink().sent, b"+PONG\r\n");
1312        assert_eq!(r.engine().sink().writes, 2);
1313    }
1314}