use yo_index::Cursor as KeyCursor;
use crate::value::Kind;
use crate::{Clock, Keyspace};
pub const MAX_STRIPES: usize = 1 << yo_index::STRIPE_BITS;
const STRIPE_SHIFT: u32 = 56;
pub struct Db {
stripes: Vec<Keyspace>,
mask: u64,
scratch: Vec<u8>,
rows: Vec<usize>,
setops: crate::setops::Scratch,
}
impl Db {
#[must_use]
pub fn with_clock(clock: Clock, stripes: usize) -> Db {
let n = stripes.clamp(1, MAX_STRIPES).next_power_of_two();
Db {
stripes: (0..n).map(|_| Keyspace::with_clock(clock)).collect(),
mask: (n - 1) as u64,
scratch: Vec::new(),
rows: Vec::new(),
setops: crate::setops::Scratch::new(),
}
}
#[must_use]
pub fn new() -> Db {
Db::with_clock(Clock::system(), 1)
}
#[must_use]
pub fn width(&self) -> usize {
self.stripes.len()
}
#[inline]
#[must_use]
pub fn stripe_of(&self, key: &[u8]) -> usize {
self.stripe_of_hash(Keyspace::hash_of(key))
}
#[inline]
#[must_use]
pub fn stripe_of_hash(&self, hash: u64) -> usize {
((hash >> STRIPE_SHIFT) & self.mask) as usize
}
#[inline]
#[must_use]
pub fn at(&mut self, key: &[u8]) -> &mut Keyspace {
let i = self.stripe_of(key);
&mut self.stripes[i]
}
#[inline]
#[must_use]
pub fn at_ref(&self, key: &[u8]) -> &Keyspace {
let i = self.stripe_of(key);
&self.stripes[i]
}
#[inline]
#[must_use]
pub fn at_hashed(&mut self, hash: u64) -> &mut Keyspace {
let i = self.stripe_of_hash(hash);
&mut self.stripes[i]
}
#[inline]
#[must_use]
pub fn at_ref_hashed(&self, hash: u64) -> &Keyspace {
let i = self.stripe_of_hash(hash);
&self.stripes[i]
}
#[must_use]
pub fn one_stripe<'k>(&self, mut keys: impl Iterator<Item = &'k [u8]>) -> Option<usize> {
let first = self.stripe_of(keys.next()?);
keys.all(|key| self.stripe_of(key) == first)
.then_some(first)
}
pub(crate) fn take_scratch(&mut self) -> (Vec<u8>, Vec<usize>) {
(
std::mem::take(&mut self.scratch),
std::mem::take(&mut self.rows),
)
}
pub(crate) fn put_scratch(&mut self, scratch: Vec<u8>, rows: Vec<usize>) {
self.scratch = scratch;
self.rows = rows;
}
pub(crate) fn scratch_bytes(&self) -> &[u8] {
&self.scratch
}
pub(crate) fn take_setops(&mut self) -> crate::setops::Scratch {
std::mem::take(&mut self.setops)
}
pub(crate) fn put_setops(&mut self, setops: crate::setops::Scratch) {
self.setops = setops;
}
#[inline]
#[must_use]
pub fn stripe_mut(&mut self, i: usize) -> &mut Keyspace {
&mut self.stripes[i]
}
#[inline]
#[must_use]
pub fn stripe(&self, i: usize) -> &Keyspace {
&self.stripes[i]
}
#[must_use]
pub fn stripes(&self) -> &[Keyspace] {
&self.stripes
}
pub fn stripes_mut(&mut self) -> &mut [Keyspace] {
&mut self.stripes
}
#[must_use]
pub fn now_ms(&self) -> u64 {
self.stripes[0].clock().now_ms()
}
#[must_use]
pub fn len(&self) -> usize {
self.stripes.iter().map(Keyspace::len).sum()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.stripes.iter().all(Keyspace::is_empty)
}
#[must_use]
pub fn expires(&self) -> usize {
self.stripes.iter().map(Keyspace::expires).sum()
}
pub fn clear(&mut self) {
for stripe in &mut self.stripes {
stripe.clear();
}
}
pub fn set_clock_ms(&mut self, ms: u64) {
for stripe in &mut self.stripes {
stripe.clock_mut().set(ms);
}
}
pub fn track_memory(&mut self, on: bool) {
for stripe in &mut self.stripes {
stripe.track_memory(on);
}
}
pub fn scan(
&mut self,
from: KeyCursor,
budget: usize,
ty: Option<Kind>,
mut out: impl FnMut(&[u8]),
) -> KeyCursor {
let mut at = from.stripe();
if at >= self.stripes.len() {
return KeyCursor::START;
}
let mut cursor = from.without_stripe();
let mut seen = 0usize;
while at < self.stripes.len() {
let next = self.stripes[at].scan(cursor, budget, ty, |key| {
seen += 1;
out(key);
});
if !next.is_end() {
return next.with_stripe(at);
}
at += 1;
cursor = KeyCursor::START;
if at < self.stripes.len() && seen >= budget {
return KeyCursor::START.with_stripe(at);
}
}
KeyCursor::START
}
pub fn keys(&mut self, mut out: impl FnMut(&[u8])) {
for stripe in &mut self.stripes {
stripe.keys(&mut out);
}
}
pub fn random_key(&mut self) -> Option<&[u8]> {
let live = self.len();
if live == 0 {
return None;
}
let draw = (self.stripes[0].random() % live as u64) as usize;
let mut running = 0;
let mut first = 0;
for (i, stripe) in self.stripes.iter().enumerate() {
running += stripe.len();
if draw < running {
first = i;
break;
}
}
let mut buf = std::mem::take(&mut self.scratch);
buf.clear();
let mut found = false;
for step in 0..self.stripes.len() {
let i = (first as u64 + step as u64) & self.mask;
if let Some(key) = self.stripes[i as usize].random_key() {
yo_alloc::high_water(|| buf.extend_from_slice(key));
found = true;
break;
}
}
self.scratch = buf;
found.then_some(self.scratch.as_slice())
}
}
impl Default for Db {
fn default() -> Db {
Db::new()
}
}
#[cfg(test)]
mod tests {
use std::collections::HashSet;
use yo_index::Cursor as KeyCursor;
use super::{Db, MAX_STRIPES};
use crate::{Clock, Keyspace};
fn filled(stripes: usize, keys: u32) -> Db {
let mut db = Db::with_clock(Clock::fixed(1_000_000), stripes);
for i in 0..keys {
let key = format!("k{i}").into_bytes();
db.at(&key).setnx(&key, b"v").expect("room for a record");
}
db
}
#[test]
fn a_width_is_always_a_power_of_two_and_never_zero() {
for asked in [0, 1, 2, 3, 5, 8, 9, 100] {
let db = Db::with_clock(Clock::system(), asked);
assert!(db.width().is_power_of_two());
assert!(db.width() >= asked.max(1));
}
assert_eq!(Db::with_clock(Clock::system(), 10_000).width(), MAX_STRIPES);
}
#[test]
fn one_stripe_takes_every_key() {
let db = Db::with_clock(Clock::system(), 1);
for i in 0..1000u32 {
assert_eq!(db.stripe_of(&i.to_le_bytes()), 0);
}
}
#[test]
fn a_list_of_keys_is_on_one_stripe_or_it_is_not() {
let names: [&[u8]; 3] = [b"a", b"b", b"c"];
let one = Db::with_clock(Clock::system(), 1);
assert_eq!(one.one_stripe(names.into_iter()), Some(0));
assert_eq!(one.one_stripe(std::iter::empty()), None);
let many = Db::with_clock(Clock::system(), 16);
assert_eq!(many.one_stripe(names.into_iter()), None);
let home = many.stripe_of(b"a");
assert_eq!(many.one_stripe(std::iter::once(&b"a"[..])), Some(home));
assert_eq!(many.one_stripe([&b"a"[..], b"a"].into_iter()), Some(home));
}
#[test]
fn a_key_always_answers_the_same_stripe() {
let db = Db::with_clock(Clock::system(), 16);
for i in 0..1000u32 {
let key = i.to_le_bytes();
let first = db.stripe_of(&key);
assert_eq!(db.stripe_of(&key), first);
assert_eq!(db.stripe_of_hash(Keyspace::hash_of(&key)), first);
}
}
#[test]
fn the_stripe_number_moves_with_the_key() {
let db = Db::with_clock(Clock::system(), 16);
let mut seen = [0usize; 16];
for i in 0..1000u32 {
seen[db.stripe_of(&i.to_le_bytes())] += 1;
}
assert!(
seen.iter().all(|&n| n > 0),
"some stripe took no keys: {seen:?}"
);
}
#[test]
fn a_key_written_to_its_stripe_is_found_on_its_stripe() {
let mut db = Db::with_clock(Clock::system(), 8);
for i in 0..200u32 {
let key = i.to_le_bytes();
assert!(db.at(&key).setnx(&key, b"x").unwrap());
}
assert_eq!(db.len(), 200);
for i in 0..200u32 {
let key = i.to_le_bytes();
assert!(db.at(&key).exists(&key));
}
db.clear();
assert!(db.is_empty());
}
#[test]
fn a_scan_walks_every_stripe_and_answers_every_key_once() {
let mut db = filled(8, 2_000);
let mut seen: Vec<Vec<u8>> = Vec::new();
let mut at = KeyCursor::START;
let mut calls = 0;
loop {
at = db.scan(at, 10, None, |key| seen.push(key.to_vec()));
calls += 1;
if at.is_end() {
break;
}
assert!(calls < 10_000, "a scan that will not finish");
}
let unique: HashSet<Vec<u8>> = seen.iter().cloned().collect();
assert_eq!(unique.len(), 2_000);
assert_eq!(seen.len(), 2_000, "a quiet scan returned a key twice");
let mut walked = HashSet::new();
db.keys(|key| {
walked.insert(key.to_vec());
});
assert_eq!(unique, walked);
}
#[test]
fn a_scan_carries_the_stripe_in_the_cursor() {
let mut db = filled(8, 2_000);
let first = db.scan(KeyCursor::START, 10, None, |_| {});
assert!(!first.is_end());
let mut stripes = HashSet::new();
let mut at = KeyCursor::START;
loop {
at = db.scan(at, 10, None, |_| {});
if at.is_end() {
break;
}
stripes.insert(at.stripe());
}
assert_eq!(stripes.len(), 8, "some stripe was never the one in hand");
let beyond = KeyCursor::START.with_stripe(9);
let mut any = false;
assert!(db.scan(beyond, 10, None, |_| any = true).is_end());
assert!(!any);
}
#[test]
fn a_random_key_comes_from_whichever_stripe_still_has_one() {
let mut db = filled(8, 5_000);
let mut all = HashSet::new();
db.keys(|key| {
all.insert(key.to_vec());
});
let mut picked = HashSet::new();
for _ in 0..200 {
let key = db.random_key().expect("the database is not empty").to_vec();
assert!(all.contains(&key), "a key that is not there");
picked.insert(key);
}
assert!(picked.len() > 10, "only {} distinct keys", picked.len());
for i in 0..5_000u32 {
if i != 4_242 {
let key = format!("k{i}").into_bytes();
db.at(&key).del(&key);
}
}
for _ in 0..20 {
assert_eq!(db.random_key(), Some(&b"k4242"[..]));
}
db.at(b"k4242").del(b"k4242");
assert_eq!(db.random_key(), None);
}
#[test]
fn a_second_random_key_does_not_allocate() {
let mut db = filled(8, 500);
assert!(db.random_key().is_some(), "the database is not empty");
let (_, allocs) = crate::tally::counted(|| {
for _ in 0..200 {
assert!(db.random_key().is_some(), "the database is not empty");
}
});
assert_eq!(
allocs, 0,
"randomkey allocated {allocs} times in two hundred"
);
}
}