yo_kv/slab.rs
1//! Somewhere to keep a value that is not bytes, addressed by a small number.
2//!
3//! A string lives in the record: the map hands back the bytes and the bytes are
4//! the value, which is what makes `GET` one lookup and one cache miss. A set
5//! cannot do that. It owns three allocations that grow and shrink as members
6//! come and go, and rewriting the record on every `SADD` to keep them inline
7//! would be a copy of the whole set per member added.
8//!
9//! So the record holds four bytes saying where, and this is what those four
10//! bytes point into:
11//!
12//! ```text
13//! record slab
14//! +------+-------+ +---+---+---+---+---+
15//! | meta | u32 3 | ------> | 0 | 1 | 2 | 3 | 4 |
16//! +------+-------+ +---+---+---+---+---+
17//! kind says free ^ set
18//! which slab |
19//! the number in the record
20//! ```
21//!
22//! That is a second cache miss on the way to a set, and there is no arrangement
23//! that avoids it, because the set is bigger than a record and lives longer than
24//! any one command. What it does avoid is a second *hash* and a second *lookup*:
25//! the number comes out of the record the key lookup already fetched, so the
26//! second miss is a dependent load and not another trip through the index.
27//!
28//! # One slab per type, not one slab of an enum
29//!
30//! There will be a slab of sets, then one of hashes, then lists and sorted sets.
31//! The alternative is one slab holding an enum over all of them, which would be
32//! one field instead of four, and it would make every slot as big as the largest
33//! type and put a discriminant check in front of every access. The type tag in
34//! the meta byte already says which type a key holds, so that check would be the
35//! second time the same question got asked. Four fields it is.
36//!
37//! # Reuse, and what happens if a number outlives its value
38//!
39//! Freed slots go on a free list threaded through the vacancies, so an insert
40//! after a delete costs the same as the first insert and the vector does not
41//! grow forever under a churning workload.
42//!
43//! That means a number can be handed out twice, and a stale number would read
44//! somebody else's value rather than nothing. There is no generation counter to
45//! catch it, and the reason is that the only way to hold a stale number is for a
46//! record to outlive the [`Slab::remove`] that freed it, which is one function
47//! putting the two in the wrong order. A generation would turn that bug into a
48//! `None` at the cost of eight bytes a record for every key of every type, which
49//! is paying forever for a mistake that is caught the first time it is made.
50//! What the slab does promise is that [`Slab::remove`] on a slot that is already
51//! free answers `None` and leaves the free list alone, so a double free is inert
52//! rather than a loop in the list.
53//!
54//! # Knowing what it costs without asking everything
55//!
56//! Adding up what the values hold means asking every one of them, and a server
57//! with a `maxmemory` has to know that number often enough that walking a
58//! million sets to find it is not an option. So the slab can be asked to keep a
59//! running total instead.
60//!
61//! The trick is that a value can only change through [`Slab::get_mut`], which is
62//! also the only way in, so the slab sees every collection that is about to
63//! move before it moves. When tracking is on it takes that value's bytes back
64//! out of the total and writes the slot down. Nothing is added back until
65//! somebody asks for the number, and then only the slots on that list are asked
66//! again. A batch touches a handful of collections, so reading the total costs a
67//! handful of questions rather than one per key.
68//!
69//! Tracking is off until something turns it on, and off it costs one predictable
70//! branch on the way into `get_mut`. A server with no memory limit never needs
71//! the number and never pays for it.
72
73use std::mem;
74
75/// What one value in a slab is holding, so the slab can keep a total of it.
76///
77/// This is the same `memory_bytes` every collection already had, named as a
78/// trait so the slab can ask without knowing which of them it is holding.
79pub trait Bytes {
80 /// Bytes this value holds, not counting the slot it sits in.
81 fn memory_bytes(&self) -> usize;
82}
83
84/// The number that means no slot, which is the end of the free list.
85const NONE: u32 = u32::MAX;
86
87/// The most slots there can be.
88///
89/// One short of the whole u32 range, because the top value is spoken for as the
90/// end of the free list.
91pub const MAX_SLOTS: usize = NONE as usize;
92
93/// A slot is a value or a step along the free list.
94#[derive(Debug)]
95enum Slot<T> {
96 Filled(T),
97 /// The next free slot, or [`NONE`].
98 Free(u32),
99}
100
101/// Values addressed by a small stable number.
102#[derive(Debug)]
103pub struct Slab<T> {
104 slots: Vec<Slot<T>>,
105 /// The first free slot, or [`NONE`] when every slot is filled.
106 free: u32,
107 /// How many slots are filled, which is not `slots.len()`.
108 len: usize,
109 /// Bytes held by the values in the slots that are not on `soiled`.
110 ///
111 /// Only a real number while `track` is on. Off, it is zero and nobody reads
112 /// it.
113 clean: usize,
114 /// Slots reached mutably since the last reading, whose bytes are therefore
115 /// not in `clean` and have to be asked for again.
116 soiled: Vec<u32>,
117 /// One bit a slot, set while that slot is on `soiled`.
118 ///
119 /// A bit and not a byte because this is one per collection key and the
120 /// memory bar counts. A list rather than a scan of the bits because a
121 /// reading has to cost what the batch touched and not what the slab holds.
122 mark: Vec<u64>,
123 /// Whether the three above are being kept up to date.
124 track: bool,
125}
126
127impl<T: Bytes> Slab<T> {
128 /// An empty slab, which has not allocated anything.
129 #[must_use]
130 pub fn new() -> Slab<T> {
131 Slab {
132 slots: Vec::new(),
133 free: NONE,
134 len: 0,
135 clean: 0,
136 soiled: Vec::new(),
137 mark: Vec::new(),
138 track: false,
139 }
140 }
141
142 /// An empty slab with room for `n` values before it grows.
143 #[must_use]
144 pub fn with_capacity(n: usize) -> Slab<T> {
145 Slab {
146 slots: Vec::with_capacity(n),
147 ..Slab::new()
148 }
149 }
150
151 /// How many values are in it.
152 #[inline]
153 pub fn len(&self) -> usize {
154 self.len
155 }
156
157 /// Whether there are none.
158 #[inline]
159 pub fn is_empty(&self) -> bool {
160 self.len == 0
161 }
162
163 /// Put `value` in and answer where it went.
164 ///
165 /// A free slot if there is one, and the end otherwise. Nothing moves, so
166 /// every number handed out before stays good.
167 ///
168 /// # Panics
169 ///
170 /// If there are already [`MAX_SLOTS`] slots. That is four billion values of
171 /// one type in one database, which is not a number a caller can reach by
172 /// accident, and the alternative is a `Result` on the hot path of every
173 /// `SADD` against a key that does not exist yet.
174 pub fn insert(&mut self, value: T) -> u32 {
175 if self.free != NONE {
176 let at = self.free as usize;
177 let Slot::Free(next) = self.slots[at] else {
178 unreachable!("the free list only ever points at free slots");
179 };
180 self.free = next;
181 // Before the value goes in, so the slot the total is told about is
182 // still the empty one and nothing gets taken out for a value that
183 // was never counted.
184 self.soil(at as u32);
185 self.slots[at] = Slot::Filled(value);
186 self.len += 1;
187 return at as u32;
188 }
189 assert!(self.slots.len() < MAX_SLOTS, "slab is full");
190 let at = self.slots.len() as u32;
191 self.soil(at);
192 self.slots.push(Slot::Filled(value));
193 self.len += 1;
194 at
195 }
196
197 /// The value at `at`, or `None` if that slot is free or does not exist.
198 #[inline]
199 pub fn get(&self, at: u32) -> Option<&T> {
200 match self.slots.get(at as usize) {
201 Some(Slot::Filled(v)) => Some(v),
202 _ => None,
203 }
204 }
205
206 /// The value at `at`, to be changed in place.
207 ///
208 /// This is the only way to change a value, which is what lets the running
209 /// total be a total rather than a guess: whatever the caller does with the
210 /// reference, the slab already knows it has to ask this slot again.
211 #[inline]
212 pub fn get_mut(&mut self, at: u32) -> Option<&mut T> {
213 if self.track && (at as usize) < self.slots.len() {
214 self.soil(at);
215 }
216 match self.slots.get_mut(at as usize) {
217 Some(Slot::Filled(v)) => Some(v),
218 _ => None,
219 }
220 }
221
222 /// Take the value at `at` out and free the slot.
223 ///
224 /// Answers `None` if the slot was already free, without touching the free
225 /// list, so freeing twice is inert rather than a loop.
226 pub fn remove(&mut self, at: u32) -> Option<T> {
227 if self.track && (at as usize) < self.slots.len() {
228 self.soil(at);
229 }
230 match self.slots.get_mut(at as usize) {
231 Some(slot @ Slot::Filled(_)) => {
232 let taken = mem::replace(slot, Slot::Free(self.free));
233 self.free = at;
234 self.len -= 1;
235 match taken {
236 Slot::Filled(v) => Some(v),
237 Slot::Free(_) => unreachable!("just matched on filled"),
238 }
239 }
240 _ => None,
241 }
242 }
243
244 /// Every value in it, in no order a caller should lean on.
245 ///
246 /// This is for counting bytes and for saving, both of which want all of
247 /// them and neither of which cares which came first.
248 pub fn iter(&self) -> impl Iterator<Item = &T> {
249 self.slots.iter().filter_map(|s| match s {
250 Slot::Filled(v) => Some(v),
251 Slot::Free(_) => None,
252 })
253 }
254
255 /// Drop everything and hand the memory back.
256 ///
257 /// This is `FLUSHALL`, where keeping a vector of four million free slots
258 /// around for a database the client just emptied would be the wrong answer.
259 pub fn clear(&mut self) {
260 self.slots = Vec::new();
261 self.free = NONE;
262 self.len = 0;
263 self.clean = 0;
264 self.soiled = Vec::new();
265 self.mark = Vec::new();
266 }
267
268 /// What the slots themselves cost, not counting what the values point at.
269 pub fn slot_bytes(&self) -> usize {
270 self.slots.capacity() * mem::size_of::<Slot<T>>()
271 }
272
273 /// The old name for [`Slab::slot_bytes`], which answered the same number.
274 ///
275 /// Kept because a patch release does not take a public item away, and there
276 /// are now three ways to ask a slab what it costs rather than one, so the
277 /// name that does not say which of the three it means had to go. It goes at
278 /// the next minor.
279 #[deprecated(since = "0.3.8", note = "renamed to slot_bytes")]
280 pub fn memory_bytes(&self) -> usize {
281 self.slot_bytes()
282 }
283
284 /// What the values are holding, asked of every one of them.
285 ///
286 /// The honest walk, for the places that want the number exactly and do not
287 /// care what it costs to get: `INFO memory`, `MEMORY USAGE` and the tests.
288 /// It does not touch the running total and does not need it to be on.
289 pub fn value_bytes(&self) -> usize {
290 self.iter().map(T::memory_bytes).sum()
291 }
292
293 /// The same number, asked only of the values that could have changed.
294 ///
295 /// With tracking on this asks the slots touched since the last call and no
296 /// others, which is what makes a `maxmemory` server able to afford the
297 /// question once a batch. With tracking off it is [`Slab::value_bytes`].
298 pub fn settled_bytes(&mut self) -> usize {
299 if !self.track {
300 return self.value_bytes();
301 }
302 while let Some(at) = self.soiled.pop() {
303 self.mark[at as usize / 64] &= !(1u64 << (at % 64));
304 if let Some(Slot::Filled(v)) = self.slots.get(at as usize) {
305 self.clean += v.memory_bytes();
306 }
307 }
308 self.clean
309 }
310
311 /// Start or stop keeping the running total.
312 ///
313 /// Starting costs one walk, because a total has to start from somewhere and
314 /// the slab may already be holding a million sets when the client sets the
315 /// limit. Stopping costs nothing and gives the two lists back.
316 ///
317 /// Setting it to what it already is does nothing at all, which matters
318 /// because `CONFIG SET maxmemory` on a server that already had one would
319 /// otherwise pay for that walk every time.
320 pub fn track_bytes(&mut self, on: bool) {
321 if on == self.track {
322 return;
323 }
324 self.track = on;
325 self.soiled = Vec::new();
326 self.mark = Vec::new();
327 self.clean = if on { self.value_bytes() } else { 0 };
328 }
329
330 /// Take a slot's bytes back out of the total and write the slot down.
331 ///
332 /// Called before the slot changes and not after, so the value it asks is the
333 /// one the total was told about. A slot already written down is left alone,
334 /// which is why the same key written sixty four times in a batch costs one
335 /// question and not sixty four.
336 #[inline]
337 fn soil(&mut self, at: u32) {
338 if !self.track {
339 return;
340 }
341 let word = at as usize / 64;
342 let bit = 1u64 << (at % 64);
343 if word >= self.mark.len() {
344 self.mark.resize(word + 1, 0);
345 }
346 if self.mark[word] & bit != 0 {
347 return;
348 }
349 self.mark[word] |= bit;
350 self.soiled.push(at);
351 if let Some(Slot::Filled(v)) = self.slots.get(at as usize) {
352 self.clean -= v.memory_bytes();
353 }
354 }
355}
356
357impl<T: Bytes> Default for Slab<T> {
358 fn default() -> Slab<T> {
359 Slab::new()
360 }
361}
362
363#[cfg(test)]
364mod tests {
365 use super::*;
366
367 // Length and not capacity, so a test can say what it expects without
368 // knowing how a `Vec` doubles.
369 impl Bytes for String {
370 fn memory_bytes(&self) -> usize {
371 self.len()
372 }
373 }
374
375 impl Bytes for Vec<u8> {
376 fn memory_bytes(&self) -> usize {
377 self.len()
378 }
379 }
380
381 // For the tests that are about the free list rather than about bytes.
382 impl Bytes for i32 {
383 fn memory_bytes(&self) -> usize {
384 0
385 }
386 }
387
388 impl Bytes for u8 {
389 fn memory_bytes(&self) -> usize {
390 0
391 }
392 }
393
394 #[test]
395 fn a_new_slab_holds_nothing_and_has_allocated_nothing() {
396 let s: Slab<String> = Slab::new();
397 assert_eq!(s.len(), 0);
398 assert!(s.is_empty());
399 assert_eq!(s.get(0), None);
400 assert_eq!(s.slot_bytes(), 0);
401 }
402
403 #[test]
404 fn what_goes_in_comes_back_out_at_the_number_it_was_given() {
405 let mut s = Slab::new();
406 let a = s.insert("a".to_string());
407 let b = s.insert("b".to_string());
408 let c = s.insert("c".to_string());
409 assert_eq!((a, b, c), (0, 1, 2), "the first three go at the end");
410 assert_eq!(s.get(a).map(String::as_str), Some("a"));
411 assert_eq!(s.get(b).map(String::as_str), Some("b"));
412 assert_eq!(s.get(c).map(String::as_str), Some("c"));
413 assert_eq!(s.len(), 3);
414 }
415
416 #[test]
417 fn a_value_can_be_changed_where_it_lies() {
418 let mut s = Slab::new();
419 let a = s.insert(vec![1u8]);
420 s.get_mut(a).expect("filled").push(2);
421 assert_eq!(s.get(a), Some(&vec![1, 2]));
422 assert_eq!(s.get_mut(9), None, "past the end");
423 }
424
425 #[test]
426 fn removing_hands_the_value_back_and_the_others_keep_their_numbers() {
427 let mut s = Slab::new();
428 let a = s.insert("a".to_string());
429 let b = s.insert("b".to_string());
430 let c = s.insert("c".to_string());
431
432 assert_eq!(s.remove(b), Some("b".to_string()));
433 assert_eq!(s.len(), 2);
434 assert_eq!(s.get(b), None);
435 assert_eq!(
436 s.get(a).map(String::as_str),
437 Some("a"),
438 "a did not move when b left"
439 );
440 assert_eq!(s.get(c).map(String::as_str), Some("c"));
441 }
442
443 #[test]
444 fn a_freed_slot_is_the_next_one_used() {
445 let mut s = Slab::new();
446 s.insert(0);
447 let b = s.insert(1);
448 s.insert(2);
449
450 s.remove(b);
451 let next = s.insert(9);
452 assert_eq!(next, b, "the hole was filled rather than the vector grown");
453 assert_eq!(s.len(), 3);
454 assert_eq!(s.get(b), Some(&9));
455 }
456
457 #[test]
458 fn the_free_list_gives_the_holes_back_in_reverse() {
459 // Not a promise to callers, but it is the shape a list threaded head
460 // first has, and a test that walks it is how a broken link shows up.
461 let mut s = Slab::new();
462 let n: Vec<u32> = (0..5).map(|i| s.insert(i)).collect();
463 for i in [1, 3, 4] {
464 s.remove(n[i]);
465 }
466 assert_eq!(s.len(), 2);
467
468 assert_eq!(s.insert(50), 4);
469 assert_eq!(s.insert(51), 3);
470 assert_eq!(s.insert(52), 1);
471 assert_eq!(s.len(), 5);
472
473 // And once the holes run out it grows again.
474 assert_eq!(s.insert(53), 5);
475 assert_eq!(s.get(0), Some(&0), "the untouched ones are untouched");
476 assert_eq!(s.get(2), Some(&2));
477 }
478
479 #[test]
480 fn freeing_twice_is_inert_rather_than_a_loop_in_the_list() {
481 // The failure this guards against is not the second remove. It is the
482 // insert after it: a free list with a slot on it twice hands the same
483 // number to two live values, and the second one silently overwrites the
484 // first. So the test is that the numbers after a double free are still
485 // all different.
486 let mut s = Slab::new();
487 let a = s.insert("a".to_string());
488 let b = s.insert("b".to_string());
489
490 assert_eq!(s.remove(a), Some("a".to_string()));
491 assert_eq!(s.remove(a), None, "already free");
492 assert_eq!(s.remove(a), None, "still already free");
493 assert_eq!(s.len(), 1);
494
495 let x = s.insert("x".to_string());
496 let y = s.insert("y".to_string());
497 let z = s.insert("z".to_string());
498 assert_eq!(x, a, "the one real hole came back");
499 assert_ne!(y, x);
500 assert_ne!(z, x);
501 assert_ne!(z, y);
502 assert_eq!(s.len(), 4);
503 assert_eq!(
504 s.get(b).map(String::as_str),
505 Some("b"),
506 "b was never touched"
507 );
508 }
509
510 #[test]
511 fn removing_something_that_was_never_there_answers_nothing() {
512 let mut s: Slab<u8> = Slab::new();
513 assert_eq!(s.remove(0), None);
514 assert_eq!(s.remove(7), None);
515 assert_eq!(s.len(), 0);
516 assert_eq!(s.insert(1), 0, "and it did not corrupt the free list");
517 }
518
519 #[test]
520 fn iterating_sees_the_values_and_not_the_holes() {
521 let mut s = Slab::new();
522 let n: Vec<u32> = (0..6).map(|i| s.insert(i * 10)).collect();
523 s.remove(n[0]);
524 s.remove(n[3]);
525 s.remove(n[5]);
526
527 let mut got: Vec<i32> = s.iter().copied().collect();
528 got.sort_unstable();
529 assert_eq!(got, [10, 20, 40]);
530 assert_eq!(got.len(), s.len());
531 }
532
533 #[test]
534 fn clearing_hands_the_memory_back_and_starts_the_numbers_again() {
535 let mut s = Slab::with_capacity(64);
536 for i in 0..64 {
537 s.insert(i);
538 }
539 assert!(s.slot_bytes() >= 64 * mem::size_of::<Slot<i32>>());
540
541 s.clear();
542 assert_eq!(s.len(), 0);
543 assert!(s.is_empty());
544 assert_eq!(s.slot_bytes(), 0, "the vector went, not just the values");
545 assert_eq!(s.get(0), None);
546 assert_eq!(s.insert(1), 0, "numbering starts over");
547 }
548
549 #[test]
550 fn the_running_total_says_what_the_walk_says_whatever_was_done_to_it() {
551 // The only property that matters. Everything else in the tracking is an
552 // implementation of it, so the test is a long run of every operation
553 // there is with the two numbers checked against each other after each
554 // one. A missed bookkeeping step in insert, get_mut or remove shows up
555 // here as a difference and nowhere else.
556 let mut s: Slab<String> = Slab::new();
557 s.track_bytes(true);
558 let mut live: Vec<u32> = Vec::new();
559 let mut n = 0usize;
560 for step in 0..500 {
561 match step % 5 {
562 0 | 1 => {
563 n += 1;
564 live.push(s.insert("x".repeat(n % 17)));
565 }
566 2 | 3 => {
567 if let Some(&at) = live.get(step % live.len().max(1)) {
568 s.get_mut(at).expect("filled").push('y');
569 }
570 }
571 _ => {
572 if !live.is_empty() {
573 let at = live.swap_remove(step % live.len());
574 s.remove(at);
575 }
576 }
577 }
578 assert_eq!(
579 s.settled_bytes(),
580 s.value_bytes(),
581 "after step {step}, which was a {}",
582 step % 5
583 );
584 }
585 assert!(n > 0 && !live.is_empty(), "the run did something");
586 }
587
588 #[test]
589 fn a_slot_written_over_and_over_is_only_asked_once_before_a_reading() {
590 // What makes the total affordable. Sixty four writes to the same key in
591 // a batch put it on the list once, so the reading at the end of the
592 // batch asks it once, and the reading is still right.
593 let mut s: Slab<String> = Slab::new();
594 s.track_bytes(true);
595 let a = s.insert(String::new());
596 let b = s.insert("bb".to_string());
597 assert_eq!(s.settled_bytes(), 2);
598
599 for _ in 0..64 {
600 s.get_mut(a).expect("filled").push('a');
601 }
602 assert_eq!(s.soiled.len(), 1, "one slot written down, not sixty four");
603 assert_eq!(s.settled_bytes(), 66);
604 assert_eq!(s.soiled.len(), 0, "and the list is empty again");
605 assert_eq!(s.get(b).map(String::as_str), Some("bb"));
606 }
607
608 #[test]
609 fn nothing_is_counted_until_the_total_is_switched_on() {
610 // Off, the slab still answers the question, it just walks for it. The
611 // first switch on is the walk the total starts from, and switching it on
612 // again when it is already on does not walk a second time.
613 let mut s: Slab<String> = Slab::new();
614 s.insert("abc".to_string());
615 s.insert("de".to_string());
616 assert_eq!(s.settled_bytes(), 5, "the walk, because nothing is tracked");
617 assert_eq!(s.clean, 0, "and it did not start a total behind our back");
618
619 s.track_bytes(true);
620 assert_eq!(s.clean, 5, "the walk it starts from");
621 s.track_bytes(true);
622 assert_eq!(s.clean, 5);
623
624 s.insert("fghi".to_string());
625 assert_eq!(s.settled_bytes(), 9);
626
627 s.track_bytes(false);
628 assert_eq!(s.clean, 0, "and it gave the bookkeeping back");
629 assert_eq!(s.settled_bytes(), 9, "walking again for the same answer");
630 }
631
632 #[test]
633 fn clearing_takes_the_total_with_it() {
634 // FLUSHALL. A total left behind after the values went would be a server
635 // that thinks it is holding an empty database's worth of sets.
636 let mut s: Slab<String> = Slab::new();
637 s.track_bytes(true);
638 for i in 0..10 {
639 s.insert("z".repeat(i));
640 }
641 assert_eq!(s.settled_bytes(), 45);
642
643 s.clear();
644 assert_eq!(s.settled_bytes(), 0);
645 assert_eq!(s.slot_bytes(), 0);
646
647 s.insert("new".to_string());
648 assert_eq!(s.settled_bytes(), 3, "and it counts again from there");
649 }
650
651 #[test]
652 fn a_reused_slot_is_counted_as_what_is_in_it_now() {
653 // The awkward one. A slot goes on the list when its value is removed and
654 // comes back filled with something else before anybody reads the total,
655 // so the reading has to ask the new value and not remember the old.
656 let mut s: Slab<String> = Slab::new();
657 s.track_bytes(true);
658 let a = s.insert("aaaaa".to_string());
659 assert_eq!(s.settled_bytes(), 5);
660
661 s.remove(a);
662 let b = s.insert("bb".to_string());
663 assert_eq!(b, a, "the same slot came back");
664 assert_eq!(s.settled_bytes(), 2);
665 assert_eq!(s.value_bytes(), 2);
666 }
667
668 #[test]
669 fn a_churning_workload_does_not_grow_the_vector() {
670 // The reason the free list exists. Ten thousand keys created and deleted
671 // one after another is a shape a real server sees, and without reuse it
672 // would leave ten thousand dead slots behind.
673 let mut s = Slab::with_capacity(4);
674 let before = s.slot_bytes();
675 for i in 0..10_000 {
676 let at = s.insert(i);
677 assert_eq!(at, 0, "the same slot every time");
678 assert_eq!(s.remove(at), Some(i));
679 }
680 assert_eq!(s.len(), 0);
681 assert_eq!(s.slot_bytes(), before);
682 }
683}