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