use reifydb_value::{util::hash::xxh3_128, value::datetime::DateTime};
use crate::{
key::{
operator::{
state::{GroupId, KeyspaceId},
traits::Keyspace,
},
typed::{
BoundedKey, DenseKey, KeyLayout,
direction::{Asc, Direction, KeyField},
layout::{KeyColumn, KeyColumnType, KeyLayout, KeyValue, KeyValues},
},
},
metrics::heap::HeapSize,
state::timer::TimerKind,
};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, KeyLayout, HeapSize)]
pub struct TimerWheelKey {
pub due: Asc<DateTime>,
pub kind: Asc<TimerKind>,
pub id: Asc<[u8; 16]>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, KeyLayout, HeapSize)]
pub struct TimerIndexKey {
pub kind: Asc<TimerKind>,
pub id: Asc<[u8; 16]>,
}
pub fn timer_id(bytes: &[u8]) -> [u8; 16] {
xxh3_128(bytes).0.to_be_bytes()
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct TimerWheel;
impl Keyspace for TimerWheel {
const ID: KeyspaceId = KeyspaceId::TIMER_WHEEL;
const NAME: &'static str = "TIMER_WHEEL";
const RANGE_CACHED: bool = true;
type GroupedKey = TimerWheelKey;
type Suffix = TimerWheelKey;
fn split(key: &Self::GroupedKey) -> (GroupId, Self::Suffix) {
(GroupId::ROOT, *key)
}
fn join(_group: GroupId, suffix: Self::Suffix) -> Self::GroupedKey {
suffix
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct TimerIndex;
impl Keyspace for TimerIndex {
const ID: KeyspaceId = KeyspaceId::TIMER_INDEX;
const NAME: &'static str = "TIMER_INDEX";
const RANGE_CACHED: bool = true;
type GroupedKey = TimerIndexKey;
type Suffix = TimerIndexKey;
fn split(key: &Self::GroupedKey) -> (GroupId, Self::Suffix) {
(GroupId::ROOT, *key)
}
fn join(_group: GroupId, suffix: Self::Suffix) -> Self::GroupedKey {
suffix
}
}
#[cfg(test)]
mod tests {
use super::{TimerIndexKey, TimerWheelKey, timer_id};
use crate::key::typed::{
direction::{Asc, Direction},
layout::{KeyColumn, KeyColumnType, KeyLayout},
};
#[test]
fn a_timer_id_of_any_length_narrows_to_the_key_width() {
for len in [0usize, 1, 16, 25, 4096] {
let bytes = vec![0xABu8; len];
assert_eq!(timer_id(&bytes).len(), 16, "an id of {len} bytes must still key on sixteen");
}
}
#[test]
fn distinct_timer_ids_stay_distinct_after_narrowing() {
let long = vec![0x11u8; 25];
let mut other = long.clone();
other[24] = 0x12;
assert_ne!(timer_id(&long), timer_id(&other));
assert_ne!(timer_id(b""), timer_id(b"\0"));
}
#[test]
fn a_timer_id_is_stable_across_calls() {
assert_eq!(timer_id(b"seal:window:7"), timer_id(b"seal:window:7"));
}
#[test]
fn timer_ids_order_as_the_unsigned_integers_they_hash_to() {
let low = 7u128.to_be_bytes();
let high = 8u128.to_be_bytes();
assert!(low < high);
assert!(Asc(low) < Asc(high));
}
#[test]
fn the_wheel_leads_on_due_time_and_the_index_leads_on_kind() {
assert_eq!(TimerWheelKey::COLUMNS[0].name, "due");
assert_eq!(TimerWheelKey::COLUMNS[0].direction, Direction::Asc);
assert_eq!(TimerWheelKey::COLUMNS[2].ty, KeyColumnType::Blob16);
assert_eq!(TimerIndexKey::COLUMNS[0].name, "kind");
assert_eq!(
TimerIndexKey::COLUMNS[1],
KeyColumn {
name: "id",
ty: KeyColumnType::Blob16,
direction: Direction::Asc,
}
);
}
}