reifydb_core/key/operator/keyspace/
timer.rs1use reifydb_value::{util::hash::xxh3_128, value::datetime::DateTime};
5
6use crate::{
7 key::{
8 operator::{
9 state::{GroupId, KeyspaceId},
10 traits::Keyspace,
11 },
12 typed::{
13 BoundedKey, DenseKey, KeyLayout,
14 direction::{Asc, Direction, KeyField},
15 layout::{KeyColumn, KeyColumnType, KeyLayout, KeyValue, KeyValues},
16 },
17 },
18 metrics::heap::HeapSize,
19 state::timer::TimerKind,
20};
21
22#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, KeyLayout, HeapSize)]
23pub struct TimerWheelKey {
24 pub due: Asc<DateTime>,
25 pub kind: Asc<TimerKind>,
26 pub id: Asc<[u8; 16]>,
27}
28
29#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, KeyLayout, HeapSize)]
30pub struct TimerIndexKey {
31 pub kind: Asc<TimerKind>,
32 pub id: Asc<[u8; 16]>,
33}
34
35pub fn timer_id(bytes: &[u8]) -> [u8; 16] {
36 xxh3_128(bytes).0.to_be_bytes()
37}
38
39#[derive(Clone, Copy, Debug, PartialEq, Eq)]
40pub struct TimerWheel;
41
42impl Keyspace for TimerWheel {
43 const ID: KeyspaceId = KeyspaceId::TIMER_WHEEL;
44 const NAME: &'static str = "TIMER_WHEEL";
45 const RANGE_CACHED: bool = true;
46
47 type GroupedKey = TimerWheelKey;
48 type Suffix = TimerWheelKey;
49
50 fn split(key: &Self::GroupedKey) -> (GroupId, Self::Suffix) {
51 (GroupId::ROOT, *key)
52 }
53
54 fn join(_group: GroupId, suffix: Self::Suffix) -> Self::GroupedKey {
55 suffix
56 }
57}
58
59#[derive(Clone, Copy, Debug, PartialEq, Eq)]
60pub struct TimerIndex;
61
62impl Keyspace for TimerIndex {
63 const ID: KeyspaceId = KeyspaceId::TIMER_INDEX;
64 const NAME: &'static str = "TIMER_INDEX";
65 const RANGE_CACHED: bool = true;
66
67 type GroupedKey = TimerIndexKey;
68 type Suffix = TimerIndexKey;
69
70 fn split(key: &Self::GroupedKey) -> (GroupId, Self::Suffix) {
71 (GroupId::ROOT, *key)
72 }
73
74 fn join(_group: GroupId, suffix: Self::Suffix) -> Self::GroupedKey {
75 suffix
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use super::{TimerIndexKey, TimerWheelKey, timer_id};
82 use crate::key::typed::{
83 direction::{Asc, Direction},
84 layout::{KeyColumn, KeyColumnType, KeyLayout},
85 };
86
87 #[test]
88 fn a_timer_id_of_any_length_narrows_to_the_key_width() {
89 for len in [0usize, 1, 16, 25, 4096] {
92 let bytes = vec![0xABu8; len];
93 assert_eq!(timer_id(&bytes).len(), 16, "an id of {len} bytes must still key on sixteen");
94 }
95 }
96
97 #[test]
98 fn distinct_timer_ids_stay_distinct_after_narrowing() {
99 let long = vec![0x11u8; 25];
102 let mut other = long.clone();
103 other[24] = 0x12;
104 assert_ne!(timer_id(&long), timer_id(&other));
105 assert_ne!(timer_id(b""), timer_id(b"\0"));
106 }
107
108 #[test]
109 fn a_timer_id_is_stable_across_calls() {
110 assert_eq!(timer_id(b"seal:window:7"), timer_id(b"seal:window:7"));
112 }
113
114 #[test]
115 fn timer_ids_order_as_the_unsigned_integers_they_hash_to() {
116 let low = 7u128.to_be_bytes();
119 let high = 8u128.to_be_bytes();
120 assert!(low < high);
121 assert!(Asc(low) < Asc(high));
122 }
123
124 #[test]
125 fn the_wheel_leads_on_due_time_and_the_index_leads_on_kind() {
126 assert_eq!(TimerWheelKey::COLUMNS[0].name, "due");
129 assert_eq!(TimerWheelKey::COLUMNS[0].direction, Direction::Asc);
130 assert_eq!(TimerWheelKey::COLUMNS[2].ty, KeyColumnType::Blob16);
131 assert_eq!(TimerIndexKey::COLUMNS[0].name, "kind");
132 assert_eq!(
133 TimerIndexKey::COLUMNS[1],
134 KeyColumn {
135 name: "id",
136 ty: KeyColumnType::Blob16,
137 direction: Direction::Asc,
138 }
139 );
140 }
141}