reifydb_core/key/
version_epoch.rs1use std::ops::Bound;
5
6use reifydb_codec::key::{
7 deserializer::KeyDeserializer,
8 encoded::{EncodedKey, EncodedKeyRange},
9 serializer::KeySerializer,
10};
11use reifydb_runtime::version_epoch::EpochSeconds;
12
13use super::{EncodableKey, KeyKind};
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub struct VersionEpochKey {
17 pub bucket: EpochSeconds,
18}
19
20impl EncodableKey for VersionEpochKey {
21 const KIND: KeyKind = KeyKind::VersionEpoch;
22
23 fn encode(&self) -> EncodedKey {
24 let mut serializer = KeySerializer::with_capacity(9);
25 serializer.extend_u8(Self::KIND as u8).extend_u64(self.bucket.seconds());
26 serializer.to_encoded_key()
27 }
28
29 fn decode(key: &EncodedKey) -> Option<Self> {
30 let mut de = KeyDeserializer::from_bytes(key.as_slice());
31
32 let kind: KeyKind = de.read_u8().ok()?.try_into().ok()?;
33 if kind != Self::KIND {
34 return None;
35 }
36
37 let bucket = EpochSeconds::new(de.read_u64().ok()?);
38
39 Some(Self {
40 bucket,
41 })
42 }
43}
44
45impl VersionEpochKey {
46 pub fn encoded(bucket: EpochSeconds) -> EncodedKey {
47 Self {
48 bucket,
49 }
50 .encode()
51 }
52
53 pub fn floor_scan(target: EpochSeconds) -> EncodedKeyRange {
54 EncodedKeyRange::new(
55 Bound::Included(Self::encoded(target)),
56 Bound::Included(Self::encoded(EpochSeconds::new(0))),
57 )
58 }
59
60 pub fn older_than(cutoff: EpochSeconds) -> EncodedKeyRange {
61 EncodedKeyRange::new(
62 Bound::Excluded(Self::encoded(cutoff)),
63 Bound::Included(Self::encoded(EpochSeconds::new(0))),
64 )
65 }
66}
67
68#[cfg(test)]
69mod tests {
70 use std::ops::Bound;
71
72 use super::{EncodableKey, EpochSeconds, VersionEpochKey};
73
74 fn sec(seconds: u64) -> EpochSeconds {
75 EpochSeconds::new(seconds)
76 }
77
78 #[test]
79 fn test_encode_decode() {
80 let key = VersionEpochKey {
81 bucket: sec(0x0123456789ABCDEF),
82 };
83 let encoded = key.encode();
84 let decoded = VersionEpochKey::decode(&encoded).unwrap();
85 assert_eq!(decoded.bucket, sec(0x0123456789ABCDEF));
86 }
87
88 #[test]
89 fn test_descending_order_so_newer_bucket_sorts_first() {
90 let older = VersionEpochKey::encoded(sec(100));
91 let newer = VersionEpochKey::encoded(sec(200));
92 assert!(
93 newer < older,
94 "a newer (larger) bucket must encode to smaller key bytes so floor_scan can take the first entry at-or-after the target"
95 );
96 }
97
98 #[test]
99 fn test_floor_scan_lower_bound_is_target_bucket() {
100 let target = sec(150);
101 let range = VersionEpochKey::floor_scan(target);
102 assert_eq!(range.start, Bound::Included(VersionEpochKey::encoded(target)));
103 assert_eq!(range.end, Bound::Included(VersionEpochKey::encoded(sec(0))));
104 assert!(VersionEpochKey::encoded(target) >= VersionEpochKey::encoded(target));
106 assert!(VersionEpochKey::encoded(sec(151)) < VersionEpochKey::encoded(target));
107 }
108}