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