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}
179
180impl<S: Sink> Wire<S> {
181    /// An engine with an empty server.
182    #[must_use]
183    pub fn new(sink: S) -> Wire<S> {
184        Wire::with_server(Server::new(), sink)
185    }
186
187    /// An engine over a server the caller built, which is how a test gives it a
188    /// clock it can move by hand.
189    #[must_use]
190    pub fn with_server(server: Server, sink: S) -> Wire<S> {
191        Wire::over(Arc::new(server), sink)
192    }
193
194    /// An engine over a server that already exists, which is how the second
195    /// thread and every thread after it gets one.
196    ///
197    /// Each thread builds its own front and they never see each other's. What
198    /// they share is behind the handle, and the reason the handle is counted
199    /// rather than borrowed is that the threads outlive whichever call started
200    /// them by design: a scope that borrows would tie the server's lifetime to
201    /// a frame that is meant to return.
202    #[must_use]
203    pub fn over(server: Arc<Server>, sink: S) -> Wire<S> {
204        Wire {
205            front: Front::new(sink),
206            parked: Vec::new(),
207            server,
208        }
209    }
210
211    /// The databases and the numbers `INFO` reports.
212    #[must_use]
213    pub fn server(&self) -> &Server {
214        &self.server
215    }
216
217    /// Another handle on the same server, for building the next thread's
218    /// engine.
219    #[must_use]
220    pub fn shared(&self) -> Arc<Server> {
221        Arc::clone(&self.server)
222    }
223
224    /// The server, for the few settings that have to be made before it is
225    /// serving.
226    ///
227    /// That is the directory and the thread count, both of which are read
228    /// everywhere and written once at startup, so they are settings and not
229    /// state. This works while this engine holds the only handle, which is the
230    /// case from the moment the server is built until the threads are started,
231    /// and it is the caller's job to do its setting up in that window.
232    ///
233    /// # Panics
234    ///
235    /// If a second handle already exists, because there is no honest answer to
236    /// give: changing the directory under a thread that is already serving out
237    /// of it is the bug this would otherwise hide.
238    pub fn server_mut(&mut self) -> &mut Server {
239        Arc::get_mut(&mut self.server)
240            .expect("the server is set up before the threads that share it are started")
241    }
242
243    /// Where the replies went.
244    #[must_use]
245    pub const fn sink(&self) -> &S {
246        self.front.sink()
247    }
248
249    /// The same, mutably.
250    pub const fn sink_mut(&mut self) -> &mut S {
251        self.front.sink_mut()
252    }
253
254    /// Change the protocol limits, which is `proto-max-bulk-len` and friends.
255    pub fn set_limits(&mut self, limits: Limits) {
256        self.front.set_limits(limits);
257    }
258
259    /// Open a connection and give back its id.
260    pub fn accept(&mut self) -> ConnId {
261        self.server.counted().opened();
262        let at = self.front.open(self.server.next_client());
263        self.note_buffers();
264        at
265    }
266
267    /// Tell the server what the connection buffers are holding now.
268    ///
269    /// The front cannot reach the server, so it keeps the change and this is
270    /// where it is handed over: at the end of whichever call moved a buffer.
271    fn note_buffers(&mut self) {
272        let delta = self.front.buffer_delta();
273        if delta != 0 {
274            self.server.note_conn_bytes(delta);
275        }
276    }
277
278    /// The peer went away.
279    ///
280    /// Whatever is buffered for it is dropped rather than written, and the slot
281    /// comes back as soon as the commands already framed out of its buffer have
282    /// run, because those commands' arguments still point into it.
283    pub fn hangup(&mut self, conn: ConnId) {
284        if !self.front.live(conn) {
285            return;
286        }
287        self.front.mark_gone(conn);
288        // A parked client holds its own commands, and those commands are what
289        // `pending` counts, so leaving it parked here would leave the slot owed
290        // to a connection that is never going to be answered. They go back to
291        // the queue and run as the no-ops a gone connection's commands are.
292        if self.front.blocked(conn) {
293            self.front.unpark(conn);
294        }
295        if self.front.pending(conn) == 0 {
296            self.release(conn);
297        }
298        self.note_buffers();
299    }
300
301    /// Answer everybody this thread can answer, and let go of everybody whose
302    /// deadline has passed.
303    ///
304    /// The walk is over the waiter list rather than over the connections, so it
305    /// costs what blocking costs and not what the server costs. Every caller
306    /// checks that somebody is parked before calling, which is the load and the
307    /// branch a server with nobody blocked pays.
308    ///
309    /// Only this thread's waiters, because a reply goes into a buffer this
310    /// thread owns and another thread's waiter is another thread's to answer.
311    /// The list is copied out under the lock and then let go of, so the work of
312    /// answering does not hold up a thread trying to park a client.
313    fn serve_waiters(&mut self) {
314        let now = self.server.now_ms();
315        let mine = self.server.my_slot();
316        self.server.waiters().mine(mine, &mut self.parked);
317        for at in 0..self.parked.len() {
318            let p = self.parked[at];
319            // The slot is reused and the client id is not. `release` forgets
320            // waiters, so this should never fire; it is here because being
321            // wrong about it writes a reply into somebody else's socket rather
322            // than dropping one.
323            if !self.front.answers(p.conn, p.client) {
324                self.server.forget_waiters(p.client);
325                continue;
326            }
327            // The front cannot reach the databases and the server cannot reach
328            // the connections, so the two halves are taken apart here and the
329            // one buffer this waiter needs is handed over.
330            let served = {
331                let Wire { server, front, .. } = self;
332                server.serve_waiter(p.client, now, front.out(p.conn))
333            };
334            if served {
335                self.server.forget_waiters(p.client);
336                self.front.unpark(p.conn);
337                self.front.soil(p.conn);
338            }
339        }
340        self.parked.clear();
341    }
342
343    /// How many connections are open.
344    #[must_use]
345    pub fn clients(&self) -> usize {
346        self.front.clients()
347    }
348
349    /// Commands framed and waiting for the reactor.
350    #[must_use]
351    pub fn ready(&self) -> usize {
352        self.front.ready()
353    }
354
355    /// Connections with a reply that has not gone out yet.
356    ///
357    /// Non zero means a socket was full and what is left is being held for a
358    /// later flush, which a driver waiting on readability needs to know: there
359    /// is work here that no incoming byte will ever wake it up for.
360    #[must_use]
361    pub fn owed(&self) -> usize {
362        self.front.owed()
363    }
364
365    /// Clients of this thread's that are blocked on a key.
366    ///
367    /// The other thing a driver waiting on readability needs to know, and for
368    /// the same reason `owed` is: there is work here that no incoming byte will
369    /// wake it for. A blocked client is answered by a write another thread made
370    /// or by its own deadline passing, and neither of those is a byte arriving
371    /// on this thread's poller, so a driver that reads this keeps its wait short
372    /// while anybody is waiting on it.
373    #[must_use]
374    pub fn waiting(&self) -> usize {
375        self.server.parked_here()
376    }
377
378    /// Whether a client has asked the server to stop.
379    ///
380    /// The driver reads this once a turn, next to the flag a signal sets, and
381    /// leaves its loop when either is set. Asked after the batch rather than
382    /// during it, so the `SHUTDOWN` and everything that shared its batch is
383    /// finished and written out before anything closes.
384    #[must_use]
385    pub fn stopping(&self) -> bool {
386        self.server.stopping()
387    }
388
389    /// Decoders in the pool, which is the high water mark of one batch.
390    #[must_use]
391    pub fn decoders(&self) -> usize {
392        self.front.decoders()
393    }
394
395    /// What every connection's read and reply buffers are holding.
396    #[must_use]
397    pub fn buffer_bytes(&self) -> usize {
398        self.front.buffer_bytes()
399    }
400
401    /// Take bytes off a connection and frame whatever commands they complete.
402    ///
403    /// Anything left over stays in the connection's buffer, half a command
404    /// included, so the caller hands over whatever the socket gave it without
405    /// looking at it.
406    pub fn feed(&mut self, conn: ConnId, bytes: &[u8]) {
407        self.front.feed(conn, bytes);
408        self.note_buffers();
409    }
410
411    /// Hand the slot and its buffers back, and let the server go of the client.
412    fn release(&mut self, conn: ConnId) {
413        let Some(client) = self.front.close(conn) else {
414            return;
415        };
416        self.forget(client);
417    }
418
419    /// The server side of a connection ending.
420    ///
421    /// It happens in the same call the slot was freed in, and before anything
422    /// else can run, because the slot is handed out again by the next accept
423    /// and a waiter still holding this client id would then be a waiter
424    /// pointing at somebody else's connection.
425    fn forget(&mut self, client: u64) {
426        self.server.forget_waiters(client);
427        self.server.counted().closed();
428    }
429
430    /// Move up to `max` framed commands into `into`.
431    ///
432    /// The reactor wants a batch it owns, and the front keeps the buffers, so
433    /// what crosses between them is this: numbers, no borrows.
434    pub fn take_ready(&mut self, into: &mut Vec<Cmd>, max: usize) -> usize {
435        self.front.take_ready(into, max)
436    }
437
438    /// Take a clock reading for the whole batch.
439    ///
440    /// `04` section 5: once per turn, never per command, so every command in a
441    /// batch compares against the same millisecond and two keys written
442    /// together expire together.
443    pub fn tick(&mut self) {
444        self.server.refresh_clock();
445    }
446
447    /// Do one batch's worth of housekeeping.
448    ///
449    /// Today that is one segment of arena compaction at most, which is what
450    /// stops a server that rewrites the same keys from holding every version of
451    /// them. It is separate from [`Wire::tick`] because the clock has to move
452    /// before a batch runs and this does not: it can wait until the replies are
453    /// out, and the driver decides when that is.
454    ///
455    /// Per batch and not per turn of the loop. A turn can carry one command or
456    /// a thousand, so a per turn call means the rate at which garbage is
457    /// collected has nothing to do with the rate at which it is made, and on a
458    /// saturated server the second one wins. That was measured: with this on
459    /// the loop's turn the server settled at seven segments for six segments'
460    /// worth of keys, which is where an unloaded process running the same
461    /// writes settled at six.
462    pub fn maintain(&mut self) -> Option<usize> {
463        // Before the compaction and not after it, because the reading the next
464        // batch judges its limit against should be the one taken after the last
465        // batch's writes rather than the one taken after this call's collecting.
466        // Both are true, and the first is the one that is a batch old at worst.
467        // Nothing at all on a server with no `maxmemory`, which is the default.
468        self.server.refresh_memory();
469        // Two fields and a return on a server that has never taken a backup,
470        // which is nearly all of them. It is here rather than on a timer for the
471        // same reason the compaction is: one loop turns everything.
472        self.server.backup_expire();
473        self.server.compact_step()
474    }
475}
476
477impl<S: Sink> Engine for Wire<S> {
478    type Work = Cmd;
479
480    fn key_hash(&self, cmd: &Cmd) -> Option<u64> {
481        // Before the argument list is built, because most of the commands that
482        // get this far and answer `None` answer it on the spec alone, and
483        // building an `Args` to then throw it away is the sort of thing that
484        // does not show up in a profile and does show up in a total.
485        let spec = table::at(cmd.spec)?;
486        if spec.first_key <= 0 {
487            return None;
488        }
489        let args = self.front.args(cmd);
490        // The first key only. A command with more than one, which is `MSET` and
491        // `MGET`, warms the first and takes the miss on the rest; warming all of
492        // them means a hash list per command and that is the batch's own job
493        // once multi key commands are worth measuring.
494        let key = args.opt(spec.first_key as usize)?;
495        Some(Keyspace::hash_of(key))
496    }
497
498    fn prefetch(&self, cmd: &Cmd, hash: u64) {
499        let db = self.front.db(cmd.conn());
500        // The hash picks the stripe as well as the record, so this warms the
501        // line the command is going to read and not a line on some other
502        // stripe. It is the same hash the command itself will route on, which
503        // is why the stripe is worked out from a hash rather than from a key.
504        self.server.striped_ref(db).prefetch_hashed(hash);
505    }
506
507    fn run(&mut self, cmd: Cmd, _hash: Option<u64>) -> yo_reactor::Flow {
508        let conn = cmd.conn();
509        // Framed with the batch that blocked, so it is a command the client sent
510        // before it knew it would be waiting. It keeps its decoder and it keeps
511        // its place in `pending`, which is what stops the buffer it points into
512        // being compacted while it waits.
513        if self.front.blocked(conn) {
514            self.front.park(conn, cmd);
515            return yo_reactor::Flow::Next;
516        }
517
518        // The one place both halves are held at once. The front hands over the
519        // arguments, the session and the reply buffer, the server hands over
520        // the databases, and the command layer sees the two as one call.
521        let flow = if self.front.start(&cmd) {
522            let Wire { front, server, .. } = self;
523            let (args, session, out) = front.parts(&cmd);
524            let spec = table::at(cmd.spec);
525            dispatch::resolved(server, session, spec, args, out)
526        } else {
527            // Nobody to answer, or nobody who should be. The decoder still has
528            // to come back and the slot still has to be released, which is why
529            // this is not an early return.
530            Flow::Continue
531        };
532
533        self.front.done(&cmd);
534        if self.front.gone(conn) {
535            if self.front.pending(conn) == 0 {
536                self.release(conn);
537            }
538        } else {
539            match flow {
540                Flow::Close => {
541                    self.front.quit(conn);
542                    self.front.soil(conn);
543                }
544                // Nothing was written, so there is nothing to flush and no
545                // reason to put this connection on the dirty list. The waiter
546                // carries the slot from here on, and it needs to know which one:
547                // the command layer only ever saw the client id.
548                Flow::Block => {
549                    self.front.block(conn);
550                    let client = self.front.client(conn);
551                    self.server.bind_waiter(client, conn);
552                }
553                Flow::Continue => self.front.soil(conn),
554            }
555        }
556
557        // After each command and not once per batch. A client blocked on two
558        // keys and woken by `RPUSH b` then `RPUSH a` in one pipeline has to
559        // answer with `b`, because that is the push that was in front of it, and
560        // it can only do that if it was served in between the two.
561        if self.server.parked_here() != 0 {
562            self.serve_waiters();
563        }
564        yo_reactor::Flow::Next
565    }
566
567    fn flush(&mut self) {
568        // The deadline sweep, and it is here because this is the one thing the
569        // driver calls on a turn that ran nothing at all. A client whose timeout
570        // passes while the server is idle is answered within the loop's idle
571        // wait, which the loop shortens to a millisecond on a thread that has
572        // somebody waiting. That is finer than the 10hz Redis checks its own
573        // blocked clients at.
574        //
575        // This thread's count and not the server's, because the sweep can only
576        // answer this thread's waiters, so on any other thread it is a lock
577        // taken to find nothing.
578        if self.server.parked_here() != 0 {
579            self.server.refresh_clock();
580            self.serve_waiters();
581        }
582
583        // Taken and put back so the loop below can reach the rest of the
584        // engine. The capacity comes back with it, so this is not an
585        // allocation.
586        let mut dirty = self.front.take_dirty();
587        let mut at = 0;
588        while at < dirty.len() {
589            let conn = dirty[at];
590            match self.front.write_out(conn) {
591                // The socket was full. The connection stays on the list with
592                // what is left of its reply, and the next flush offers it
593                // again, which is the whole of the backpressure story here.
594                Wrote::Owed => at += 1,
595                Wrote::Done => {
596                    dirty.swap_remove(at);
597                }
598                Wrote::Ended(client) => {
599                    self.forget(client);
600                    dirty.swap_remove(at);
601                }
602            }
603        }
604        self.front.give_dirty(dirty);
605        self.note_buffers();
606    }
607
608    fn maintain(&mut self, budget: &mut yo_reactor::Budget) {
609        // The clock is the first thing the maintenance slice does, because
610        // everything else in it compares against a time.
611        if !budget.spend(1) {
612            return;
613        }
614        self.tick();
615        // Then the dead keys, which is what stops a cache that writes with a
616        // deadline and never reads back from holding every key it has ever
617        // written. One unit a key looked at, so the slice bounds the sweep the
618        // same way it bounds everything else in here, and a server where nothing
619        // has a deadline spends nothing at all.
620        let looks = budget.left() as usize;
621        let spent = self.server.expire_slice(looks);
622        budget.spend(u32::try_from(spent).unwrap_or(u32::MAX));
623    }
624}
625
626/// Run everything that is framed, in batches, and write the replies.
627///
628/// The inline driver: it is what a caller who is already on the shard thread
629/// uses in place of the loop, and it goes through the same two walks the loop
630/// goes through (`15` section 7). `batch` is the caller's, so a driver in a hot
631/// loop hands the same `Vec` back every time and never allocates.
632pub fn pump<S: Sink>(reactor: &mut Reactor<Wire<S>>, batch: &mut Vec<Cmd>) -> usize {
633    let mut ran = 0;
634    reactor.engine_mut().tick();
635    loop {
636        batch.clear();
637        if reactor.engine_mut().take_ready(batch, BATCH_MAX) == 0 {
638            break;
639        }
640        // The command path, and therefore the thing Y7 is about. The guard is
641        // what arms `yo-alloc`, and it covers dispatch and nothing else: framing
642        // before it and writing the replies after it are both allowed to reach
643        // for the heap, and only running the commands is not.
644        //
645        // It goes here rather than around the whole loop because `take_ready`
646        // and `flush` are on the other side of that line, and because a batch is
647        // the unit a caller can reason about. Under the default mode this is one
648        // relaxed load.
649        let armed = yo_alloc::guard();
650        ran += reactor.execute_all(batch.drain(..));
651        drop(armed);
652        reactor.engine_mut().flush();
653        // After the replies are out, so the batch that made the garbage is not
654        // the batch that waits for it to be collected.
655        reactor.engine_mut().maintain();
656    }
657    // Once more, for a connection with something to say and nothing to run: a
658    // protocol error, or a socket that was full the last time round.
659    reactor.engine_mut().flush();
660    // And once for a turn that ran nothing at all, which is where a server that
661    // has gone quiet catches up on what the last busy turn left behind.
662    reactor.engine_mut().maintain();
663    ran
664}
665
666#[cfg(test)]
667mod tests {
668    use super::*;
669
670    /// The wire bytes for a command, built the way a client would.
671    fn wire(args: &[&[u8]]) -> Vec<u8> {
672        let mut b = format!("*{}\r\n", args.len()).into_bytes();
673        for a in args {
674            b.extend_from_slice(format!("${}\r\n", a.len()).as_bytes());
675            b.extend_from_slice(a);
676            b.extend_from_slice(b"\r\n");
677        }
678        b
679    }
680
681    fn engine() -> (Reactor<Wire<Recorder>>, ConnId, Vec<Cmd>) {
682        let mut r = Reactor::inline(Wire::new(Recorder::new()));
683        let conn = r.engine_mut().accept();
684        (r, conn, Vec::new())
685    }
686
687    /// Where the fixed clock a blocking test moves by hand starts.
688    const START_MS: u64 = 1_000_000;
689
690    /// The same, on a clock the test moves rather than the system's.
691    ///
692    /// A test about a timeout cannot wait for one: waiting a hundred
693    /// milliseconds is a test that fails on a loaded machine and waiting a
694    /// hundred seconds is not a test.
695    fn timed() -> (Reactor<Wire<Recorder>>, ConnId, Vec<Cmd>) {
696        let server = crate::dispatch::Server::with_clock(yo_kv::Clock::fixed(START_MS));
697        let mut r = Reactor::inline(Wire::with_server(server, Recorder::new()));
698        let conn = r.engine_mut().accept();
699        (r, conn, Vec::new())
700    }
701
702    #[test]
703    fn a_pipelined_batch_comes_back_in_order_and_in_one_write() {
704        let (mut r, conn, mut batch) = engine();
705        let mut stream = wire(&[b"SET", b"k", b"v"]);
706        stream.extend(wire(&[b"GET", b"k"]));
707        stream.extend(wire(&[b"INCR", b"n"]));
708
709        r.engine_mut().feed(conn, &stream);
710        assert_eq!(r.engine().ready(), 3);
711        assert_eq!(pump(&mut r, &mut batch), 3);
712
713        assert_eq!(r.engine().sink().sent(conn), b"+OK\r\n$1\r\nv\r\n:1\r\n");
714        assert_eq!(r.engine().ready(), 0);
715    }
716
717    /// The framing has to survive a command arriving in pieces, because that is
718    /// what a socket does.
719    #[test]
720    fn a_command_split_across_reads_resumes_rather_than_restarts() {
721        let (mut r, conn, mut batch) = engine();
722        let bytes = wire(&[b"SET", b"key", b"value"]);
723
724        for at in 1..bytes.len() {
725            r.engine_mut().feed(conn, &bytes[at - 1..at]);
726            assert_eq!(r.engine().ready(), 0, "not a command yet at {at}");
727        }
728        r.engine_mut().feed(conn, &bytes[bytes.len() - 1..]);
729        assert_eq!(r.engine().ready(), 1);
730        assert_eq!(pump(&mut r, &mut batch), 1);
731        assert_eq!(r.engine().sink().sent(conn), b"+OK\r\n");
732
733        // And the value that arrived in single bytes is the value that was
734        // stored, which is the part a naive resume gets wrong.
735        r.engine_mut().feed(conn, &wire(&[b"GET", b"key"]));
736        pump(&mut r, &mut batch);
737        assert_eq!(r.engine().sink().sent(conn), b"+OK\r\n$5\r\nvalue\r\n");
738    }
739
740    #[test]
741    fn two_connections_are_two_sessions_over_one_server() {
742        let (mut r, a, mut batch) = engine();
743        let b = r.engine_mut().accept();
744
745        r.engine_mut().feed(a, &wire(&[b"SELECT", b"3"]));
746        r.engine_mut().feed(a, &wire(&[b"SET", b"k", b"a"]));
747        r.engine_mut().feed(b, &wire(&[b"SET", b"k", b"b"]));
748        r.engine_mut().feed(a, &wire(&[b"GET", b"k"]));
749        r.engine_mut().feed(b, &wire(&[b"GET", b"k"]));
750        pump(&mut r, &mut batch);
751
752        assert_eq!(r.engine().sink().sent(a), b"+OK\r\n+OK\r\n$1\r\na\r\n");
753        assert_eq!(r.engine().sink().sent(b), b"+OK\r\n$1\r\nb\r\n");
754        assert_eq!(r.engine().clients(), 2);
755    }
756
757    /// The point of the whole exercise: two engines, two threads, one server.
758    ///
759    /// The server is told it will have two threads before either starts, the
760    /// way `yodb serve` tells it. Without that it has one set of counters and
761    /// both threads land on it, which is the wrap round `Server::mine_at`
762    /// documents and which loses counts: a bump is a load and a store rather
763    /// than a fetch and add, because the fast path is one thread writing its
764    /// own set and paying for a locked instruction on every command to make a
765    /// shared set exact would be paying it on the path that is never shared.
766    /// Miri found this by running the two threads far enough apart to lose one,
767    /// which a real machine does rarely enough to have passed here for months.
768    #[test]
769    fn two_threads_write_into_one_server() {
770        const EACH: usize = 200;
771
772        let mut server = Server::new();
773        server.set_threads(2);
774        let first = Wire::with_server(server, Recorder::new());
775        let second = Wire::over(first.shared(), Recorder::new());
776        let server = first.shared();
777
778        std::thread::scope(|s| {
779            for (at, engine) in [first, second].into_iter().enumerate() {
780                s.spawn(move || {
781                    let mut r = Reactor::inline(engine);
782                    let mut batch = Vec::new();
783                    let conn = r.engine_mut().accept();
784                    for i in 0..EACH {
785                        let key = format!("t{at}:{i}");
786                        r.engine_mut()
787                            .feed(conn, &wire(&[b"SET", key.as_bytes(), b"v"]));
788                        pump(&mut r, &mut batch);
789                    }
790                });
791            }
792        });
793
794        // Every key both threads wrote is in the one database, which is the
795        // whole claim: the fronts were separate and the keyspace was not.
796        assert_eq!(server.striped_ref(0).len(), 2 * EACH);
797        // And both threads counted into the same total, each from its own set
798        // of counters, which is what the sum over the threads is for.
799        assert_eq!(server.totals().connections, 2);
800    }
801
802    /// A blocked client is answered into a buffer one thread owns, so it is
803    /// that thread's to answer and nobody else's to throw away.
804    #[test]
805    fn a_waiter_belongs_to_the_thread_that_parked_it() {
806        let mut server = Server::new();
807        server.set_threads(2);
808        let first = Wire::with_server(server, Recorder::new());
809        let second = Wire::over(first.shared(), Recorder::new());
810        let server = first.shared();
811
812        let parked = std::sync::Barrier::new(2);
813        let swept = std::sync::Barrier::new(2);
814
815        std::thread::scope(|s| {
816            let (parked, swept) = (&parked, &swept);
817            s.spawn(move || {
818                let mut r = Reactor::inline(first);
819                let mut batch = Vec::new();
820                let conn = r.engine_mut().accept();
821                r.engine_mut().feed(conn, &wire(&[b"BLPOP", b"a", b"0"]));
822                pump(&mut r, &mut batch);
823                parked.wait();
824
825                // Turns with nothing on them, each of which walks a list whose
826                // one other entry belongs to the thread next door.
827                for _ in 0..50 {
828                    pump(&mut r, &mut batch);
829                }
830                swept.wait();
831                assert!(r.engine().sink().sent(conn).is_empty(), "nothing to say");
832            });
833            s.spawn(move || {
834                let mut r = Reactor::inline(second);
835                let mut batch = Vec::new();
836                let conn = r.engine_mut().accept();
837                r.engine_mut().feed(conn, &wire(&[b"BLPOP", b"b", b"0"]));
838                pump(&mut r, &mut batch);
839                parked.wait();
840                swept.wait();
841
842                // The push comes in on a second connection, because the first
843                // one is not reading anything while it waits.
844                let pusher = r.engine_mut().accept();
845                r.engine_mut().feed(pusher, &wire(&[b"RPUSH", b"b", b"v"]));
846                pump(&mut r, &mut batch);
847                assert_eq!(
848                    r.engine().sink().sent(conn),
849                    b"*2\r\n$1\r\nb\r\n$1\r\nv\r\n",
850                    "served by the thread that parked it"
851                );
852            });
853        });
854
855        assert_eq!(server.parked(), 1, "and the other one is still waiting");
856    }
857
858    /// The count a thread branches on before it reaches for the shared list is
859    /// its own, because the list is one lock and a thread can only answer what
860    /// it parked itself. Branching on the server wide count instead would put
861    /// every thread through that lock after every command as soon as one client
862    /// blocked anywhere.
863    #[test]
864    fn a_thread_counts_the_clients_it_blocked_and_nobody_else_s() {
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 looked = std::sync::Barrier::new(2);
873
874        std::thread::scope(|s| {
875            let (parked, looked) = (&parked, &looked);
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                assert_eq!(r.engine().waiting(), 1, "the one this thread blocked");
883                parked.wait();
884                looked.wait();
885
886                // A second client of this thread's that never blocked, opened
887                // and closed. It is not on the list, so the count stays where
888                // it was rather than following the disconnect down.
889                let other = r.engine_mut().accept();
890                r.engine_mut().feed(other, &wire(&[b"PING"]));
891                pump(&mut r, &mut batch);
892                r.engine_mut().hangup(other);
893                pump(&mut r, &mut batch);
894                assert_eq!(r.engine().waiting(), 1, "still just the blocked one");
895            });
896            s.spawn(move || {
897                let mut r = Reactor::inline(second);
898                let mut batch = Vec::new();
899                parked.wait();
900
901                // A thread with nothing of its own blocked, on a server that
902                // has one client blocked on it.
903                pump(&mut r, &mut batch);
904                assert_eq!(r.engine().waiting(), 0, "none of them are this one's");
905                assert_eq!(r.engine().server().parked(), 1, "one on the server");
906                looked.wait();
907            });
908        });
909
910        assert_eq!(server.parked(), 1);
911    }
912
913    /// Two fronts hand out connection slots from zero, so the number that tells
914    /// two clients apart cannot come from a front.
915    #[test]
916    fn client_ids_are_the_server_s_to_hand_out() {
917        let first = Wire::new(Recorder::new());
918        let second = Wire::over(first.shared(), Recorder::new());
919        let mut a = Reactor::inline(first);
920        let mut b = Reactor::inline(second);
921
922        let (one, two) = (a.engine_mut().accept(), b.engine_mut().accept());
923        assert_eq!(one, two, "the same slot on each front");
924
925        // HELLO answers with the connection id, which is the number CLIENT
926        // KILL and CLIENT UNPAUSE take, so two fronts agreeing on it is two
927        // clients that cannot be told apart. Protocol three so that the proto
928        // field in the same reply is not one of the ids being looked for.
929        let mut batch = Vec::new();
930        a.engine_mut().feed(one, &wire(&[b"HELLO", b"3"]));
931        b.engine_mut().feed(two, &wire(&[b"HELLO", b"3"]));
932        pump(&mut a, &mut batch);
933        pump(&mut b, &mut batch);
934
935        let first = String::from_utf8_lossy(a.engine().sink().sent(one)).into_owned();
936        let second = String::from_utf8_lossy(b.engine().sink().sent(two)).into_owned();
937        assert!(first.contains(":1\r\n"), "{first}");
938        assert!(second.contains(":2\r\n"), "{second}");
939    }
940
941    #[test]
942    fn quit_is_answered_and_then_the_connection_goes() {
943        let (mut r, conn, mut batch) = engine();
944        r.engine_mut().feed(conn, &wire(&[b"PING"]));
945        r.engine_mut().feed(conn, &wire(&[b"QUIT"]));
946        pump(&mut r, &mut batch);
947
948        assert_eq!(r.engine().sink().sent(conn), b"+PONG\r\n+OK\r\n");
949        assert!(r.engine().sink().was_closed(conn));
950        assert_eq!(r.engine().clients(), 0);
951
952        // The slot comes back, buffers and all.
953        let again = r.engine_mut().accept();
954        assert_eq!(again, conn);
955        assert_eq!(r.engine().clients(), 1);
956    }
957
958    /// Redis's own unit/quit, which caught this: we answered the `QUIT` and
959    /// then ran the `SET` behind it.
960    #[test]
961    fn what_a_client_pipelined_behind_quit_is_never_run() {
962        let (mut r, conn, mut batch) = engine();
963        let mut stream = wire(&[b"QUIT"]);
964        stream.extend(wire(&[b"SET", b"foo", b"bar"]));
965        r.engine_mut().feed(conn, &stream);
966        // Both were framed, because framing happens before anything runs.
967        assert_eq!(r.engine().ready(), 2);
968        pump(&mut r, &mut batch);
969
970        // One reply and not two, and the connection is gone.
971        assert_eq!(r.engine().sink().sent(conn), b"+OK\r\n");
972        assert!(r.engine().sink().was_closed(conn));
973
974        // And the write never happened, which is the part a client can see
975        // after it reconnects. The recorder is cleared first because the next
976        // connection lands back in the slot this one just left, and what was
977        // written to the slot before is still sitting in it.
978        r.engine_mut().sink_mut().clear();
979        let next = r.engine_mut().accept();
980        r.engine_mut().feed(next, &wire(&[b"GET", b"foo"]));
981        pump(&mut r, &mut batch);
982        assert_eq!(r.engine().sink().sent(next), b"$-1\r\n");
983    }
984
985    /// A connection that never said `HELLO` is answered in RESP2, whatever the
986    /// last client in that slot was speaking.
987    ///
988    /// The protocol is kept in the reply buffer and the reply buffer outlives
989    /// the connection, so this is the one piece of connection state that a
990    /// recycled slot used to carry over. A client got a RESP3 null back from
991    /// the first `GET` that missed and could not parse it, which is as bad as a
992    /// compatibility bug gets: nothing the client did caused it and nothing it
993    /// could send would have avoided it.
994    #[test]
995    fn a_slot_that_last_spoke_resp3_answers_the_next_client_in_resp2() {
996        let (mut r, conn, mut batch) = engine();
997        r.engine_mut().feed(conn, &wire(&[b"HELLO", b"3"]));
998        r.engine_mut().feed(conn, &wire(&[b"GET", b"nothing"]));
999        pump(&mut r, &mut batch);
1000        assert!(r.engine().sink().sent(conn).ends_with(b"_\r\n"));
1001        r.engine_mut().feed(conn, &wire(&[b"QUIT"]));
1002        pump(&mut r, &mut batch);
1003
1004        r.engine_mut().sink_mut().clear();
1005        let next = r.engine_mut().accept();
1006        assert_eq!(next, conn, "the same slot, which is what this is about");
1007        r.engine_mut().feed(next, &wire(&[b"GET", b"nothing"]));
1008        pump(&mut r, &mut batch);
1009        assert_eq!(r.engine().sink().sent(next), b"$-1\r\n");
1010    }
1011
1012    /// The other way a connection ends, which does not throw anything away.
1013    #[test]
1014    fn commands_that_arrived_before_a_protocol_error_are_still_answered() {
1015        let (mut r, conn, mut batch) = engine();
1016        let mut stream = wire(&[b"SET", b"k", b"v"]);
1017        stream.extend(wire(&[b"GET", b"k"]));
1018        stream.extend_from_slice(b"*1\r\n+notabulk\r\n");
1019        r.engine_mut().feed(conn, &stream);
1020        pump(&mut r, &mut batch);
1021
1022        // Both good commands were complete and correct before the stream went
1023        // wrong, so both are answered and the error comes after them.
1024        let sent = r.engine().sink().sent(conn);
1025        assert!(
1026            sent.starts_with(b"+OK\r\n$1\r\nv\r\n-ERR Protocol error: "),
1027            "{sent:?}"
1028        );
1029        assert!(r.engine().sink().was_closed(conn));
1030    }
1031
1032    #[test]
1033    fn a_protocol_error_is_written_and_closes_the_connection() {
1034        let (mut r, conn, mut batch) = engine();
1035        // A multibulk that says its first argument is a bulk and then does not.
1036        r.engine_mut().feed(conn, b"*1\r\n+notabulk\r\n");
1037        pump(&mut r, &mut batch);
1038
1039        let sent = r.engine().sink().sent(conn);
1040        assert!(sent.starts_with(b"-ERR Protocol error: "), "{sent:?}");
1041        assert!(r.engine().sink().was_closed(conn));
1042        assert_eq!(r.engine().clients(), 0);
1043    }
1044
1045    /// Redis's own `unit/protocol` walks a list of malformed frames, each on a
1046    /// fresh connection, which means every one of them after the first runs on
1047    /// a decoder that came back to the pool part way through a command.
1048    #[test]
1049    fn a_decoder_that_came_back_mid_command_starts_the_next_one_clean() {
1050        let (mut r, conn, mut batch) = engine();
1051        // Stops inside the third argument, on a length that is not a length.
1052        r.engine_mut()
1053            .feed(conn, b"*3\r\n$3\r\nSET\r\n$1\r\nx\r\n$blabla\r\n");
1054        pump(&mut r, &mut batch);
1055        let sent = r.engine().sink().sent(conn);
1056        assert!(
1057            sent.starts_with(b"-ERR Protocol error: invalid bulk length"),
1058            "{sent:?}"
1059        );
1060
1061        // The slot that decoder was in is now the slot the next connection
1062        // gets, and it has to be at the start of a command and not half way
1063        // through the one that went wrong.
1064        r.engine_mut().sink_mut().clear();
1065        let next = r.engine_mut().accept();
1066        r.engine_mut().feed(next, &wire(&[b"GET", b"k"]));
1067        pump(&mut r, &mut batch);
1068        assert_eq!(r.engine().sink().sent(next), b"$-1\r\n");
1069
1070        r.engine_mut().sink_mut().clear();
1071        let third = r.engine_mut().accept();
1072        r.engine_mut().feed(third, b"*1\r\n+notabulk\r\n");
1073        pump(&mut r, &mut batch);
1074        let sent = r.engine().sink().sent(third);
1075        assert!(sent.starts_with(b"-ERR Protocol error: "), "{sent:?}");
1076    }
1077
1078    /// A client that hangs up mid batch is the case that gets a server killed:
1079    /// the commands already framed still point into its buffer.
1080    #[test]
1081    fn a_hangup_with_commands_in_flight_waits_for_them() {
1082        let (mut r, conn, mut batch) = engine();
1083        r.engine_mut().feed(conn, &wire(&[b"SET", b"k", b"v"]));
1084        r.engine_mut().feed(conn, &wire(&[b"GET", b"k"]));
1085
1086        batch.clear();
1087        r.engine_mut().take_ready(&mut batch, BATCH_MAX);
1088        r.engine_mut().hangup(conn);
1089        assert_eq!(r.engine().clients(), 1, "still holding the buffer");
1090
1091        r.execute_all(batch.drain(..));
1092        r.engine_mut().flush();
1093        assert_eq!(r.engine().clients(), 0);
1094        assert!(r.engine().sink().sent(conn).is_empty(), "nobody to answer");
1095
1096        // And the slot is usable again, with the decoders both back in the
1097        // pool rather than lost with the connection.
1098        let decoders = r.engine().decoders();
1099        let again = r.engine_mut().accept();
1100        assert_eq!(again, conn);
1101        r.engine_mut().feed(again, &wire(&[b"PING"]));
1102        pump(&mut r, &mut batch);
1103        assert_eq!(r.engine().sink().sent(again), b"+PONG\r\n");
1104        assert_eq!(r.engine().decoders(), decoders);
1105    }
1106
1107    /// The claim that the steady state does not allocate, checked the only way
1108    /// a library test can check it: nothing grows.
1109    #[test]
1110    fn the_buffers_and_the_decoder_pool_stop_growing() {
1111        let (mut r, conn, mut batch) = engine();
1112        let mut stream = Vec::new();
1113        for i in 0..32 {
1114            stream.extend(wire(&[b"SET", format!("k{i}").as_bytes(), b"v"]));
1115        }
1116
1117        r.engine_mut().feed(conn, &stream);
1118        pump(&mut r, &mut batch);
1119        let decoders = r.engine().decoders();
1120        let batch_cap = batch.capacity();
1121
1122        for _ in 0..10 {
1123            r.engine_mut().feed(conn, &stream);
1124            pump(&mut r, &mut batch);
1125        }
1126        assert_eq!(r.engine().decoders(), decoders, "the pool is reused");
1127        assert_eq!(batch.capacity(), batch_cap, "the batch buffer is reused");
1128        assert!(
1129            decoders <= BATCH_MAX + 1,
1130            "{decoders} decoders for 32 commands"
1131        );
1132    }
1133
1134    /// The read buffer holds what has not been dealt with yet and nothing else.
1135    ///
1136    /// A client that pipelines sixteen commands, waits for the sixteen replies
1137    /// and goes again is what `redis-benchmark -P 16` does and what half of the
1138    /// clients in the world do. Every one of those rounds leaves the buffer
1139    /// exactly caught up, and a buffer that never drops what it has already
1140    /// dealt with grows to everything the connection has ever sent: 16 MiB
1141    /// apiece on server3 for four connections sending 100000 sets each.
1142    #[test]
1143    fn a_pipelining_client_does_not_grow_the_read_buffer() {
1144        let (mut r, conn, mut batch) = engine();
1145        let mut round = Vec::new();
1146        for i in 0..16 {
1147            round.extend(wire(&[b"SET", format!("k{i}").as_bytes(), b"v"]));
1148        }
1149
1150        r.engine_mut().feed(conn, &round);
1151        pump(&mut r, &mut batch);
1152        r.engine_mut().sink_mut().clear();
1153        let after_one = r.engine().buffer_bytes();
1154
1155        // A thousand rounds is sixteen thousand commands and about a megabyte
1156        // of wire bytes, which is a hundred times what the buffer starts with.
1157        // Fifty is a twentieth of that and it is what runs under Miri, where
1158        // sixteen thousand commands through the whole engine was a quarter of
1159        // an hour. The check below is that the size is the one it was after the
1160        // first round, exactly, so a buffer that keeps anything at all is
1161        // caught on the second round and every one after it, whichever count
1162        // this is.
1163        let rounds = if cfg!(miri) { 50 } else { 1000 };
1164        for _ in 0..rounds {
1165            r.engine_mut().feed(conn, &round);
1166            pump(&mut r, &mut batch);
1167            r.engine_mut().sink_mut().clear();
1168        }
1169
1170        assert_eq!(
1171            r.engine().buffer_bytes(),
1172            after_one,
1173            "the buffers grew over {rounds} rounds of the same sixteen commands"
1174        );
1175        assert!(
1176            r.engine().server().memory_bytes() >= after_one,
1177            "the buffers are counted in what the server reports"
1178        );
1179    }
1180
1181    /// Half a command in the buffer is the case compaction has to be careful
1182    /// about, because the decoder holding it kept offsets into those bytes.
1183    #[test]
1184    fn a_command_split_across_reads_survives_compaction() {
1185        let (mut r, conn, mut batch) = engine();
1186        let cmd = wire(&[b"SET", b"key", b"value"]);
1187        let (head, tail) = cmd.split_at(cmd.len() - 4);
1188
1189        // A complete command, so that there is something in front to drop, then
1190        // most of a second one.
1191        r.engine_mut().feed(conn, &wire(&[b"PING"]));
1192        r.engine_mut().feed(conn, head);
1193        pump(&mut r, &mut batch);
1194        assert_eq!(r.engine().sink().sent(conn), b"+PONG\r\n");
1195
1196        // The rest of it arrives after the buffer has been compacted under it.
1197        r.engine_mut().feed(conn, tail);
1198        pump(&mut r, &mut batch);
1199        assert_eq!(r.engine().sink().sent(conn), b"+PONG\r\n+OK\r\n");
1200
1201        r.engine_mut().feed(conn, &wire(&[b"GET", b"key"]));
1202        pump(&mut r, &mut batch);
1203        assert!(r.engine().sink().sent(conn).ends_with(b"$5\r\nvalue\r\n"));
1204    }
1205
1206    /// The two walks are the reactor's, not this module's, so the test is that
1207    /// the engine can be driven by them at all: same commands, same replies.
1208    #[test]
1209    fn the_batch_goes_through_the_reactors_two_walks() {
1210        let (mut r, conn, mut batch) = engine();
1211        for i in 0..100 {
1212            r.engine_mut()
1213                .feed(conn, &wire(&[b"INCR", format!("k{}", i % 7).as_bytes()]));
1214        }
1215        let ran = pump(&mut r, &mut batch);
1216
1217        assert_eq!(ran, 100);
1218        assert_eq!(r.commands(), 100);
1219        // Two batches, because a hundred commands do not fit in sixty four.
1220        assert_eq!(r.turns(), 2);
1221        // The hundredth command is the fifteenth `INCR` of `k1`.
1222        assert!(r.engine().sink().sent(conn).ends_with(b":15\r\n"));
1223    }
1224
1225    /// A sink that takes four bytes at a time, which is what a full socket
1226    /// looks like from in here.
1227    #[derive(Default)]
1228    struct Trickle {
1229        sent: Vec<u8>,
1230        writes: usize,
1231    }
1232
1233    impl Sink for Trickle {
1234        fn write(&mut self, _conn: ConnId, bytes: &[u8]) -> usize {
1235            self.writes += 1;
1236            let n = bytes.len().min(4);
1237            self.sent.extend_from_slice(&bytes[..n]);
1238            n
1239        }
1240    }
1241
1242    /// A blocking command that does not block costs nothing: no waiter, no
1243    /// allocation, the same three lines the non blocking one runs.
1244    #[test]
1245    fn a_blpop_on_a_list_with_something_in_it_never_waits() {
1246        let (mut r, conn, mut batch) = engine();
1247        r.engine_mut().feed(conn, &wire(&[b"RPUSH", b"q", b"a"]));
1248        r.engine_mut().feed(conn, &wire(&[b"BLPOP", b"q", b"0"]));
1249        pump(&mut r, &mut batch);
1250
1251        assert_eq!(
1252            r.engine().sink().sent(conn),
1253            b":1\r\n*2\r\n$1\r\nq\r\n$1\r\na\r\n"
1254        );
1255        assert_eq!(r.engine().server().parked(), 0);
1256    }
1257
1258    /// The whole point: a client with nothing to pop is answered later, by
1259    /// somebody else's command.
1260    #[test]
1261    fn a_parked_client_is_answered_by_another_connections_push() {
1262        let (mut r, a, mut batch) = engine();
1263        let b = r.engine_mut().accept();
1264
1265        r.engine_mut().feed(a, &wire(&[b"BLPOP", b"q", b"0"]));
1266        pump(&mut r, &mut batch);
1267        assert!(r.engine().sink().sent(a).is_empty(), "nothing to say yet");
1268        assert_eq!(r.engine().server().parked(), 1);
1269
1270        r.engine_mut().feed(b, &wire(&[b"RPUSH", b"q", b"one"]));
1271        pump(&mut r, &mut batch);
1272
1273        assert_eq!(r.engine().sink().sent(a), b"*2\r\n$1\r\nq\r\n$3\r\none\r\n");
1274        // The push still reports the length it made, even though the element was
1275        // gone again before the reply was written.
1276        assert_eq!(r.engine().sink().sent(b), b":1\r\n");
1277        assert_eq!(r.engine().server().parked(), 0);
1278    }
1279
1280    /// A push to a key nobody named, and a key of another type on a key
1281    /// somebody did: neither is a wake up, and the client stays parked.
1282    #[test]
1283    fn only_a_list_arriving_under_a_named_key_wakes_a_waiter() {
1284        let (mut r, a, mut batch) = engine();
1285        let b = r.engine_mut().accept();
1286        r.engine_mut().feed(a, &wire(&[b"BLPOP", b"q", b"0"]));
1287        pump(&mut r, &mut batch);
1288
1289        r.engine_mut()
1290            .feed(b, &wire(&[b"RPUSH", b"elsewhere", b"x"]));
1291        r.engine_mut().feed(b, &wire(&[b"SADD", b"q", b"x"]));
1292        pump(&mut r, &mut batch);
1293
1294        assert!(r.engine().sink().sent(a).is_empty());
1295        assert_eq!(r.engine().server().parked(), 1, "still waiting");
1296        // And the set is intact, so the waiter did not take anything out of it
1297        // on its way past.
1298        assert_eq!(r.engine().sink().sent(b), b":1\r\n:1\r\n");
1299    }
1300
1301    /// Two workers on one queue, which is what `BLPOP` is for. They are served
1302    /// in the order they arrived and not in whatever order the list is walked.
1303    #[test]
1304    fn two_parked_clients_are_served_in_the_order_they_arrived() {
1305        let (mut r, a, mut batch) = engine();
1306        let b = r.engine_mut().accept();
1307        let c = r.engine_mut().accept();
1308
1309        r.engine_mut().feed(a, &wire(&[b"BLPOP", b"q", b"0"]));
1310        pump(&mut r, &mut batch);
1311        r.engine_mut().feed(b, &wire(&[b"BLPOP", b"q", b"0"]));
1312        pump(&mut r, &mut batch);
1313        assert_eq!(r.engine().server().parked(), 2);
1314
1315        r.engine_mut()
1316            .feed(c, &wire(&[b"RPUSH", b"q", b"first", b"second"]));
1317        pump(&mut r, &mut batch);
1318
1319        assert_eq!(
1320            r.engine().sink().sent(a),
1321            b"*2\r\n$1\r\nq\r\n$5\r\nfirst\r\n"
1322        );
1323        assert_eq!(
1324            r.engine().sink().sent(b),
1325            b"*2\r\n$1\r\nq\r\n$6\r\nsecond\r\n"
1326        );
1327        assert_eq!(r.engine().server().parked(), 0);
1328    }
1329
1330    /// A client waiting for an answer is not a client that has sent another
1331    /// question, so what it pipelined behind its `BLPOP` waits for the `BLPOP`.
1332    #[test]
1333    fn what_a_client_pipelined_behind_a_block_waits_for_the_block() {
1334        let (mut r, a, mut batch) = engine();
1335        let b = r.engine_mut().accept();
1336
1337        // Framed together, so the `PING` is already on its way to the reactor
1338        // when the `BLPOP` in front of it parks.
1339        let mut stream = wire(&[b"BLPOP", b"q", b"0"]);
1340        stream.extend(wire(&[b"PING"]));
1341        r.engine_mut().feed(a, &stream);
1342        pump(&mut r, &mut batch);
1343        assert!(
1344            r.engine().sink().sent(a).is_empty(),
1345            "the PING went out in front of the answer it was sent behind"
1346        );
1347
1348        // And one that arrives while it is parked is not even framed.
1349        r.engine_mut().feed(a, &wire(&[b"ECHO", b"after"]));
1350        pump(&mut r, &mut batch);
1351        assert!(r.engine().sink().sent(a).is_empty());
1352
1353        r.engine_mut().feed(b, &wire(&[b"RPUSH", b"q", b"x"]));
1354        pump(&mut r, &mut batch);
1355        assert_eq!(
1356            r.engine().sink().sent(a),
1357            b"*2\r\n$1\r\nq\r\n$1\r\nx\r\n+PONG\r\n$5\r\nafter\r\n"
1358        );
1359    }
1360
1361    /// Redis serves parked clients after every command rather than once per
1362    /// turn of the loop, and a pipeline is where the difference shows: the
1363    /// waiter has to be served between the two pushes, so it answers with the
1364    /// key the first push filled and not with the one it named first.
1365    #[test]
1366    fn a_waiter_is_served_between_two_pipelined_pushes() {
1367        let (mut r, a, mut batch) = engine();
1368        let b = r.engine_mut().accept();
1369        r.engine_mut()
1370            .feed(a, &wire(&[b"BLPOP", b"p1", b"p2", b"0"]));
1371        pump(&mut r, &mut batch);
1372
1373        let mut stream = wire(&[b"RPUSH", b"p2", b"second"]);
1374        stream.extend(wire(&[b"RPUSH", b"p1", b"first"]));
1375        r.engine_mut().feed(b, &stream);
1376        pump(&mut r, &mut batch);
1377
1378        assert_eq!(
1379            r.engine().sink().sent(a),
1380            b"*2\r\n$2\r\np2\r\n$6\r\nsecond\r\n"
1381        );
1382        // Which leaves the key it named first holding what was pushed to it.
1383        r.engine_mut()
1384            .feed(b, &wire(&[b"LRANGE", b"p1", b"0", b"-1"]));
1385        pump(&mut r, &mut batch);
1386        assert!(
1387            r.engine()
1388                .sink()
1389                .sent(b)
1390                .ends_with(b"*1\r\n$5\r\nfirst\r\n")
1391        );
1392    }
1393
1394    /// A `BLMOVE` that serves itself is a push, so it wakes the client waiting
1395    /// on the key it pushed to, in the same moment and without a turn of the
1396    /// loop in between.
1397    #[test]
1398    fn a_waiter_woken_by_another_waiter() {
1399        let (mut r, a, mut batch) = engine();
1400        let b = r.engine_mut().accept();
1401        let c = r.engine_mut().accept();
1402
1403        r.engine_mut()
1404            .feed(a, &wire(&[b"BLMOVE", b"x", b"y", b"LEFT", b"RIGHT", b"0"]));
1405        pump(&mut r, &mut batch);
1406        r.engine_mut().feed(b, &wire(&[b"BLPOP", b"y", b"0"]));
1407        pump(&mut r, &mut batch);
1408        assert_eq!(r.engine().server().parked(), 2);
1409
1410        r.engine_mut().feed(c, &wire(&[b"RPUSH", b"x", b"chain"]));
1411        pump(&mut r, &mut batch);
1412
1413        assert_eq!(r.engine().sink().sent(a), b"$5\r\nchain\r\n");
1414        assert_eq!(
1415            r.engine().sink().sent(b),
1416            b"*2\r\n$1\r\ny\r\n$5\r\nchain\r\n"
1417        );
1418        assert_eq!(r.engine().server().parked(), 0);
1419    }
1420
1421    /// A waiter on one database is not woken by a push on another, even though
1422    /// the key has the same name.
1423    #[test]
1424    fn a_waiter_is_only_woken_on_the_database_it_blocked_on() {
1425        let (mut r, a, mut batch) = engine();
1426        let b = r.engine_mut().accept();
1427        r.engine_mut().feed(a, &wire(&[b"SELECT", b"3"]));
1428        r.engine_mut().feed(a, &wire(&[b"BLPOP", b"q", b"0"]));
1429        pump(&mut r, &mut batch);
1430        assert_eq!(r.engine().sink().sent(a), b"+OK\r\n");
1431
1432        r.engine_mut().feed(b, &wire(&[b"RPUSH", b"q", b"wrongdb"]));
1433        pump(&mut r, &mut batch);
1434        assert_eq!(r.engine().sink().sent(a), b"+OK\r\n", "still waiting");
1435
1436        r.engine_mut().feed(b, &wire(&[b"SELECT", b"3"]));
1437        r.engine_mut().feed(b, &wire(&[b"RPUSH", b"q", b"rightdb"]));
1438        pump(&mut r, &mut batch);
1439        assert!(r.engine().sink().sent(a).ends_with(b"$7\r\nrightdb\r\n"));
1440    }
1441
1442    /// The deadline sweep, which runs on a turn that has nothing else to do.
1443    #[test]
1444    fn a_client_that_waited_long_enough_gets_a_null_array() {
1445        let (mut r, conn, mut batch) = timed();
1446        r.engine_mut().feed(conn, &wire(&[b"BLPOP", b"q", b"30"]));
1447        pump(&mut r, &mut batch);
1448        assert!(r.engine().sink().sent(conn).is_empty());
1449
1450        r.engine_mut().server_mut().set_clock_ms(START_MS + 29_999);
1451        pump(&mut r, &mut batch);
1452        assert!(
1453            r.engine().sink().sent(conn).is_empty(),
1454            "a millisecond short"
1455        );
1456
1457        r.engine_mut().server_mut().set_clock_ms(START_MS + 30_000);
1458        pump(&mut r, &mut batch);
1459        // A null array and not a null string, which a RESP2 client can see.
1460        assert_eq!(r.engine().sink().sent(conn), b"*-1\r\n");
1461        assert_eq!(r.engine().server().parked(), 0);
1462    }
1463
1464    /// The four that answer with something other than a two element array all
1465    /// answer a timeout the same way, which is not what the reply shape would
1466    /// suggest and is what Redis does.
1467    #[test]
1468    fn every_blocking_command_times_out_with_the_same_null_array() {
1469        for cmd in [
1470            &[b"BLPOP".as_slice(), b"q", b"0.001"][..],
1471            &[b"BRPOP", b"q", b"0.001"],
1472            &[b"BLMOVE", b"q", b"d", b"LEFT", b"RIGHT", b"0.001"],
1473            &[b"BRPOPLPUSH", b"q", b"d", b"0.001"],
1474            &[b"BLMPOP", b"0.001", b"1", b"q", b"LEFT"],
1475        ] {
1476            let (mut r, conn, mut batch) = timed();
1477            r.engine_mut().feed(conn, &wire(cmd));
1478            pump(&mut r, &mut batch);
1479            r.engine_mut().server_mut().set_clock_ms(START_MS + 1);
1480            pump(&mut r, &mut batch);
1481            assert_eq!(r.engine().sink().sent(conn), b"*-1\r\n", "for {cmd:?}");
1482        }
1483    }
1484
1485    /// A client that gave up does not go on holding a claim on the queue: the
1486    /// element that arrives after it stays where it was put.
1487    #[test]
1488    fn a_waiter_that_timed_out_does_not_eat_a_later_push() {
1489        let (mut r, a, mut batch) = timed();
1490        let b = r.engine_mut().accept();
1491        r.engine_mut().feed(a, &wire(&[b"BLPOP", b"q", b"1"]));
1492        pump(&mut r, &mut batch);
1493        r.engine_mut().server_mut().set_clock_ms(START_MS + 1000);
1494        pump(&mut r, &mut batch);
1495        assert_eq!(r.engine().sink().sent(a), b"*-1\r\n");
1496
1497        r.engine_mut().feed(b, &wire(&[b"RPUSH", b"q", b"late"]));
1498        r.engine_mut()
1499            .feed(b, &wire(&[b"LRANGE", b"q", b"0", b"-1"]));
1500        pump(&mut r, &mut batch);
1501        assert_eq!(r.engine().sink().sent(a), b"*-1\r\n", "nothing more");
1502        assert!(r.engine().sink().sent(b).ends_with(b"*1\r\n$4\r\nlate\r\n"));
1503    }
1504
1505    /// A `BLPOP key 0` has no deadline, so nothing but the connection closing
1506    /// will ever take it off the list. That makes the close path the one that
1507    /// has to be right, or a waiter outlives its client and the slot it names
1508    /// gets handed to somebody else.
1509    #[test]
1510    fn a_client_that_goes_away_while_it_waits_takes_its_waiter_with_it() {
1511        let (mut r, a, mut batch) = engine();
1512        let b = r.engine_mut().accept();
1513        r.engine_mut().feed(a, &wire(&[b"BLPOP", b"q", b"0"]));
1514        pump(&mut r, &mut batch);
1515        assert_eq!(r.engine().server().parked(), 1);
1516
1517        r.engine_mut().hangup(a);
1518        pump(&mut r, &mut batch);
1519        assert_eq!(r.engine().server().parked(), 0);
1520        assert_eq!(r.engine().clients(), 1);
1521
1522        // The slot is handed straight back out, which is what the waiter would
1523        // have been pointing at.
1524        let again = r.engine_mut().accept();
1525        assert_eq!(again, a);
1526        r.engine_mut().feed(b, &wire(&[b"RPUSH", b"q", b"x"]));
1527        r.engine_mut()
1528            .feed(again, &wire(&[b"LRANGE", b"q", b"0", b"-1"]));
1529        pump(&mut r, &mut batch);
1530        assert_eq!(r.engine().sink().sent(again), b"*1\r\n$1\r\nx\r\n");
1531    }
1532
1533    /// The same, with commands the client had already sent sitting behind the
1534    /// block. Those are what `pending` counts, so a close that forgets them is a
1535    /// connection slot that never comes back.
1536    #[test]
1537    fn a_hangup_while_parked_gives_back_the_slot_and_the_decoders() {
1538        let (mut r, a, mut batch) = engine();
1539        let mut stream = wire(&[b"BLPOP", b"q", b"0"]);
1540        stream.extend(wire(&[b"PING"]));
1541        stream.extend(wire(&[b"PING"]));
1542        r.engine_mut().feed(a, &stream);
1543        pump(&mut r, &mut batch);
1544
1545        let decoders = r.engine().decoders();
1546        r.engine_mut().hangup(a);
1547        pump(&mut r, &mut batch);
1548
1549        assert_eq!(r.engine().clients(), 0);
1550        assert!(r.engine().sink().was_closed(a));
1551        assert_eq!(r.engine().decoders(), decoders, "the pool came back whole");
1552        let again = r.engine_mut().accept();
1553        assert_eq!(again, a);
1554        r.engine_mut().feed(again, &wire(&[b"PING"]));
1555        pump(&mut r, &mut batch);
1556        assert_eq!(r.engine().sink().sent(again), b"+PONG\r\n");
1557    }
1558
1559    #[test]
1560    fn a_reply_the_socket_would_not_take_is_offered_again() {
1561        let mut r = Reactor::inline(Wire::new(Trickle::default()));
1562        let conn = r.engine_mut().accept();
1563        let mut batch = Vec::new();
1564
1565        r.engine_mut().feed(conn, &wire(&[b"PING"]));
1566        pump(&mut r, &mut batch);
1567        // Two flushes in a pump, so four bytes and then three.
1568        assert_eq!(r.engine().sink().sent, b"+PONG\r\n");
1569        assert_eq!(r.engine().sink().writes, 2);
1570    }
1571}