1use std::ops::Bound;
5
6use reifydb_codec::key::encoded::EncodedKey;
7use reifydb_macro::KeyCodec;
8use reifydb_runtime::version_epoch::EpochSeconds;
9use serde::{Deserialize, Serialize, de};
10
11use super::KeyTag;
12use crate::{
13 interface::catalog::id::{MigrationEventId, MigrationId, SequenceId},
14 key::{
15 any::{Field, KeyFields, Width},
16 bound::{TaggedKeyBound, TaggedKeyBoundRange},
17 },
18};
19
20#[derive(Debug, Clone, PartialEq, KeyCodec, Hash)]
21#[key(tag = SystemSequence)]
22pub struct SystemSequenceKey {
23 pub sequence: SequenceId,
24}
25
26impl SystemSequenceKey {
27 pub fn new(sequence: impl Into<SequenceId>) -> Self {
28 Self {
29 sequence: sequence.into(),
30 }
31 }
32
33 pub fn encoded(sequence: impl Into<SequenceId>) -> EncodedKey {
34 Self {
35 sequence: sequence.into(),
36 }
37 .encode()
38 }
39
40 pub fn full_scan() -> TaggedKeyBoundRange {
41 TaggedKeyBoundRange::kind(Self::TAG)
42 }
43}
44
45#[cfg(test)]
46pub mod system_sequence_key_tests {
47 use super::SystemSequenceKey;
48 use crate::interface::catalog::id::SequenceId;
49
50 #[test]
51 fn test_encode_decode() {
52 let key = SystemSequenceKey {
53 sequence: SequenceId(0xABCD),
54 };
55 let encoded = key.encode();
56 let expected = vec![0xFA, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x54, 0x32];
57 assert_eq!(encoded.as_slice(), expected);
58
59 let key = SystemSequenceKey::decode(&encoded).unwrap();
60 assert_eq!(key.sequence.0, 0xABCD);
61 }
62}
63
64#[derive(Debug, Clone, PartialEq, KeyCodec, Hash)]
65#[key(tag = SystemVersion)]
66pub struct SystemVersionKey {
67 #[key(repr = u8)]
68 pub version: SystemVersion,
69}
70
71#[repr(u8)]
72#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
73#[serde(try_from = "u8", into = "u8")]
74pub enum SystemVersion {
75 Storage = 0x01,
76}
77
78impl From<SystemVersion> for u8 {
79 fn from(version: SystemVersion) -> Self {
80 version as u8
81 }
82}
83impl TryFrom<u8> for SystemVersion {
84 type Error = de::value::Error;
85
86 fn try_from(value: u8) -> Result<Self, Self::Error> {
87 match value {
88 0x01 => Ok(Self::Storage),
89 _ => Err(de::Error::custom(format!("Invalid SystemVersion value: {value:#04x}"))),
90 }
91 }
92}
93
94impl SystemVersionKey {
95 pub fn new(version: SystemVersion) -> Self {
96 Self {
97 version,
98 }
99 }
100
101 pub fn encoded(version: SystemVersion) -> EncodedKey {
102 Self {
103 version,
104 }
105 .encode()
106 }
107}
108
109#[cfg(test)]
110pub mod system_version_key_tests {
111 use super::{SystemVersion, SystemVersionKey};
112
113 #[test]
114 fn test_encode_decode_storage_version() {
115 let key = SystemVersionKey {
116 version: SystemVersion::Storage,
117 };
118 let encoded = key.encode();
119 let expected = vec![0xF5, 0xFE];
120 assert_eq!(encoded.as_slice(), expected);
121
122 let key = SystemVersionKey::decode(&encoded).unwrap();
123 assert_eq!(key.version, SystemVersion::Storage);
124 }
125}
126
127#[derive(Debug, Clone, Copy, PartialEq, Eq, KeyCodec, Hash)]
128#[key(tag = VersionEpoch)]
129pub struct VersionEpochKey {
130 pub bucket: EpochSeconds,
131}
132
133impl VersionEpochKey {
134 pub fn new(bucket: EpochSeconds) -> Self {
135 Self {
136 bucket,
137 }
138 }
139
140 pub fn encoded(bucket: EpochSeconds) -> EncodedKey {
141 Self::new(bucket).encode()
142 }
143
144 fn bucket_bound(bucket: EpochSeconds) -> TaggedKeyBound {
145 TaggedKeyBound::prefix(Self::TAG, [Field::UDesc(Width::U64, bucket.seconds() as u128)])
146 }
147
148 pub fn floor_scan(target: EpochSeconds) -> TaggedKeyBoundRange {
149 TaggedKeyBoundRange {
150 start: Bound::Included(Self::bucket_bound(target)),
151 end: Bound::Included(Self::bucket_bound(EpochSeconds::new(0))),
152 }
153 }
154
155 pub fn older_than(cutoff: EpochSeconds) -> TaggedKeyBoundRange {
156 TaggedKeyBoundRange {
157 start: Bound::Excluded(Self::bucket_bound(cutoff)),
158 end: Bound::Included(Self::bucket_bound(EpochSeconds::new(0))),
159 }
160 }
161}
162
163#[cfg(test)]
164mod version_epoch_key_tests {
165 use std::ops::Bound;
166
167 use super::{EpochSeconds, VersionEpochKey};
168
169 fn sec(seconds: u64) -> EpochSeconds {
170 EpochSeconds::new(seconds)
171 }
172
173 #[test]
174 fn test_encode_decode() {
175 let key = VersionEpochKey {
176 bucket: sec(0x0123456789ABCDEF),
177 };
178 let encoded = key.encode();
179 let decoded = VersionEpochKey::decode(&encoded).unwrap();
180 assert_eq!(decoded.bucket, sec(0x0123456789ABCDEF));
181 }
182
183 #[test]
184 fn test_descending_order_so_newer_bucket_sorts_first() {
185 let older = VersionEpochKey::encoded(sec(100));
186 let newer = VersionEpochKey::encoded(sec(200));
187 assert!(
188 newer < older,
189 "a newer (larger) bucket must encode to smaller key bytes so floor_scan can take the first entry at-or-after the target"
190 );
191 }
192
193 #[test]
194 fn test_floor_scan_lower_bound_is_target_bucket() {
195 let target = sec(150);
196 let range = VersionEpochKey::floor_scan(target).encode();
197 assert_eq!(range.start, Bound::Included(VersionEpochKey::encoded(target)));
198 assert_eq!(range.end, Bound::Included(VersionEpochKey::encoded(sec(0))));
199 assert!(VersionEpochKey::encoded(target) >= VersionEpochKey::encoded(target));
201 assert!(VersionEpochKey::encoded(sec(151)) < VersionEpochKey::encoded(target));
202 }
203}
204
205#[derive(Debug, Clone, PartialEq, KeyCodec, Hash)]
206#[key(tag = Migration)]
207pub struct MigrationKey {
208 pub migration: MigrationId,
209}
210
211impl MigrationKey {
212 pub fn new(migration: MigrationId) -> Self {
213 Self {
214 migration,
215 }
216 }
217
218 pub fn encoded(migration: impl Into<MigrationId>) -> EncodedKey {
219 Self::new(migration.into()).encode()
220 }
221
222 pub fn full_scan() -> TaggedKeyBoundRange {
223 TaggedKeyBoundRange::kind(Self::TAG)
224 }
225}
226
227#[cfg(test)]
228mod migration_key_tests {
229 use super::MigrationKey;
230 use crate::interface::catalog::id::MigrationId;
231
232 #[test]
233 fn test_encode_decode() {
234 let key = MigrationKey {
235 migration: MigrationId(0xABCD),
236 };
237 let encoded = key.encode();
238 let decoded = MigrationKey::decode(&encoded).unwrap();
239 assert_eq!(decoded.migration, MigrationId(0xABCD));
240 }
241}
242
243#[derive(Debug, Clone, PartialEq, KeyCodec, Hash)]
244#[key(tag = MigrationEvent)]
245pub struct MigrationEventKey {
246 pub event: MigrationEventId,
247}
248
249impl MigrationEventKey {
250 pub fn new(event: MigrationEventId) -> Self {
251 Self {
252 event,
253 }
254 }
255
256 pub fn encoded(event: impl Into<MigrationEventId>) -> EncodedKey {
257 Self::new(event.into()).encode()
258 }
259
260 pub fn full_scan() -> TaggedKeyBoundRange {
261 TaggedKeyBoundRange::kind(Self::TAG)
262 }
263}
264
265#[cfg(test)]
266mod migration_event_key_tests {
267 use super::MigrationEventKey;
268 use crate::interface::catalog::id::MigrationEventId;
269
270 #[test]
271 fn test_encode_decode() {
272 let key = MigrationEventKey {
273 event: MigrationEventId(0xABCD),
274 };
275 let encoded = key.encode();
276 let decoded = MigrationEventKey::decode(&encoded).unwrap();
277 assert_eq!(decoded.event, MigrationEventId(0xABCD));
278 }
279}