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 the threads run along: a
19//! front belongs to the thread that accepted its connections and is reached by
20//! nothing else, and the server is the handle every thread holds a copy of.
21//! Everything that needs both is a method on `Wire` and there are three of them,
22//! which are running a command, answering a client that blocked and forgetting a
23//! client 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 std::sync::Arc;
75
76use yo_reactor::{BATCH_MAX, Engine, Reactor};
77
78use crate::dispatch::table;
79use crate::dispatch::{self, Flow, Parked, Server};
80use crate::front::{Front, Wrote};
81use crate::proto::Limits;
82use yo_kv::Keyspace;
83
84pub use crate::front::Cmd;
85
86/// Which connection. An index, reused after a connection closes.
87pub type ConnId = u32;
88
89/// Where replies go.
90///
91/// One call per connection per batch, with however many replies are waiting.
92/// The network reactor implements this over io_uring, a test implements it over
93/// a `Vec`, and neither this module nor `dispatch` has to know which.
94pub trait Sink {
95    /// Take up to all of `bytes` for `conn`, and say how many were taken.
96    ///
97    /// Fewer than were offered means the socket is full: what is left stays in
98    /// the connection's reply buffer and is offered again on the next flush.
99    fn write(&mut self, conn: ConnId, bytes: &[u8]) -> usize;
100
101    /// The connection is finished with and its id is about to be reused.
102    fn closed(&mut self, conn: ConnId) {
103        let _ = conn;
104    }
105}
106
107/// A sink that keeps everything, for tests and for a driver with no socket.
108#[derive(Debug, Default)]
109pub struct Recorder {
110    sent: Vec<Vec<u8>>,
111    closed: Vec<ConnId>,
112}
113
114impl Recorder {
115    /// An empty one.
116    #[must_use]
117    pub fn new() -> Recorder {
118        Recorder::default()
119    }
120
121    /// Everything written to a connection so far.
122    #[must_use]
123    pub fn sent(&self, conn: ConnId) -> &[u8] {
124        self.sent.get(conn as usize).map_or(&[], Vec::as_slice)
125    }
126
127    /// Whether a connection was closed.
128    #[must_use]
129    pub fn was_closed(&self, conn: ConnId) -> bool {
130        self.closed.contains(&conn)
131    }
132
133    /// Forget what was written, keeping the room it was written into.
134    pub fn clear(&mut self) {
135        for c in &mut self.sent {
136            c.clear();
137        }
138        self.closed.clear();
139    }
140}
141
142impl Sink for Recorder {
143    fn write(&mut self, conn: ConnId, bytes: &[u8]) -> usize {
144        // A test sink, so the growth here is not on anybody's data path.
145        yo_alloc::allow(|| {
146            if self.sent.len() <= conn as usize {
147                self.sent.resize_with(conn as usize + 1, Vec::new);
148            }
149            self.sent[conn as usize].extend_from_slice(bytes);
150        });
151        bytes.len()
152    }
153
154    fn closed(&mut self, conn: ConnId) {
155        yo_alloc::allow(|| self.closed.push(conn));
156    }
157}
158
159/// The engine: connections on one side, the command layer on the other.
160///
161/// One per thread, and it is two halves rather than one thing. The front is the
162/// connections and everything they own, which never leaves the thread that
163/// accepted them. [`Server`] is the databases, and every thread has a handle on
164/// the same one. This type is where the two meet, and every method on it that is
165/// not a one line delegation is a method that genuinely needs both: running a
166/// command, answering a client that blocked, and forgetting a client that has
167/// gone.
168pub struct Wire<S> {
169    front: Front<S>,
170    server: Arc<Server>,
171    /// This thread's parked clients, copied out of the shared list.
172    ///
173    /// Here rather than in `serve_waiters` so that a server with blocked
174    /// clients on it does not allocate once a batch. It is empty between
175    /// batches and it is only ever this thread's, like everything else on this
176    /// side of the engine.
177    parked: Vec<Parked>,
178    /// Messages published for this thread's connections, copied out of the
179    /// mailbox.
180    ///
181    /// Here for the reason `parked` is here: a server with subscribers on it
182    /// should not allocate a vector once a batch to drain into. It is empty
183    /// between batches.
184    post: Vec<dispatch::Envelope>,
185}
186
187impl<S: Sink> Wire<S> {
188    /// An engine with an empty server.
189    #[must_use]
190    pub fn new(sink: S) -> Wire<S> {
191        Wire::with_server(Server::new(), sink)
192    }
193
194    /// An engine over a server the caller built, which is how a test gives it a
195    /// clock it can move by hand.
196    #[must_use]
197    pub fn with_server(server: Server, sink: S) -> Wire<S> {
198        Wire::over(Arc::new(server), sink)
199    }
200
201    /// An engine over a server that already exists, which is how the second
202    /// thread and every thread after it gets one.
203    ///
204    /// Each thread builds its own front and they never see each other's. What
205    /// they share is behind the handle, and the reason the handle is counted
206    /// rather than borrowed is that the threads outlive whichever call started
207    /// them by design: a scope that borrows would tie the server's lifetime to
208    /// a frame that is meant to return.
209    #[must_use]
210    pub fn over(server: Arc<Server>, sink: S) -> Wire<S> {
211        Wire {
212            front: Front::new(sink),
213            parked: Vec::new(),
214            post: Vec::new(),
215            server,
216        }
217    }
218
219    /// The databases and the numbers `INFO` reports.
220    #[must_use]
221    pub fn server(&self) -> &Server {
222        &self.server
223    }
224
225    /// Another handle on the same server, for building the next thread's
226    /// engine.
227    #[must_use]
228    pub fn shared(&self) -> Arc<Server> {
229        Arc::clone(&self.server)
230    }
231
232    /// The server, for the few settings that have to be made before it is
233    /// serving.
234    ///
235    /// That is the directory and the thread count, both of which are read
236    /// everywhere and written once at startup, so they are settings and not
237    /// state. This works while this engine holds the only handle, which is the
238    /// case from the moment the server is built until the threads are started,
239    /// and it is the caller's job to do its setting up in that window.
240    ///
241    /// # Panics
242    ///
243    /// If a second handle already exists, because there is no honest answer to
244    /// give: changing the directory under a thread that is already serving out
245    /// of it is the bug this would otherwise hide.
246    pub fn server_mut(&mut self) -> &mut Server {
247        Arc::get_mut(&mut self.server)
248            .expect("the server is set up before the threads that share it are started")
249    }
250
251    /// Where the replies went.
252    #[must_use]
253    pub const fn sink(&self) -> &S {
254        self.front.sink()
255    }
256
257    /// The same, mutably.
258    pub const fn sink_mut(&mut self) -> &mut S {
259        self.front.sink_mut()
260    }
261
262    /// Change the protocol limits, which is `proto-max-bulk-len` and friends.
263    pub fn set_limits(&mut self, limits: Limits) {
264        self.front.set_limits(limits);
265    }
266
267    /// Open a connection and give back its id.
268    pub fn accept(&mut self) -> ConnId {
269        self.server.counted().opened();
270        let at = self.front.open(self.server.next_client());
271        self.note_buffers();
272        at
273    }
274
275    /// Tell the server what the connection buffers are holding now.
276    ///
277    /// The front cannot reach the server, so it keeps the change and this is
278    /// where it is handed over: at the end of whichever call moved a buffer.
279    fn note_buffers(&mut self) {
280        let delta = self.front.buffer_delta();
281        if delta != 0 {
282            self.server.note_conn_bytes(delta);
283        }
284    }
285
286    /// The peer went away.
287    ///
288    /// Whatever is buffered for it is dropped rather than written, and the slot
289    /// comes back as soon as the commands already framed out of its buffer have
290    /// run, because those commands' arguments still point into it.
291    pub fn hangup(&mut self, conn: ConnId) {
292        if !self.front.live(conn) {
293            return;
294        }
295        self.front.mark_gone(conn);
296        // A parked client holds its own commands, and those commands are what
297        // `pending` counts, so leaving it parked here would leave the slot owed
298        // to a connection that is never going to be answered. They go back to
299        // the queue and run as the no-ops a gone connection's commands are.
300        if self.front.blocked(conn) {
301            self.front.unpark(conn);
302        }
303        if self.front.pending(conn) == 0 {
304            self.release(conn);
305        }
306        self.note_buffers();
307    }
308
309    /// Answer everybody this thread can answer, and let go of everybody whose
310    /// deadline has passed.
311    ///
312    /// The walk is over the waiter list rather than over the connections, so it
313    /// costs what blocking costs and not what the server costs. Every caller
314    /// checks that somebody is parked before calling, which is the load and the
315    /// branch a server with nobody blocked pays.
316    ///
317    /// Only this thread's waiters, because a reply goes into a buffer this
318    /// thread owns and another thread's waiter is another thread's to answer.
319    /// The list is copied out under the lock and then let go of, so the work of
320    /// answering does not hold up a thread trying to park a client.
321    fn serve_waiters(&mut self) {
322        let now = self.server.now_ms();
323        let mine = self.server.my_slot();
324        self.server.waiters().mine(mine, &mut self.parked);
325        for at in 0..self.parked.len() {
326            let p = self.parked[at];
327            // The slot is reused and the client id is not. `release` forgets
328            // waiters, so this should never fire; it is here because being
329            // wrong about it writes a reply into somebody else's socket rather
330            // than dropping one.
331            if !self.front.answers(p.conn, p.client) {
332                self.server.forget_waiters(p.client);
333                continue;
334            }
335            // The front cannot reach the databases and the server cannot reach
336            // the connections, so the two halves are taken apart here and the
337            // one buffer this waiter needs is handed over.
338            let served = {
339                let Wire { server, front, .. } = self;
340                server.serve_waiter(p.client, now, front.out(p.conn))
341            };
342            if served {
343                self.server.forget_waiters(p.client);
344                self.front.unpark(p.conn);
345                self.front.soil(p.conn);
346            }
347        }
348        self.parked.clear();
349    }
350
351    /// Write out everything published for this thread's connections.
352    ///
353    /// The mailbox is emptied under its lock and then let go of, so a thread
354    /// rendering a thousand messages is not holding up the publishers filling
355    /// its box. The client id on each envelope is checked against the slot
356    /// because a slot is reused and an id is not, which is the same guard the
357    /// waiter list uses and for the same reason: being wrong here writes into
358    /// somebody else's socket rather than dropping a message.
359    fn deliver(&mut self) {
360        // Taken and put back so the loop can reach the front, the way the dirty
361        // list is. The capacity comes back with it.
362        let mut post = core::mem::take(&mut self.post);
363        self.server.take_mail(&mut post);
364        for env in post.drain(..) {
365            let conn = env.conn();
366            if !self.front.answers(conn, env.client()) {
367                continue;
368            }
369            env.write(self.front.out(conn));
370            self.front.soil(conn);
371        }
372        self.post = post;
373    }
374
375    /// How many connections are open.
376    #[must_use]
377    pub fn clients(&self) -> usize {
378        self.front.clients()
379    }
380
381    /// Commands framed and waiting for the reactor.
382    #[must_use]
383    pub fn ready(&self) -> usize {
384        self.front.ready()
385    }
386
387    /// Connections with a reply that has not gone out yet.
388    ///
389    /// Non zero means a socket was full and what is left is being held for a
390    /// later flush, which a driver waiting on readability needs to know: there
391    /// is work here that no incoming byte will ever wake it up for.
392    #[must_use]
393    pub fn owed(&self) -> usize {
394        self.front.owed()
395    }
396
397    /// Clients of this thread's that are blocked on a key.
398    ///
399    /// The other thing a driver waiting on readability needs to know, and for
400    /// the same reason `owed` is: there is work here that no incoming byte will
401    /// wake it for. A blocked client is answered by a write another thread made
402    /// or by its own deadline passing, and neither of those is a byte arriving
403    /// on this thread's poller, so a driver that reads this keeps its wait short
404    /// while anybody is waiting on it.
405    #[must_use]
406    pub fn waiting(&self) -> usize {
407        self.server.parked_here()
408    }
409
410    /// Mail waiting for this thread, plus subscribers of its own that mail
411    /// could arrive for.
412    ///
413    /// The third thing a driver waiting on readability needs to know, and for
414    /// the reason the other two are: a published message is a write another
415    /// thread made and no byte arriving here will wake this thread for it. So a
416    /// thread that has a subscriber keeps its wait short, and one that has none
417    /// is not affected.
418    #[must_use]
419    pub fn posted(&self) -> usize {
420        self.server.posted()
421    }
422
423    /// Whether a client has asked the server to stop.
424    ///
425    /// The driver reads this once a turn, next to the flag a signal sets, and
426    /// leaves its loop when either is set. Asked after the batch rather than
427    /// during it, so the `SHUTDOWN` and everything that shared its batch is
428    /// finished and written out before anything closes.
429    #[must_use]
430    pub fn stopping(&self) -> bool {
431        self.server.stopping()
432    }
433
434    /// Decoders in the pool, which is the high water mark of one batch.
435    #[must_use]
436    pub fn decoders(&self) -> usize {
437        self.front.decoders()
438    }
439
440    /// What every connection's read and reply buffers are holding.
441    #[must_use]
442    pub fn buffer_bytes(&self) -> usize {
443        self.front.buffer_bytes()
444    }
445
446    /// Take bytes off a connection and frame whatever commands they complete.
447    ///
448    /// Anything left over stays in the connection's buffer, half a command
449    /// included, so the caller hands over whatever the socket gave it without
450    /// looking at it.
451    pub fn feed(&mut self, conn: ConnId, bytes: &[u8]) {
452        self.front.feed(conn, bytes);
453        self.note_buffers();
454    }
455
456    /// Hand the slot and its buffers back, and let the server go of the client.
457    fn release(&mut self, conn: ConnId) {
458        // Before the slot goes back, because the watches this connection took
459        // are rows on the server and the session that names them is about to be
460        // reused by whoever gets the slot next.
461        if let Some(session) = self.front.session_mut(conn) {
462            dispatch::forget_session(&self.server, session);
463        }
464        let Some(client) = self.front.close(conn) else {
465            return;
466        };
467        self.forget(client);
468    }
469
470    /// The server side of a connection ending.
471    ///
472    /// It happens in the same call the slot was freed in, and before anything
473    /// else can run, because the slot is handed out again by the next accept
474    /// and a waiter still holding this client id would then be a waiter
475    /// pointing at somebody else's connection.
476    fn forget(&mut self, client: u64) {
477        self.server.forget_waiters(client);
478        self.server.counted().closed();
479    }
480
481    /// Move up to `max` framed commands into `into`.
482    ///
483    /// The reactor wants a batch it owns, and the front keeps the buffers, so
484    /// what crosses between them is this: numbers, no borrows.
485    pub fn take_ready(&mut self, into: &mut Vec<Cmd>, max: usize) -> usize {
486        self.front.take_ready(into, max)
487    }
488
489    /// Take a clock reading for the whole batch.
490    ///
491    /// `04` section 5: once per turn, never per command, so every command in a
492    /// batch compares against the same millisecond and two keys written
493    /// together expire together.
494    pub fn tick(&mut self) {
495        self.server.refresh_clock();
496    }
497
498    /// Do one batch's worth of housekeeping.
499    ///
500    /// Today that is one segment of arena compaction at most, which is what
501    /// stops a server that rewrites the same keys from holding every version of
502    /// them. It is separate from [`Wire::tick`] because the clock has to move
503    /// before a batch runs and this does not: it can wait until the replies are
504    /// out, and the driver decides when that is.
505    ///
506    /// Per batch and not per turn of the loop. A turn can carry one command or
507    /// a thousand, so a per turn call means the rate at which garbage is
508    /// collected has nothing to do with the rate at which it is made, and on a
509    /// saturated server the second one wins. That was measured: with this on
510    /// the loop's turn the server settled at seven segments for six segments'
511    /// worth of keys, which is where an unloaded process running the same
512    /// writes settled at six.
513    pub fn maintain(&mut self) -> Option<usize> {
514        // Before the compaction and not after it, because the reading the next
515        // batch judges its limit against should be the one taken after the last
516        // batch's writes rather than the one taken after this call's collecting.
517        // Both are true, and the first is the one that is a batch old at worst.
518        // Nothing at all on a server with no `maxmemory`, which is the default.
519        self.server.refresh_memory();
520        // Two fields and a return on a server that has never taken a backup,
521        // which is nearly all of them. It is here rather than on a timer for the
522        // same reason the compaction is: one loop turns everything.
523        self.server.backup_expire();
524        self.server.compact_step()
525    }
526}
527
528impl<S: Sink> Engine for Wire<S> {
529    type Work = Cmd;
530
531    fn key_hash(&self, cmd: &Cmd) -> Option<u64> {
532        // Before the argument list is built, because most of the commands that
533        // get this far and answer `None` answer it on the spec alone, and
534        // building an `Args` to then throw it away is the sort of thing that
535        // does not show up in a profile and does show up in a total.
536        let spec = table::at(cmd.spec)?;
537        if spec.first_key <= 0 {
538            return None;
539        }
540        let args = self.front.args(cmd);
541        // The first key only. A command with more than one, which is `MSET` and
542        // `MGET`, warms the first and takes the miss on the rest; warming all of
543        // them means a hash list per command and that is the batch's own job
544        // once multi key commands are worth measuring.
545        let key = args.opt(spec.first_key as usize)?;
546        Some(Keyspace::hash_of(key))
547    }
548
549    fn prefetch(&self, cmd: &Cmd, hash: u64) {
550        let db = self.front.db(cmd.conn());
551        // The hash picks the stripe as well as the record, so this warms the
552        // line the command is going to read and not a line on some other
553        // stripe. It is the same hash the command itself will route on, which
554        // is why the stripe is worked out from a hash rather than from a key.
555        self.server.striped_ref(db).prefetch_hashed(hash);
556    }
557
558    fn run(&mut self, cmd: Cmd, _hash: Option<u64>) -> yo_reactor::Flow {
559        let conn = cmd.conn();
560        // Framed with the batch that blocked, so it is a command the client sent
561        // before it knew it would be waiting. It keeps its decoder and it keeps
562        // its place in `pending`, which is what stops the buffer it points into
563        // being compacted while it waits.
564        if self.front.blocked(conn) {
565            self.front.park(conn, cmd);
566            return yo_reactor::Flow::Next;
567        }
568
569        // The one place both halves are held at once. The front hands over the
570        // arguments, the session and the reply buffer, the server hands over
571        // the databases, and the command layer sees the two as one call.
572        let flow = if self.front.start(&cmd) {
573            let Wire { front, server, .. } = self;
574            let (args, session, out) = front.parts(&cmd);
575            let spec = table::at(cmd.spec);
576            dispatch::resolved(server, session, spec, args, out)
577        } else {
578            // Nobody to answer, or nobody who should be. The decoder still has
579            // to come back and the slot still has to be released, which is why
580            // this is not an early return.
581            Flow::Continue
582        };
583
584        self.front.done(&cmd);
585        if self.front.gone(conn) {
586            if self.front.pending(conn) == 0 {
587                self.release(conn);
588            }
589        } else {
590            match flow {
591                Flow::Close => {
592                    self.front.quit(conn);
593                    self.front.soil(conn);
594                }
595                // Nothing was written, so there is nothing to flush and no
596                // reason to put this connection on the dirty list. The waiter
597                // carries the slot from here on, and it needs to know which one:
598                // the command layer only ever saw the client id.
599                Flow::Block => {
600                    self.front.block(conn);
601                    let client = self.front.client(conn);
602                    self.server.bind_waiter(client, conn);
603                }
604                Flow::Continue => self.front.soil(conn),
605            }
606        }
607
608        // After each command and not once per batch. A client blocked on two
609        // keys and woken by `RPUSH b` then `RPUSH a` in one pipeline has to
610        // answer with `b`, because that is the push that was in front of it, and
611        // it can only do that if it was served in between the two.
612        if self.server.parked_here() != 0 {
613            self.serve_waiters();
614        }
615        yo_reactor::Flow::Next
616    }
617
618    fn flush(&mut self) {
619        // The deadline sweep, and it is here because this is the one thing the
620        // driver calls on a turn that ran nothing at all. A client whose timeout
621        // passes while the server is idle is answered within the loop's idle
622        // wait, which the loop shortens to a millisecond on a thread that has
623        // somebody waiting. That is finer than the 10hz Redis checks its own
624        // blocked clients at.
625        //
626        // This thread's count and not the server's, because the sweep can only
627        // answer this thread's waiters, so on any other thread it is a lock
628        // taken to find nothing.
629        if self.server.parked_here() != 0 {
630            self.server.refresh_clock();
631            self.serve_waiters();
632        }
633
634        // Then the published messages, before the write out below and after
635        // everything this batch answered, which is the order a client that
636        // publishes to itself sees on a real server: the count first and the
637        // message second, checked on the wire against 8.10.1.
638        if self.server.mail_here() != 0 {
639            self.deliver();
640        }
641
642        // Taken and put back so the loop below can reach the rest of the
643        // engine. The capacity comes back with it, so this is not an
644        // allocation.
645        let mut dirty = self.front.take_dirty();
646        let mut at = 0;
647        while at < dirty.len() {
648            let conn = dirty[at];
649            match self.front.write_out(conn) {
650                // The socket was full. The connection stays on the list with
651                // what is left of its reply, and the next flush offers it
652                // again, which is the whole of the backpressure story here.
653                Wrote::Owed => at += 1,
654                Wrote::Done => {
655                    dirty.swap_remove(at);
656                }
657                Wrote::Ended(client) => {
658                    self.forget(client);
659                    dirty.swap_remove(at);
660                }
661            }
662        }
663        self.front.give_dirty(dirty);
664        self.note_buffers();
665    }
666
667    fn maintain(&mut self, budget: &mut yo_reactor::Budget) {
668        // The clock is the first thing the maintenance slice does, because
669        // everything else in it compares against a time.
670        if !budget.spend(1) {
671            return;
672        }
673        self.tick();
674        // Then the dead keys, which is what stops a cache that writes with a
675        // deadline and never reads back from holding every key it has ever
676        // written. One unit a key looked at, so the slice bounds the sweep the
677        // same way it bounds everything else in here, and a server where nothing
678        // has a deadline spends nothing at all.
679        let looks = budget.left() as usize;
680        let spent = self.server.expire_slice(looks);
681        budget.spend(u32::try_from(spent).unwrap_or(u32::MAX));
682    }
683}
684
685/// Run everything that is framed, in batches, and write the replies.
686///
687/// The inline driver: it is what a caller who is already on the shard thread
688/// uses in place of the loop, and it goes through the same two walks the loop
689/// goes through (`15` section 7). `batch` is the caller's, so a driver in a hot
690/// loop hands the same `Vec` back every time and never allocates.
691pub fn pump<S: Sink>(reactor: &mut Reactor<Wire<S>>, batch: &mut Vec<Cmd>) -> usize {
692    let mut ran = 0;
693    reactor.engine_mut().tick();
694    loop {
695        batch.clear();
696        if reactor.engine_mut().take_ready(batch, BATCH_MAX) == 0 {
697            break;
698        }
699        // The command path, and therefore the thing Y7 is about. The guard is
700        // what arms `yo-alloc`, and it covers dispatch and nothing else: framing
701        // before it and writing the replies after it are both allowed to reach
702        // for the heap, and only running the commands is not.
703        //
704        // It goes here rather than around the whole loop because `take_ready`
705        // and `flush` are on the other side of that line, and because a batch is
706        // the unit a caller can reason about. Under the default mode this is one
707        // relaxed load.
708        let armed = yo_alloc::guard();
709        ran += reactor.execute_all(batch.drain(..));
710        drop(armed);
711        reactor.engine_mut().flush();
712        // After the replies are out, so the batch that made the garbage is not
713        // the batch that waits for it to be collected.
714        reactor.engine_mut().maintain();
715    }
716    // Once more, for a connection with something to say and nothing to run: a
717    // protocol error, or a socket that was full the last time round.
718    reactor.engine_mut().flush();
719    // And once for a turn that ran nothing at all, which is where a server that
720    // has gone quiet catches up on what the last busy turn left behind.
721    reactor.engine_mut().maintain();
722    ran
723}
724
725#[cfg(test)]
726mod tests {
727    use super::*;
728
729    /// The wire bytes for a command, built the way a client would.
730    fn wire(args: &[&[u8]]) -> Vec<u8> {
731        let mut b = format!("*{}\r\n", args.len()).into_bytes();
732        for a in args {
733            b.extend_from_slice(format!("${}\r\n", a.len()).as_bytes());
734            b.extend_from_slice(a);
735            b.extend_from_slice(b"\r\n");
736        }
737        b
738    }
739
740    fn engine() -> (Reactor<Wire<Recorder>>, ConnId, Vec<Cmd>) {
741        let mut r = Reactor::inline(Wire::new(Recorder::new()));
742        let conn = r.engine_mut().accept();
743        (r, conn, Vec::new())
744    }
745
746    /// Where the fixed clock a blocking test moves by hand starts.
747    const START_MS: u64 = 1_000_000;
748
749    /// The same, on a clock the test moves rather than the system's.
750    ///
751    /// A test about a timeout cannot wait for one: waiting a hundred
752    /// milliseconds is a test that fails on a loaded machine and waiting a
753    /// hundred seconds is not a test.
754    fn timed() -> (Reactor<Wire<Recorder>>, ConnId, Vec<Cmd>) {
755        let server = crate::dispatch::Server::with_clock(yo_kv::Clock::fixed(START_MS));
756        let mut r = Reactor::inline(Wire::with_server(server, Recorder::new()));
757        let conn = r.engine_mut().accept();
758        (r, conn, Vec::new())
759    }
760
761    #[test]
762    fn a_pipelined_batch_comes_back_in_order_and_in_one_write() {
763        let (mut r, conn, mut batch) = engine();
764        let mut stream = wire(&[b"SET", b"k", b"v"]);
765        stream.extend(wire(&[b"GET", b"k"]));
766        stream.extend(wire(&[b"INCR", b"n"]));
767
768        r.engine_mut().feed(conn, &stream);
769        assert_eq!(r.engine().ready(), 3);
770        assert_eq!(pump(&mut r, &mut batch), 3);
771
772        assert_eq!(r.engine().sink().sent(conn), b"+OK\r\n$1\r\nv\r\n:1\r\n");
773        assert_eq!(r.engine().ready(), 0);
774    }
775
776    /// The framing has to survive a command arriving in pieces, because that is
777    /// what a socket does.
778    #[test]
779    fn a_command_split_across_reads_resumes_rather_than_restarts() {
780        let (mut r, conn, mut batch) = engine();
781        let bytes = wire(&[b"SET", b"key", b"value"]);
782
783        for at in 1..bytes.len() {
784            r.engine_mut().feed(conn, &bytes[at - 1..at]);
785            assert_eq!(r.engine().ready(), 0, "not a command yet at {at}");
786        }
787        r.engine_mut().feed(conn, &bytes[bytes.len() - 1..]);
788        assert_eq!(r.engine().ready(), 1);
789        assert_eq!(pump(&mut r, &mut batch), 1);
790        assert_eq!(r.engine().sink().sent(conn), b"+OK\r\n");
791
792        // And the value that arrived in single bytes is the value that was
793        // stored, which is the part a naive resume gets wrong.
794        r.engine_mut().feed(conn, &wire(&[b"GET", b"key"]));
795        pump(&mut r, &mut batch);
796        assert_eq!(r.engine().sink().sent(conn), b"+OK\r\n$5\r\nvalue\r\n");
797    }
798
799    #[test]
800    fn two_connections_are_two_sessions_over_one_server() {
801        let (mut r, a, mut batch) = engine();
802        let b = r.engine_mut().accept();
803
804        r.engine_mut().feed(a, &wire(&[b"SELECT", b"3"]));
805        r.engine_mut().feed(a, &wire(&[b"SET", b"k", b"a"]));
806        r.engine_mut().feed(b, &wire(&[b"SET", b"k", b"b"]));
807        r.engine_mut().feed(a, &wire(&[b"GET", b"k"]));
808        r.engine_mut().feed(b, &wire(&[b"GET", b"k"]));
809        pump(&mut r, &mut batch);
810
811        assert_eq!(r.engine().sink().sent(a), b"+OK\r\n+OK\r\n$1\r\na\r\n");
812        assert_eq!(r.engine().sink().sent(b), b"+OK\r\n$1\r\nb\r\n");
813        assert_eq!(r.engine().clients(), 2);
814    }
815
816    /// The point of the whole exercise: two engines, two threads, one server.
817    ///
818    /// The server is told it will have two threads before either starts, the
819    /// way `yodb serve` tells it. Without that it has one set of counters and
820    /// both threads land on it, which is the wrap round `Server::mine_at`
821    /// documents and which loses counts: a bump is a load and a store rather
822    /// than a fetch and add, because the fast path is one thread writing its
823    /// own set and paying for a locked instruction on every command to make a
824    /// shared set exact would be paying it on the path that is never shared.
825    /// Miri found this by running the two threads far enough apart to lose one,
826    /// which a real machine does rarely enough to have passed here for months.
827    #[test]
828    fn two_threads_write_into_one_server() {
829        const EACH: usize = 200;
830
831        let mut server = Server::new();
832        server.set_threads(2);
833        let first = Wire::with_server(server, Recorder::new());
834        let second = Wire::over(first.shared(), Recorder::new());
835        let server = first.shared();
836
837        std::thread::scope(|s| {
838            for (at, engine) in [first, second].into_iter().enumerate() {
839                s.spawn(move || {
840                    let mut r = Reactor::inline(engine);
841                    let mut batch = Vec::new();
842                    let conn = r.engine_mut().accept();
843                    for i in 0..EACH {
844                        let key = format!("t{at}:{i}");
845                        r.engine_mut()
846                            .feed(conn, &wire(&[b"SET", key.as_bytes(), b"v"]));
847                        pump(&mut r, &mut batch);
848                    }
849                });
850            }
851        });
852
853        // Every key both threads wrote is in the one database, which is the
854        // whole claim: the fronts were separate and the keyspace was not.
855        assert_eq!(server.striped_ref(0).len(), 2 * EACH);
856        // And both threads counted into the same total, each from its own set
857        // of counters, which is what the sum over the threads is for.
858        assert_eq!(server.totals().connections, 2);
859    }
860
861    /// A blocked client is answered into a buffer one thread owns, so it is
862    /// that thread's to answer and nobody else's to throw away.
863    #[test]
864    fn a_waiter_belongs_to_the_thread_that_parked_it() {
865        let mut server = Server::new();
866        server.set_threads(2);
867        let first = Wire::with_server(server, Recorder::new());
868        let second = Wire::over(first.shared(), Recorder::new());
869        let server = first.shared();
870
871        let parked = std::sync::Barrier::new(2);
872        let swept = std::sync::Barrier::new(2);
873
874        std::thread::scope(|s| {
875            let (parked, swept) = (&parked, &swept);
876            s.spawn(move || {
877                let mut r = Reactor::inline(first);
878                let mut batch = Vec::new();
879                let conn = r.engine_mut().accept();
880                r.engine_mut().feed(conn, &wire(&[b"BLPOP", b"a", b"0"]));
881                pump(&mut r, &mut batch);
882                parked.wait();
883
884                // Turns with nothing on them, each of which walks a list whose
885                // one other entry belongs to the thread next door.
886                for _ in 0..50 {
887                    pump(&mut r, &mut batch);
888                }
889                swept.wait();
890                assert!(r.engine().sink().sent(conn).is_empty(), "nothing to say");
891            });
892            s.spawn(move || {
893                let mut r = Reactor::inline(second);
894                let mut batch = Vec::new();
895                let conn = r.engine_mut().accept();
896                r.engine_mut().feed(conn, &wire(&[b"BLPOP", b"b", b"0"]));
897                pump(&mut r, &mut batch);
898                parked.wait();
899                swept.wait();
900
901                // The push comes in on a second connection, because the first
902                // one is not reading anything while it waits.
903                let pusher = r.engine_mut().accept();
904                r.engine_mut().feed(pusher, &wire(&[b"RPUSH", b"b", b"v"]));
905                pump(&mut r, &mut batch);
906                assert_eq!(
907                    r.engine().sink().sent(conn),
908                    b"*2\r\n$1\r\nb\r\n$1\r\nv\r\n",
909                    "served by the thread that parked it"
910                );
911            });
912        });
913
914        assert_eq!(server.parked(), 1, "and the other one is still waiting");
915    }
916
917    /// The count a thread branches on before it reaches for the shared list is
918    /// its own, because the list is one lock and a thread can only answer what
919    /// it parked itself. Branching on the server wide count instead would put
920    /// every thread through that lock after every command as soon as one client
921    /// blocked anywhere.
922    #[test]
923    fn a_thread_counts_the_clients_it_blocked_and_nobody_else_s() {
924        let mut server = Server::new();
925        server.set_threads(2);
926        let first = Wire::with_server(server, Recorder::new());
927        let second = Wire::over(first.shared(), Recorder::new());
928        let server = first.shared();
929
930        let parked = std::sync::Barrier::new(2);
931        let looked = std::sync::Barrier::new(2);
932
933        std::thread::scope(|s| {
934            let (parked, looked) = (&parked, &looked);
935            s.spawn(move || {
936                let mut r = Reactor::inline(first);
937                let mut batch = Vec::new();
938                let conn = r.engine_mut().accept();
939                r.engine_mut().feed(conn, &wire(&[b"BLPOP", b"a", b"0"]));
940                pump(&mut r, &mut batch);
941                assert_eq!(r.engine().waiting(), 1, "the one this thread blocked");
942                parked.wait();
943                looked.wait();
944
945                // A second client of this thread's that never blocked, opened
946                // and closed. It is not on the list, so the count stays where
947                // it was rather than following the disconnect down.
948                let other = r.engine_mut().accept();
949                r.engine_mut().feed(other, &wire(&[b"PING"]));
950                pump(&mut r, &mut batch);
951                r.engine_mut().hangup(other);
952                pump(&mut r, &mut batch);
953                assert_eq!(r.engine().waiting(), 1, "still just the blocked one");
954            });
955            s.spawn(move || {
956                let mut r = Reactor::inline(second);
957                let mut batch = Vec::new();
958                parked.wait();
959
960                // A thread with nothing of its own blocked, on a server that
961                // has one client blocked on it.
962                pump(&mut r, &mut batch);
963                assert_eq!(r.engine().waiting(), 0, "none of them are this one's");
964                assert_eq!(r.engine().server().parked(), 1, "one on the server");
965                looked.wait();
966            });
967        });
968
969        assert_eq!(server.parked(), 1);
970    }
971
972    /// Two fronts hand out connection slots from zero, so the number that tells
973    /// two clients apart cannot come from a front.
974    #[test]
975    fn client_ids_are_the_server_s_to_hand_out() {
976        let first = Wire::new(Recorder::new());
977        let second = Wire::over(first.shared(), Recorder::new());
978        let mut a = Reactor::inline(first);
979        let mut b = Reactor::inline(second);
980
981        let (one, two) = (a.engine_mut().accept(), b.engine_mut().accept());
982        assert_eq!(one, two, "the same slot on each front");
983
984        // HELLO answers with the connection id, which is the number CLIENT
985        // KILL and CLIENT UNPAUSE take, so two fronts agreeing on it is two
986        // clients that cannot be told apart. Protocol three so that the proto
987        // field in the same reply is not one of the ids being looked for.
988        let mut batch = Vec::new();
989        a.engine_mut().feed(one, &wire(&[b"HELLO", b"3"]));
990        b.engine_mut().feed(two, &wire(&[b"HELLO", b"3"]));
991        pump(&mut a, &mut batch);
992        pump(&mut b, &mut batch);
993
994        let first = String::from_utf8_lossy(a.engine().sink().sent(one)).into_owned();
995        let second = String::from_utf8_lossy(b.engine().sink().sent(two)).into_owned();
996        assert!(first.contains(":1\r\n"), "{first}");
997        assert!(second.contains(":2\r\n"), "{second}");
998    }
999
1000    #[test]
1001    fn quit_is_answered_and_then_the_connection_goes() {
1002        let (mut r, conn, mut batch) = engine();
1003        r.engine_mut().feed(conn, &wire(&[b"PING"]));
1004        r.engine_mut().feed(conn, &wire(&[b"QUIT"]));
1005        pump(&mut r, &mut batch);
1006
1007        assert_eq!(r.engine().sink().sent(conn), b"+PONG\r\n+OK\r\n");
1008        assert!(r.engine().sink().was_closed(conn));
1009        assert_eq!(r.engine().clients(), 0);
1010
1011        // The slot comes back, buffers and all.
1012        let again = r.engine_mut().accept();
1013        assert_eq!(again, conn);
1014        assert_eq!(r.engine().clients(), 1);
1015    }
1016
1017    /// Redis's own unit/quit, which caught this: we answered the `QUIT` and
1018    /// then ran the `SET` behind it.
1019    #[test]
1020    fn what_a_client_pipelined_behind_quit_is_never_run() {
1021        let (mut r, conn, mut batch) = engine();
1022        let mut stream = wire(&[b"QUIT"]);
1023        stream.extend(wire(&[b"SET", b"foo", b"bar"]));
1024        r.engine_mut().feed(conn, &stream);
1025        // Both were framed, because framing happens before anything runs.
1026        assert_eq!(r.engine().ready(), 2);
1027        pump(&mut r, &mut batch);
1028
1029        // One reply and not two, and the connection is gone.
1030        assert_eq!(r.engine().sink().sent(conn), b"+OK\r\n");
1031        assert!(r.engine().sink().was_closed(conn));
1032
1033        // And the write never happened, which is the part a client can see
1034        // after it reconnects. The recorder is cleared first because the next
1035        // connection lands back in the slot this one just left, and what was
1036        // written to the slot before is still sitting in it.
1037        r.engine_mut().sink_mut().clear();
1038        let next = r.engine_mut().accept();
1039        r.engine_mut().feed(next, &wire(&[b"GET", b"foo"]));
1040        pump(&mut r, &mut batch);
1041        assert_eq!(r.engine().sink().sent(next), b"$-1\r\n");
1042    }
1043
1044    /// A connection that never said `HELLO` is answered in RESP2, whatever the
1045    /// last client in that slot was speaking.
1046    ///
1047    /// The protocol is kept in the reply buffer and the reply buffer outlives
1048    /// the connection, so this is the one piece of connection state that a
1049    /// recycled slot used to carry over. A client got a RESP3 null back from
1050    /// the first `GET` that missed and could not parse it, which is as bad as a
1051    /// compatibility bug gets: nothing the client did caused it and nothing it
1052    /// could send would have avoided it.
1053    #[test]
1054    fn a_slot_that_last_spoke_resp3_answers_the_next_client_in_resp2() {
1055        let (mut r, conn, mut batch) = engine();
1056        r.engine_mut().feed(conn, &wire(&[b"HELLO", b"3"]));
1057        r.engine_mut().feed(conn, &wire(&[b"GET", b"nothing"]));
1058        pump(&mut r, &mut batch);
1059        assert!(r.engine().sink().sent(conn).ends_with(b"_\r\n"));
1060        r.engine_mut().feed(conn, &wire(&[b"QUIT"]));
1061        pump(&mut r, &mut batch);
1062
1063        r.engine_mut().sink_mut().clear();
1064        let next = r.engine_mut().accept();
1065        assert_eq!(next, conn, "the same slot, which is what this is about");
1066        r.engine_mut().feed(next, &wire(&[b"GET", b"nothing"]));
1067        pump(&mut r, &mut batch);
1068        assert_eq!(r.engine().sink().sent(next), b"$-1\r\n");
1069    }
1070
1071    /// The other way a connection ends, which does not throw anything away.
1072    #[test]
1073    fn commands_that_arrived_before_a_protocol_error_are_still_answered() {
1074        let (mut r, conn, mut batch) = engine();
1075        let mut stream = wire(&[b"SET", b"k", b"v"]);
1076        stream.extend(wire(&[b"GET", b"k"]));
1077        stream.extend_from_slice(b"*1\r\n+notabulk\r\n");
1078        r.engine_mut().feed(conn, &stream);
1079        pump(&mut r, &mut batch);
1080
1081        // Both good commands were complete and correct before the stream went
1082        // wrong, so both are answered and the error comes after them.
1083        let sent = r.engine().sink().sent(conn);
1084        assert!(
1085            sent.starts_with(b"+OK\r\n$1\r\nv\r\n-ERR Protocol error: "),
1086            "{sent:?}"
1087        );
1088        assert!(r.engine().sink().was_closed(conn));
1089    }
1090
1091    #[test]
1092    fn a_protocol_error_is_written_and_closes_the_connection() {
1093        let (mut r, conn, mut batch) = engine();
1094        // A multibulk that says its first argument is a bulk and then does not.
1095        r.engine_mut().feed(conn, b"*1\r\n+notabulk\r\n");
1096        pump(&mut r, &mut batch);
1097
1098        let sent = r.engine().sink().sent(conn);
1099        assert!(sent.starts_with(b"-ERR Protocol error: "), "{sent:?}");
1100        assert!(r.engine().sink().was_closed(conn));
1101        assert_eq!(r.engine().clients(), 0);
1102    }
1103
1104    /// Redis's own `unit/protocol` walks a list of malformed frames, each on a
1105    /// fresh connection, which means every one of them after the first runs on
1106    /// a decoder that came back to the pool part way through a command.
1107    #[test]
1108    fn a_decoder_that_came_back_mid_command_starts_the_next_one_clean() {
1109        let (mut r, conn, mut batch) = engine();
1110        // Stops inside the third argument, on a length that is not a length.
1111        r.engine_mut()
1112            .feed(conn, b"*3\r\n$3\r\nSET\r\n$1\r\nx\r\n$blabla\r\n");
1113        pump(&mut r, &mut batch);
1114        let sent = r.engine().sink().sent(conn);
1115        assert!(
1116            sent.starts_with(b"-ERR Protocol error: invalid bulk length"),
1117            "{sent:?}"
1118        );
1119
1120        // The slot that decoder was in is now the slot the next connection
1121        // gets, and it has to be at the start of a command and not half way
1122        // through the one that went wrong.
1123        r.engine_mut().sink_mut().clear();
1124        let next = r.engine_mut().accept();
1125        r.engine_mut().feed(next, &wire(&[b"GET", b"k"]));
1126        pump(&mut r, &mut batch);
1127        assert_eq!(r.engine().sink().sent(next), b"$-1\r\n");
1128
1129        r.engine_mut().sink_mut().clear();
1130        let third = r.engine_mut().accept();
1131        r.engine_mut().feed(third, b"*1\r\n+notabulk\r\n");
1132        pump(&mut r, &mut batch);
1133        let sent = r.engine().sink().sent(third);
1134        assert!(sent.starts_with(b"-ERR Protocol error: "), "{sent:?}");
1135    }
1136
1137    /// A client that hangs up mid batch is the case that gets a server killed:
1138    /// the commands already framed still point into its buffer.
1139    #[test]
1140    fn a_hangup_with_commands_in_flight_waits_for_them() {
1141        let (mut r, conn, mut batch) = engine();
1142        r.engine_mut().feed(conn, &wire(&[b"SET", b"k", b"v"]));
1143        r.engine_mut().feed(conn, &wire(&[b"GET", b"k"]));
1144
1145        batch.clear();
1146        r.engine_mut().take_ready(&mut batch, BATCH_MAX);
1147        r.engine_mut().hangup(conn);
1148        assert_eq!(r.engine().clients(), 1, "still holding the buffer");
1149
1150        r.execute_all(batch.drain(..));
1151        r.engine_mut().flush();
1152        assert_eq!(r.engine().clients(), 0);
1153        assert!(r.engine().sink().sent(conn).is_empty(), "nobody to answer");
1154
1155        // And the slot is usable again, with the decoders both back in the
1156        // pool rather than lost with the connection.
1157        let decoders = r.engine().decoders();
1158        let again = r.engine_mut().accept();
1159        assert_eq!(again, conn);
1160        r.engine_mut().feed(again, &wire(&[b"PING"]));
1161        pump(&mut r, &mut batch);
1162        assert_eq!(r.engine().sink().sent(again), b"+PONG\r\n");
1163        assert_eq!(r.engine().decoders(), decoders);
1164    }
1165
1166    /// The claim that the steady state does not allocate, checked the only way
1167    /// a library test can check it: nothing grows.
1168    #[test]
1169    fn the_buffers_and_the_decoder_pool_stop_growing() {
1170        let (mut r, conn, mut batch) = engine();
1171        let mut stream = Vec::new();
1172        for i in 0..32 {
1173            stream.extend(wire(&[b"SET", format!("k{i}").as_bytes(), b"v"]));
1174        }
1175
1176        r.engine_mut().feed(conn, &stream);
1177        pump(&mut r, &mut batch);
1178        let decoders = r.engine().decoders();
1179        let batch_cap = batch.capacity();
1180
1181        for _ in 0..10 {
1182            r.engine_mut().feed(conn, &stream);
1183            pump(&mut r, &mut batch);
1184        }
1185        assert_eq!(r.engine().decoders(), decoders, "the pool is reused");
1186        assert_eq!(batch.capacity(), batch_cap, "the batch buffer is reused");
1187        assert!(
1188            decoders <= BATCH_MAX + 1,
1189            "{decoders} decoders for 32 commands"
1190        );
1191    }
1192
1193    /// The read buffer holds what has not been dealt with yet and nothing else.
1194    ///
1195    /// A client that pipelines sixteen commands, waits for the sixteen replies
1196    /// and goes again is what `redis-benchmark -P 16` does and what half of the
1197    /// clients in the world do. Every one of those rounds leaves the buffer
1198    /// exactly caught up, and a buffer that never drops what it has already
1199    /// dealt with grows to everything the connection has ever sent: 16 MiB
1200    /// apiece on server3 for four connections sending 100000 sets each.
1201    #[test]
1202    fn a_pipelining_client_does_not_grow_the_read_buffer() {
1203        let (mut r, conn, mut batch) = engine();
1204        let mut round = Vec::new();
1205        for i in 0..16 {
1206            round.extend(wire(&[b"SET", format!("k{i}").as_bytes(), b"v"]));
1207        }
1208
1209        r.engine_mut().feed(conn, &round);
1210        pump(&mut r, &mut batch);
1211        r.engine_mut().sink_mut().clear();
1212        let after_one = r.engine().buffer_bytes();
1213
1214        // A thousand rounds is sixteen thousand commands and about a megabyte
1215        // of wire bytes, which is a hundred times what the buffer starts with.
1216        // Fifty is a twentieth of that and it is what runs under Miri, where
1217        // sixteen thousand commands through the whole engine was a quarter of
1218        // an hour. The check below is that the size is the one it was after the
1219        // first round, exactly, so a buffer that keeps anything at all is
1220        // caught on the second round and every one after it, whichever count
1221        // this is.
1222        let rounds = if cfg!(miri) { 50 } else { 1000 };
1223        for _ in 0..rounds {
1224            r.engine_mut().feed(conn, &round);
1225            pump(&mut r, &mut batch);
1226            r.engine_mut().sink_mut().clear();
1227        }
1228
1229        assert_eq!(
1230            r.engine().buffer_bytes(),
1231            after_one,
1232            "the buffers grew over {rounds} rounds of the same sixteen commands"
1233        );
1234        assert!(
1235            r.engine().server().memory_bytes() >= after_one,
1236            "the buffers are counted in what the server reports"
1237        );
1238    }
1239
1240    /// Half a command in the buffer is the case compaction has to be careful
1241    /// about, because the decoder holding it kept offsets into those bytes.
1242    #[test]
1243    fn a_command_split_across_reads_survives_compaction() {
1244        let (mut r, conn, mut batch) = engine();
1245        let cmd = wire(&[b"SET", b"key", b"value"]);
1246        let (head, tail) = cmd.split_at(cmd.len() - 4);
1247
1248        // A complete command, so that there is something in front to drop, then
1249        // most of a second one.
1250        r.engine_mut().feed(conn, &wire(&[b"PING"]));
1251        r.engine_mut().feed(conn, head);
1252        pump(&mut r, &mut batch);
1253        assert_eq!(r.engine().sink().sent(conn), b"+PONG\r\n");
1254
1255        // The rest of it arrives after the buffer has been compacted under it.
1256        r.engine_mut().feed(conn, tail);
1257        pump(&mut r, &mut batch);
1258        assert_eq!(r.engine().sink().sent(conn), b"+PONG\r\n+OK\r\n");
1259
1260        r.engine_mut().feed(conn, &wire(&[b"GET", b"key"]));
1261        pump(&mut r, &mut batch);
1262        assert!(r.engine().sink().sent(conn).ends_with(b"$5\r\nvalue\r\n"));
1263    }
1264
1265    /// The two walks are the reactor's, not this module's, so the test is that
1266    /// the engine can be driven by them at all: same commands, same replies.
1267    #[test]
1268    fn the_batch_goes_through_the_reactors_two_walks() {
1269        let (mut r, conn, mut batch) = engine();
1270        for i in 0..100 {
1271            r.engine_mut()
1272                .feed(conn, &wire(&[b"INCR", format!("k{}", i % 7).as_bytes()]));
1273        }
1274        let ran = pump(&mut r, &mut batch);
1275
1276        assert_eq!(ran, 100);
1277        assert_eq!(r.commands(), 100);
1278        // Two batches, because a hundred commands do not fit in sixty four.
1279        assert_eq!(r.turns(), 2);
1280        // The hundredth command is the fifteenth `INCR` of `k1`.
1281        assert!(r.engine().sink().sent(conn).ends_with(b":15\r\n"));
1282    }
1283
1284    /// A sink that takes four bytes at a time, which is what a full socket
1285    /// looks like from in here.
1286    #[derive(Default)]
1287    struct Trickle {
1288        sent: Vec<u8>,
1289        writes: usize,
1290    }
1291
1292    impl Sink for Trickle {
1293        fn write(&mut self, _conn: ConnId, bytes: &[u8]) -> usize {
1294            self.writes += 1;
1295            let n = bytes.len().min(4);
1296            self.sent.extend_from_slice(&bytes[..n]);
1297            n
1298        }
1299    }
1300
1301    /// A blocking command that does not block costs nothing: no waiter, no
1302    /// allocation, the same three lines the non blocking one runs.
1303    #[test]
1304    fn a_blpop_on_a_list_with_something_in_it_never_waits() {
1305        let (mut r, conn, mut batch) = engine();
1306        r.engine_mut().feed(conn, &wire(&[b"RPUSH", b"q", b"a"]));
1307        r.engine_mut().feed(conn, &wire(&[b"BLPOP", b"q", b"0"]));
1308        pump(&mut r, &mut batch);
1309
1310        assert_eq!(
1311            r.engine().sink().sent(conn),
1312            b":1\r\n*2\r\n$1\r\nq\r\n$1\r\na\r\n"
1313        );
1314        assert_eq!(r.engine().server().parked(), 0);
1315    }
1316
1317    /// The whole point: a client with nothing to pop is answered later, by
1318    /// somebody else's command.
1319    #[test]
1320    fn a_parked_client_is_answered_by_another_connections_push() {
1321        let (mut r, a, mut batch) = engine();
1322        let b = r.engine_mut().accept();
1323
1324        r.engine_mut().feed(a, &wire(&[b"BLPOP", b"q", b"0"]));
1325        pump(&mut r, &mut batch);
1326        assert!(r.engine().sink().sent(a).is_empty(), "nothing to say yet");
1327        assert_eq!(r.engine().server().parked(), 1);
1328
1329        r.engine_mut().feed(b, &wire(&[b"RPUSH", b"q", b"one"]));
1330        pump(&mut r, &mut batch);
1331
1332        assert_eq!(r.engine().sink().sent(a), b"*2\r\n$1\r\nq\r\n$3\r\none\r\n");
1333        // The push still reports the length it made, even though the element was
1334        // gone again before the reply was written.
1335        assert_eq!(r.engine().sink().sent(b), b":1\r\n");
1336        assert_eq!(r.engine().server().parked(), 0);
1337    }
1338
1339    /// A push to a key nobody named, and a key of another type on a key
1340    /// somebody did: neither is a wake up, and the client stays parked.
1341    #[test]
1342    fn only_a_list_arriving_under_a_named_key_wakes_a_waiter() {
1343        let (mut r, a, mut batch) = engine();
1344        let b = r.engine_mut().accept();
1345        r.engine_mut().feed(a, &wire(&[b"BLPOP", b"q", b"0"]));
1346        pump(&mut r, &mut batch);
1347
1348        r.engine_mut()
1349            .feed(b, &wire(&[b"RPUSH", b"elsewhere", b"x"]));
1350        r.engine_mut().feed(b, &wire(&[b"SADD", b"q", b"x"]));
1351        pump(&mut r, &mut batch);
1352
1353        assert!(r.engine().sink().sent(a).is_empty());
1354        assert_eq!(r.engine().server().parked(), 1, "still waiting");
1355        // And the set is intact, so the waiter did not take anything out of it
1356        // on its way past.
1357        assert_eq!(r.engine().sink().sent(b), b":1\r\n:1\r\n");
1358    }
1359
1360    /// Two workers on one queue, which is what `BLPOP` is for. They are served
1361    /// in the order they arrived and not in whatever order the list is walked.
1362    #[test]
1363    fn two_parked_clients_are_served_in_the_order_they_arrived() {
1364        let (mut r, a, mut batch) = engine();
1365        let b = r.engine_mut().accept();
1366        let c = r.engine_mut().accept();
1367
1368        r.engine_mut().feed(a, &wire(&[b"BLPOP", b"q", b"0"]));
1369        pump(&mut r, &mut batch);
1370        r.engine_mut().feed(b, &wire(&[b"BLPOP", b"q", b"0"]));
1371        pump(&mut r, &mut batch);
1372        assert_eq!(r.engine().server().parked(), 2);
1373
1374        r.engine_mut()
1375            .feed(c, &wire(&[b"RPUSH", b"q", b"first", b"second"]));
1376        pump(&mut r, &mut batch);
1377
1378        assert_eq!(
1379            r.engine().sink().sent(a),
1380            b"*2\r\n$1\r\nq\r\n$5\r\nfirst\r\n"
1381        );
1382        assert_eq!(
1383            r.engine().sink().sent(b),
1384            b"*2\r\n$1\r\nq\r\n$6\r\nsecond\r\n"
1385        );
1386        assert_eq!(r.engine().server().parked(), 0);
1387    }
1388
1389    /// A client waiting for an answer is not a client that has sent another
1390    /// question, so what it pipelined behind its `BLPOP` waits for the `BLPOP`.
1391    #[test]
1392    fn what_a_client_pipelined_behind_a_block_waits_for_the_block() {
1393        let (mut r, a, mut batch) = engine();
1394        let b = r.engine_mut().accept();
1395
1396        // Framed together, so the `PING` is already on its way to the reactor
1397        // when the `BLPOP` in front of it parks.
1398        let mut stream = wire(&[b"BLPOP", b"q", b"0"]);
1399        stream.extend(wire(&[b"PING"]));
1400        r.engine_mut().feed(a, &stream);
1401        pump(&mut r, &mut batch);
1402        assert!(
1403            r.engine().sink().sent(a).is_empty(),
1404            "the PING went out in front of the answer it was sent behind"
1405        );
1406
1407        // And one that arrives while it is parked is not even framed.
1408        r.engine_mut().feed(a, &wire(&[b"ECHO", b"after"]));
1409        pump(&mut r, &mut batch);
1410        assert!(r.engine().sink().sent(a).is_empty());
1411
1412        r.engine_mut().feed(b, &wire(&[b"RPUSH", b"q", b"x"]));
1413        pump(&mut r, &mut batch);
1414        assert_eq!(
1415            r.engine().sink().sent(a),
1416            b"*2\r\n$1\r\nq\r\n$1\r\nx\r\n+PONG\r\n$5\r\nafter\r\n"
1417        );
1418    }
1419
1420    /// Redis serves parked clients after every command rather than once per
1421    /// turn of the loop, and a pipeline is where the difference shows: the
1422    /// waiter has to be served between the two pushes, so it answers with the
1423    /// key the first push filled and not with the one it named first.
1424    #[test]
1425    fn a_waiter_is_served_between_two_pipelined_pushes() {
1426        let (mut r, a, mut batch) = engine();
1427        let b = r.engine_mut().accept();
1428        r.engine_mut()
1429            .feed(a, &wire(&[b"BLPOP", b"p1", b"p2", b"0"]));
1430        pump(&mut r, &mut batch);
1431
1432        let mut stream = wire(&[b"RPUSH", b"p2", b"second"]);
1433        stream.extend(wire(&[b"RPUSH", b"p1", b"first"]));
1434        r.engine_mut().feed(b, &stream);
1435        pump(&mut r, &mut batch);
1436
1437        assert_eq!(
1438            r.engine().sink().sent(a),
1439            b"*2\r\n$2\r\np2\r\n$6\r\nsecond\r\n"
1440        );
1441        // Which leaves the key it named first holding what was pushed to it.
1442        r.engine_mut()
1443            .feed(b, &wire(&[b"LRANGE", b"p1", b"0", b"-1"]));
1444        pump(&mut r, &mut batch);
1445        assert!(
1446            r.engine()
1447                .sink()
1448                .sent(b)
1449                .ends_with(b"*1\r\n$5\r\nfirst\r\n")
1450        );
1451    }
1452
1453    /// A `BLMOVE` that serves itself is a push, so it wakes the client waiting
1454    /// on the key it pushed to, in the same moment and without a turn of the
1455    /// loop in between.
1456    #[test]
1457    fn a_waiter_woken_by_another_waiter() {
1458        let (mut r, a, mut batch) = engine();
1459        let b = r.engine_mut().accept();
1460        let c = r.engine_mut().accept();
1461
1462        r.engine_mut()
1463            .feed(a, &wire(&[b"BLMOVE", b"x", b"y", b"LEFT", b"RIGHT", b"0"]));
1464        pump(&mut r, &mut batch);
1465        r.engine_mut().feed(b, &wire(&[b"BLPOP", b"y", b"0"]));
1466        pump(&mut r, &mut batch);
1467        assert_eq!(r.engine().server().parked(), 2);
1468
1469        r.engine_mut().feed(c, &wire(&[b"RPUSH", b"x", b"chain"]));
1470        pump(&mut r, &mut batch);
1471
1472        assert_eq!(r.engine().sink().sent(a), b"$5\r\nchain\r\n");
1473        assert_eq!(
1474            r.engine().sink().sent(b),
1475            b"*2\r\n$1\r\ny\r\n$5\r\nchain\r\n"
1476        );
1477        assert_eq!(r.engine().server().parked(), 0);
1478    }
1479
1480    /// A waiter on one database is not woken by a push on another, even though
1481    /// the key has the same name.
1482    #[test]
1483    fn a_waiter_is_only_woken_on_the_database_it_blocked_on() {
1484        let (mut r, a, mut batch) = engine();
1485        let b = r.engine_mut().accept();
1486        r.engine_mut().feed(a, &wire(&[b"SELECT", b"3"]));
1487        r.engine_mut().feed(a, &wire(&[b"BLPOP", b"q", b"0"]));
1488        pump(&mut r, &mut batch);
1489        assert_eq!(r.engine().sink().sent(a), b"+OK\r\n");
1490
1491        r.engine_mut().feed(b, &wire(&[b"RPUSH", b"q", b"wrongdb"]));
1492        pump(&mut r, &mut batch);
1493        assert_eq!(r.engine().sink().sent(a), b"+OK\r\n", "still waiting");
1494
1495        r.engine_mut().feed(b, &wire(&[b"SELECT", b"3"]));
1496        r.engine_mut().feed(b, &wire(&[b"RPUSH", b"q", b"rightdb"]));
1497        pump(&mut r, &mut batch);
1498        assert!(r.engine().sink().sent(a).ends_with(b"$7\r\nrightdb\r\n"));
1499    }
1500
1501    /// The deadline sweep, which runs on a turn that has nothing else to do.
1502    #[test]
1503    fn a_client_that_waited_long_enough_gets_a_null_array() {
1504        let (mut r, conn, mut batch) = timed();
1505        r.engine_mut().feed(conn, &wire(&[b"BLPOP", b"q", b"30"]));
1506        pump(&mut r, &mut batch);
1507        assert!(r.engine().sink().sent(conn).is_empty());
1508
1509        r.engine_mut().server_mut().set_clock_ms(START_MS + 29_999);
1510        pump(&mut r, &mut batch);
1511        assert!(
1512            r.engine().sink().sent(conn).is_empty(),
1513            "a millisecond short"
1514        );
1515
1516        r.engine_mut().server_mut().set_clock_ms(START_MS + 30_000);
1517        pump(&mut r, &mut batch);
1518        // A null array and not a null string, which a RESP2 client can see.
1519        assert_eq!(r.engine().sink().sent(conn), b"*-1\r\n");
1520        assert_eq!(r.engine().server().parked(), 0);
1521    }
1522
1523    /// The four that answer with something other than a two element array all
1524    /// answer a timeout the same way, which is not what the reply shape would
1525    /// suggest and is what Redis does.
1526    #[test]
1527    fn every_blocking_command_times_out_with_the_same_null_array() {
1528        for cmd in [
1529            &[b"BLPOP".as_slice(), b"q", b"0.001"][..],
1530            &[b"BRPOP", b"q", b"0.001"],
1531            &[b"BLMOVE", b"q", b"d", b"LEFT", b"RIGHT", b"0.001"],
1532            &[b"BRPOPLPUSH", b"q", b"d", b"0.001"],
1533            &[b"BLMPOP", b"0.001", b"1", b"q", b"LEFT"],
1534        ] {
1535            let (mut r, conn, mut batch) = timed();
1536            r.engine_mut().feed(conn, &wire(cmd));
1537            pump(&mut r, &mut batch);
1538            r.engine_mut().server_mut().set_clock_ms(START_MS + 1);
1539            pump(&mut r, &mut batch);
1540            assert_eq!(r.engine().sink().sent(conn), b"*-1\r\n", "for {cmd:?}");
1541        }
1542    }
1543
1544    /// A client that gave up does not go on holding a claim on the queue: the
1545    /// element that arrives after it stays where it was put.
1546    #[test]
1547    fn a_waiter_that_timed_out_does_not_eat_a_later_push() {
1548        let (mut r, a, mut batch) = timed();
1549        let b = r.engine_mut().accept();
1550        r.engine_mut().feed(a, &wire(&[b"BLPOP", b"q", b"1"]));
1551        pump(&mut r, &mut batch);
1552        r.engine_mut().server_mut().set_clock_ms(START_MS + 1000);
1553        pump(&mut r, &mut batch);
1554        assert_eq!(r.engine().sink().sent(a), b"*-1\r\n");
1555
1556        r.engine_mut().feed(b, &wire(&[b"RPUSH", b"q", b"late"]));
1557        r.engine_mut()
1558            .feed(b, &wire(&[b"LRANGE", b"q", b"0", b"-1"]));
1559        pump(&mut r, &mut batch);
1560        assert_eq!(r.engine().sink().sent(a), b"*-1\r\n", "nothing more");
1561        assert!(r.engine().sink().sent(b).ends_with(b"*1\r\n$4\r\nlate\r\n"));
1562    }
1563
1564    /// A `BLPOP key 0` has no deadline, so nothing but the connection closing
1565    /// will ever take it off the list. That makes the close path the one that
1566    /// has to be right, or a waiter outlives its client and the slot it names
1567    /// gets handed to somebody else.
1568    #[test]
1569    fn a_client_that_goes_away_while_it_waits_takes_its_waiter_with_it() {
1570        let (mut r, a, mut batch) = engine();
1571        let b = r.engine_mut().accept();
1572        r.engine_mut().feed(a, &wire(&[b"BLPOP", b"q", b"0"]));
1573        pump(&mut r, &mut batch);
1574        assert_eq!(r.engine().server().parked(), 1);
1575
1576        r.engine_mut().hangup(a);
1577        pump(&mut r, &mut batch);
1578        assert_eq!(r.engine().server().parked(), 0);
1579        assert_eq!(r.engine().clients(), 1);
1580
1581        // The slot is handed straight back out, which is what the waiter would
1582        // have been pointing at.
1583        let again = r.engine_mut().accept();
1584        assert_eq!(again, a);
1585        r.engine_mut().feed(b, &wire(&[b"RPUSH", b"q", b"x"]));
1586        r.engine_mut()
1587            .feed(again, &wire(&[b"LRANGE", b"q", b"0", b"-1"]));
1588        pump(&mut r, &mut batch);
1589        assert_eq!(r.engine().sink().sent(again), b"*1\r\n$1\r\nx\r\n");
1590    }
1591
1592    /// The same, with commands the client had already sent sitting behind the
1593    /// block. Those are what `pending` counts, so a close that forgets them is a
1594    /// connection slot that never comes back.
1595    #[test]
1596    fn a_hangup_while_parked_gives_back_the_slot_and_the_decoders() {
1597        let (mut r, a, mut batch) = engine();
1598        let mut stream = wire(&[b"BLPOP", b"q", b"0"]);
1599        stream.extend(wire(&[b"PING"]));
1600        stream.extend(wire(&[b"PING"]));
1601        r.engine_mut().feed(a, &stream);
1602        pump(&mut r, &mut batch);
1603
1604        let decoders = r.engine().decoders();
1605        r.engine_mut().hangup(a);
1606        pump(&mut r, &mut batch);
1607
1608        assert_eq!(r.engine().clients(), 0);
1609        assert!(r.engine().sink().was_closed(a));
1610        assert_eq!(r.engine().decoders(), decoders, "the pool came back whole");
1611        let again = r.engine_mut().accept();
1612        assert_eq!(again, a);
1613        r.engine_mut().feed(again, &wire(&[b"PING"]));
1614        pump(&mut r, &mut batch);
1615        assert_eq!(r.engine().sink().sent(again), b"+PONG\r\n");
1616    }
1617
1618    /// The whole point of a mailbox: a publish on one connection turns into
1619    /// bytes on another, in the same flush.
1620    #[test]
1621    fn a_published_message_lands_on_the_subscriber() {
1622        let (mut r, sub, mut batch) = engine();
1623        let pubr = r.engine_mut().accept();
1624
1625        r.engine_mut().feed(sub, &wire(&[b"SUBSCRIBE", b"news"]));
1626        pump(&mut r, &mut batch);
1627        assert_eq!(
1628            r.engine().sink().sent(sub),
1629            b"*3\r\n$9\r\nsubscribe\r\n$4\r\nnews\r\n:1\r\n"
1630        );
1631        r.engine_mut().sink_mut().clear();
1632
1633        r.engine_mut()
1634            .feed(pubr, &wire(&[b"PUBLISH", b"news", b"hi"]));
1635        pump(&mut r, &mut batch);
1636        assert_eq!(r.engine().sink().sent(pubr), b":1\r\n");
1637        assert_eq!(
1638            r.engine().sink().sent(sub),
1639            b"*3\r\n$7\r\nmessage\r\n$4\r\nnews\r\n$2\r\nhi\r\n"
1640        );
1641    }
1642
1643    /// A pattern subscriber is told which of its patterns matched as well as
1644    /// which channel the message went to, so the reply is one field longer.
1645    #[test]
1646    fn a_pattern_subscriber_is_told_the_pattern_and_the_channel() {
1647        let (mut r, sub, mut batch) = engine();
1648        let pubr = r.engine_mut().accept();
1649
1650        r.engine_mut().feed(sub, &wire(&[b"PSUBSCRIBE", b"ne*"]));
1651        pump(&mut r, &mut batch);
1652        r.engine_mut().sink_mut().clear();
1653
1654        r.engine_mut()
1655            .feed(pubr, &wire(&[b"PUBLISH", b"news", b"hi"]));
1656        pump(&mut r, &mut batch);
1657        assert_eq!(r.engine().sink().sent(pubr), b":1\r\n");
1658        assert_eq!(
1659            r.engine().sink().sent(sub),
1660            b"*4\r\n$8\r\npmessage\r\n$3\r\nne*\r\n$4\r\nnews\r\n$2\r\nhi\r\n"
1661        );
1662    }
1663
1664    /// A RESP2 client that has subscribed to anything can only leave, ping or
1665    /// subscribe to something else until it unsubscribes, because on RESP2 a
1666    /// message and a reply are the same shape and a client reading one cannot
1667    /// tell them apart.
1668    #[test]
1669    fn resp2_takes_almost_nothing_from_a_subscriber() {
1670        let (mut r, conn, mut batch) = engine();
1671
1672        r.engine_mut().feed(conn, &wire(&[b"SUBSCRIBE", b"a"]));
1673        pump(&mut r, &mut batch);
1674        r.engine_mut().sink_mut().clear();
1675
1676        r.engine_mut().feed(conn, &wire(&[b"GET", b"k"]));
1677        pump(&mut r, &mut batch);
1678        assert_eq!(
1679            r.engine().sink().sent(conn),
1680            b"-ERR Can't execute 'get': only (P|S)SUBSCRIBE / (P|S)UNSUBSCRIBE / PING / QUIT / RESET are allowed in this context\r\n"
1681        );
1682        r.engine_mut().sink_mut().clear();
1683
1684        // Ping is allowed, and answers in the shape the mode uses.
1685        r.engine_mut().feed(conn, &wire(&[b"PING"]));
1686        pump(&mut r, &mut batch);
1687        assert_eq!(
1688            r.engine().sink().sent(conn),
1689            b"*2\r\n$4\r\npong\r\n$0\r\n\r\n"
1690        );
1691        r.engine_mut().sink_mut().clear();
1692
1693        // And unsubscribing puts the connection back to ordinary work.
1694        r.engine_mut().feed(conn, &wire(&[b"UNSUBSCRIBE", b"a"]));
1695        r.engine_mut().feed(conn, &wire(&[b"GET", b"k"]));
1696        pump(&mut r, &mut batch);
1697        assert_eq!(
1698            r.engine().sink().sent(conn),
1699            b"*3\r\n$11\r\nunsubscribe\r\n$1\r\na\r\n:0\r\n$-1\r\n"
1700        );
1701    }
1702
1703    /// Shard channels are their own namespace. A name subscribed as a shard
1704    /// channel does not hear a plain publish to the same name, and a pattern
1705    /// never matches a shard publish.
1706    #[test]
1707    fn a_shard_channel_and_a_pattern_do_not_hear_each_other() {
1708        let (mut r, sub, mut batch) = engine();
1709        let pubr = r.engine_mut().accept();
1710
1711        r.engine_mut().feed(sub, &wire(&[b"SSUBSCRIBE", b"sx"]));
1712        r.engine_mut().feed(sub, &wire(&[b"PSUBSCRIBE", b"s*"]));
1713        pump(&mut r, &mut batch);
1714        r.engine_mut().sink_mut().clear();
1715
1716        r.engine_mut()
1717            .feed(pubr, &wire(&[b"SPUBLISH", b"sx", b"one"]));
1718        pump(&mut r, &mut batch);
1719        assert_eq!(r.engine().sink().sent(pubr), b":1\r\n");
1720        assert_eq!(
1721            r.engine().sink().sent(sub),
1722            b"*3\r\n$8\r\nsmessage\r\n$2\r\nsx\r\n$3\r\none\r\n"
1723        );
1724        r.engine_mut().sink_mut().clear();
1725
1726        r.engine_mut()
1727            .feed(pubr, &wire(&[b"PUBLISH", b"sx", b"two"]));
1728        pump(&mut r, &mut batch);
1729        assert_eq!(r.engine().sink().sent(pubr), b":1\r\n");
1730        assert_eq!(
1731            r.engine().sink().sent(sub),
1732            b"*4\r\n$8\r\npmessage\r\n$2\r\ns*\r\n$2\r\nsx\r\n$3\r\ntwo\r\n"
1733        );
1734    }
1735
1736    /// A subscriber that hangs up stops being one, which matters because the
1737    /// registry holds a connection id and that id gets handed to the next
1738    /// client through the door.
1739    #[test]
1740    fn a_subscriber_that_goes_away_leaves_the_registry() {
1741        let (mut r, sub, mut batch) = engine();
1742        let pubr = r.engine_mut().accept();
1743
1744        r.engine_mut().feed(sub, &wire(&[b"SUBSCRIBE", b"news"]));
1745        pump(&mut r, &mut batch);
1746        r.engine_mut().hangup(sub);
1747        pump(&mut r, &mut batch);
1748        r.engine_mut().sink_mut().clear();
1749
1750        r.engine_mut()
1751            .feed(pubr, &wire(&[b"PUBLISH", b"news", b"hi"]));
1752        pump(&mut r, &mut batch);
1753        assert_eq!(r.engine().sink().sent(pubr), b":0\r\n");
1754
1755        // And the slot is clean for whoever gets it next.
1756        let next = r.engine_mut().accept();
1757        assert_eq!(next, sub);
1758        r.engine_mut()
1759            .feed(pubr, &wire(&[b"PUBLISH", b"news", b"hi"]));
1760        pump(&mut r, &mut batch);
1761        assert_eq!(r.engine().sink().sent(next), b"");
1762    }
1763
1764    /// On RESP3 a message is a push, not a reply, so it can be read off a
1765    /// connection that is doing something else, and that connection is free to
1766    /// run ordinary commands while it is subscribed.
1767    ///
1768    /// It also pins the order a publish to yourself comes out in. Nothing in
1769    /// the code special cases it: the count is the reply to the command and the
1770    /// message is delivered on the way out with everybody else's, so the count
1771    /// is first.
1772    #[test]
1773    fn resp3_delivers_a_message_as_a_push() {
1774        let (mut r, conn, mut batch) = engine();
1775
1776        r.engine_mut().feed(conn, &wire(&[b"HELLO", b"3"]));
1777        r.engine_mut().feed(conn, &wire(&[b"SUBSCRIBE", b"a"]));
1778        pump(&mut r, &mut batch);
1779        r.engine_mut().sink_mut().clear();
1780
1781        r.engine_mut().feed(conn, &wire(&[b"GET", b"k"]));
1782        r.engine_mut().feed(conn, &wire(&[b"PUBLISH", b"a", b"w"]));
1783        pump(&mut r, &mut batch);
1784        assert_eq!(
1785            r.engine().sink().sent(conn),
1786            b"_\r\n:1\r\n>3\r\n$7\r\nmessage\r\n$1\r\na\r\n$1\r\nw\r\n"
1787        );
1788    }
1789
1790    /// A write publishes twice, once on the channel named after the key and
1791    /// once on the channel named after the event, in that order.
1792    #[test]
1793    fn a_write_reaches_a_keyspace_subscriber() {
1794        let (mut r, sub, mut batch) = engine();
1795        let writer = r.engine_mut().accept();
1796
1797        r.engine_mut().feed(
1798            writer,
1799            &wire(&[b"CONFIG", b"SET", b"notify-keyspace-events", b"KEA"]),
1800        );
1801        r.engine_mut()
1802            .feed(sub, &wire(&[b"PSUBSCRIBE", b"__key*@0__:*"]));
1803        pump(&mut r, &mut batch);
1804        r.engine_mut().sink_mut().clear();
1805
1806        r.engine_mut().feed(writer, &wire(&[b"SET", b"k", b"v"]));
1807        pump(&mut r, &mut batch);
1808        assert_eq!(r.engine().sink().sent(writer), b"+OK\r\n");
1809        assert_eq!(
1810            r.engine().sink().sent(sub),
1811            b"*4\r\n$8\r\npmessage\r\n$12\r\n__key*@0__:*\r\n\
1812              $16\r\n__keyspace@0__:k\r\n$3\r\nset\r\n\
1813              *4\r\n$8\r\npmessage\r\n$12\r\n__key*@0__:*\r\n\
1814              $18\r\n__keyevent@0__:set\r\n$1\r\nk\r\n"
1815        );
1816    }
1817
1818    /// The setting is off by default, so a subscriber on the notification
1819    /// channels of a server nobody has turned them on for hears nothing.
1820    #[test]
1821    fn a_write_says_nothing_until_the_setting_turns_it_on() {
1822        let (mut r, sub, mut batch) = engine();
1823        let writer = r.engine_mut().accept();
1824
1825        r.engine_mut()
1826            .feed(sub, &wire(&[b"PSUBSCRIBE", b"__key*@0__:*"]));
1827        pump(&mut r, &mut batch);
1828        r.engine_mut().sink_mut().clear();
1829
1830        r.engine_mut().feed(writer, &wire(&[b"SET", b"k", b"v"]));
1831        pump(&mut r, &mut batch);
1832        assert_eq!(r.engine().sink().sent(sub), b"");
1833    }
1834
1835    /// `g` without `$` is the generic class and not the string one, so a
1836    /// delete goes out and the write that made the key does not.
1837    #[test]
1838    fn only_the_classes_that_were_asked_for_are_published() {
1839        let (mut r, sub, mut batch) = engine();
1840        let writer = r.engine_mut().accept();
1841
1842        r.engine_mut().feed(
1843            writer,
1844            &wire(&[b"CONFIG", b"SET", b"notify-keyspace-events", b"Eg"]),
1845        );
1846        r.engine_mut()
1847            .feed(sub, &wire(&[b"PSUBSCRIBE", b"__key*@0__:*"]));
1848        pump(&mut r, &mut batch);
1849        r.engine_mut().sink_mut().clear();
1850
1851        r.engine_mut().feed(writer, &wire(&[b"SET", b"k", b"v"]));
1852        r.engine_mut().feed(writer, &wire(&[b"DEL", b"k"]));
1853        pump(&mut r, &mut batch);
1854        assert_eq!(
1855            r.engine().sink().sent(sub),
1856            b"*4\r\n$8\r\npmessage\r\n$12\r\n__key*@0__:*\r\n\
1857              $18\r\n__keyevent@0__:del\r\n$1\r\nk\r\n"
1858        );
1859    }
1860
1861    /// A command that took a deadline with it says two things, and they come
1862    /// out in the order the server did them rather than all at the end.
1863    #[test]
1864    fn a_write_with_a_deadline_on_it_says_two_things() {
1865        let (mut r, sub, mut batch) = engine();
1866        let writer = r.engine_mut().accept();
1867
1868        r.engine_mut().feed(
1869            writer,
1870            &wire(&[b"CONFIG", b"SET", b"notify-keyspace-events", b"EA"]),
1871        );
1872        r.engine_mut()
1873            .feed(sub, &wire(&[b"PSUBSCRIBE", b"__keyevent@0__:*"]));
1874        pump(&mut r, &mut batch);
1875        r.engine_mut().sink_mut().clear();
1876
1877        r.engine_mut()
1878            .feed(writer, &wire(&[b"SETEX", b"k", b"100", b"v"]));
1879        pump(&mut r, &mut batch);
1880        assert_eq!(
1881            r.engine().sink().sent(sub),
1882            b"*4\r\n$8\r\npmessage\r\n$16\r\n__keyevent@0__:*\r\n\
1883              $18\r\n__keyevent@0__:set\r\n$1\r\nk\r\n\
1884              *4\r\n$8\r\npmessage\r\n$16\r\n__keyevent@0__:*\r\n\
1885              $21\r\n__keyevent@0__:expire\r\n$1\r\nk\r\n"
1886        );
1887    }
1888
1889    /// Inside a transaction each command's notifications go out before the
1890    /// next command runs, so `EXEC` does not bunch them all up at the end.
1891    #[test]
1892    fn a_transaction_publishes_between_its_commands_and_not_after_them() {
1893        let (mut r, sub, mut batch) = engine();
1894        let writer = r.engine_mut().accept();
1895
1896        r.engine_mut().feed(
1897            writer,
1898            &wire(&[b"CONFIG", b"SET", b"notify-keyspace-events", b"EA"]),
1899        );
1900        r.engine_mut()
1901            .feed(sub, &wire(&[b"PSUBSCRIBE", b"__keyevent@0__:*"]));
1902        pump(&mut r, &mut batch);
1903        r.engine_mut().sink_mut().clear();
1904
1905        r.engine_mut().feed(writer, &wire(&[b"MULTI"]));
1906        r.engine_mut().feed(writer, &wire(&[b"SET", b"k", b"v"]));
1907        r.engine_mut().feed(writer, &wire(&[b"DEL", b"k"]));
1908        r.engine_mut().feed(writer, &wire(&[b"EXEC"]));
1909        pump(&mut r, &mut batch);
1910        assert_eq!(
1911            r.engine().sink().sent(sub),
1912            b"*4\r\n$8\r\npmessage\r\n$16\r\n__keyevent@0__:*\r\n\
1913              $18\r\n__keyevent@0__:set\r\n$1\r\nk\r\n\
1914              *4\r\n$8\r\npmessage\r\n$16\r\n__keyevent@0__:*\r\n\
1915              $18\r\n__keyevent@0__:del\r\n$1\r\nk\r\n"
1916        );
1917    }
1918
1919    #[test]
1920    fn a_reply_the_socket_would_not_take_is_offered_again() {
1921        let mut r = Reactor::inline(Wire::new(Trickle::default()));
1922        let conn = r.engine_mut().accept();
1923        let mut batch = Vec::new();
1924
1925        r.engine_mut().feed(conn, &wire(&[b"PING"]));
1926        pump(&mut r, &mut batch);
1927        // Two flushes in a pump, so four bytes and then three.
1928        assert_eq!(r.engine().sink().sent, b"+PONG\r\n");
1929        assert_eq!(r.engine().sink().writes, 2);
1930    }
1931}