1use crate::{CoreError, Epoch};
30
31pub const MAX_SEQ_KEY_LEN: usize = 128;
33
34pub const DEFAULT_MAX_SEQ_COUNT: u32 = 65_536;
48
49#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
53#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
54pub struct SeqKey(String);
55
56impl SeqKey {
57 pub fn try_new(s: impl Into<String>) -> Result<Self, CoreError> {
58 let s = s.into();
59 if s.is_empty() {
60 return Err(CoreError::SeqKeyEmpty);
61 }
62 if s.len() > MAX_SEQ_KEY_LEN {
63 return Err(CoreError::SeqKeyTooLong {
64 len: s.len(),
65 max: MAX_SEQ_KEY_LEN,
66 });
67 }
68 Ok(SeqKey(s))
69 }
70
71 pub fn as_str(&self) -> &str {
72 &self.0
73 }
74}
75
76#[derive(Clone, Debug, PartialEq, Eq)]
87pub struct SeqGrant {
88 key: SeqKey,
89 start: u64,
90 count: u32,
91 epoch: Epoch,
92}
93
94impl SeqGrant {
95 pub fn try_new(key: SeqKey, start: u64, count: u32, epoch: Epoch) -> Result<Self, CoreError> {
105 if count == 0 {
106 return Err(CoreError::SeqCountZero);
107 }
108 if start.checked_add(u64::from(count) - 1).is_none() {
110 return Err(CoreError::SeqBlockOverflow { start, count });
111 }
112 Ok(SeqGrant {
113 key,
114 start,
115 count,
116 epoch,
117 })
118 }
119 pub fn key(&self) -> &SeqKey {
120 &self.key
121 }
122 pub fn start(&self) -> u64 {
123 self.start
124 }
125 pub fn count(&self) -> u32 {
126 self.count
127 }
128 pub fn epoch(&self) -> Epoch {
129 self.epoch
130 }
131 pub fn last(&self) -> u64 {
139 self.start + (u64::from(self.count) - 1)
140 }
141}
142
143#[derive(Debug)]
144enum SeqState {
145 NotLeader,
146 Leader { epoch: Epoch },
147}
148
149pub struct SeqAllocator {
154 state: SeqState,
155}
156
157impl SeqAllocator {
158 pub fn new() -> Self {
159 SeqAllocator {
160 state: SeqState::NotLeader,
161 }
162 }
163
164 pub fn become_leader(&mut self, epoch: Epoch) {
167 self.state = SeqState::Leader { epoch };
168 }
169
170 pub fn step_down(&mut self) {
171 self.state = SeqState::NotLeader;
172 }
173
174 pub fn is_leader(&self) -> bool {
175 matches!(self.state, SeqState::Leader { .. })
176 }
177
178 pub fn epoch(&self) -> Option<Epoch> {
179 match self.state {
180 SeqState::Leader { epoch } => Some(epoch),
181 SeqState::NotLeader => None,
182 }
183 }
184
185 pub fn validate_request(
197 &self,
198 key: &str,
199 count: u32,
200 max_count: u32,
201 ) -> Result<SeqKey, CoreError> {
202 if !self.is_leader() {
203 return Err(CoreError::NotLeader);
204 }
205 if count == 0 {
206 return Err(CoreError::SeqCountZero);
207 }
208 if count > max_count {
209 return Err(CoreError::SeqCountTooLarge {
210 count,
211 max: max_count,
212 });
213 }
214 SeqKey::try_new(key)
215 }
216}
217
218impl Default for SeqAllocator {
219 fn default() -> Self {
220 Self::new()
221 }
222}
223
224#[cfg(test)]
225mod seqallocator_tests {
226 use super::*;
227
228 #[test]
229 fn new_is_not_leader() {
230 let a = SeqAllocator::new();
231 assert!(!a.is_leader());
232 assert_eq!(a.epoch(), None);
233 }
234
235 #[test]
236 fn become_leader_then_step_down() {
237 let mut a = SeqAllocator::new();
238 a.become_leader(Epoch(3));
239 assert!(a.is_leader());
240 assert_eq!(a.epoch(), Some(Epoch(3)));
241 a.step_down();
242 assert!(!a.is_leader());
243 assert_eq!(a.epoch(), None);
244 }
245
246 #[test]
247 fn validate_request_off_leader_is_not_leader() {
248 let a = SeqAllocator::new();
249 assert_eq!(
250 a.validate_request("orders", 1, DEFAULT_MAX_SEQ_COUNT),
251 Err(CoreError::NotLeader)
252 );
253 }
254
255 #[test]
256 fn validate_request_rejects_zero_count() {
257 let mut a = SeqAllocator::new();
258 a.become_leader(Epoch(1));
259 assert_eq!(
260 a.validate_request("orders", 0, DEFAULT_MAX_SEQ_COUNT),
261 Err(CoreError::SeqCountZero)
262 );
263 }
264
265 #[test]
266 fn validate_request_rejects_oversized_count() {
267 let mut a = SeqAllocator::new();
268 a.become_leader(Epoch(1));
269 assert_eq!(
270 a.validate_request("orders", DEFAULT_MAX_SEQ_COUNT + 1, DEFAULT_MAX_SEQ_COUNT),
271 Err(CoreError::SeqCountTooLarge {
272 count: DEFAULT_MAX_SEQ_COUNT + 1,
273 max: DEFAULT_MAX_SEQ_COUNT
274 })
275 );
276 }
277
278 #[test]
279 fn validate_request_uses_caller_provided_max() {
280 let mut a = SeqAllocator::new();
284 a.become_leader(Epoch(1));
285 assert_eq!(
286 a.validate_request("orders", 11, 10),
287 Err(CoreError::SeqCountTooLarge { count: 11, max: 10 }),
288 "count above a small configured cap must be rejected",
289 );
290 assert!(
291 a.validate_request("orders", 10, 10).is_ok(),
292 "count at the configured cap must be accepted",
293 );
294 assert!(
295 a.validate_request(
296 "orders",
297 DEFAULT_MAX_SEQ_COUNT + 1,
298 DEFAULT_MAX_SEQ_COUNT * 2
299 )
300 .is_ok(),
301 "a larger configured cap must accept counts above the default",
302 );
303 }
304
305 #[test]
306 fn validate_request_rejects_bad_key() {
307 let mut a = SeqAllocator::new();
308 a.become_leader(Epoch(1));
309 assert_eq!(
310 a.validate_request("", 1, DEFAULT_MAX_SEQ_COUNT),
311 Err(CoreError::SeqKeyEmpty)
312 );
313 }
314
315 #[test]
316 fn validate_request_ok_returns_key() {
317 let mut a = SeqAllocator::new();
318 a.become_leader(Epoch(1));
319 let k = a
320 .validate_request("orders", 10, DEFAULT_MAX_SEQ_COUNT)
321 .unwrap();
322 assert_eq!(k.as_str(), "orders");
323 }
324
325 #[test]
326 fn validate_request_accepts_max_count_exactly() {
327 let mut a = SeqAllocator::new();
328 a.become_leader(Epoch(1));
329 assert!(
330 a.validate_request("orders", DEFAULT_MAX_SEQ_COUNT, DEFAULT_MAX_SEQ_COUNT)
331 .is_ok()
332 );
333 }
334}
335
336#[cfg(test)]
337mod seqgrant_tests {
338 use super::*;
339
340 #[test]
341 fn exposes_fields_and_last() {
342 let key = SeqKey::try_new("users").unwrap();
343 let g = SeqGrant::try_new(key.clone(), 100, 5, Epoch(7)).unwrap();
344 assert_eq!(g.key().as_str(), "users");
345 assert_eq!(g.start(), 100);
346 assert_eq!(g.count(), 5);
347 assert_eq!(g.epoch(), Epoch(7));
348 assert_eq!(g.last(), 104);
350 }
351
352 #[test]
353 fn last_equals_start_when_count_is_one() {
354 let g1 = SeqGrant::try_new(SeqKey::try_new("x").unwrap(), 42, 1, Epoch(1)).unwrap();
355 assert_eq!(g1.last(), 42);
356 }
357
358 #[test]
359 fn try_new_rejects_zero_count() {
360 let key = SeqKey::try_new("x").unwrap();
361 assert_eq!(
362 SeqGrant::try_new(key, 5, 0, Epoch(1)),
363 Err(CoreError::SeqCountZero)
364 );
365 }
366
367 #[test]
368 fn try_new_rejects_block_overflow() {
369 let key = SeqKey::try_new("x").unwrap();
371 assert_eq!(
372 SeqGrant::try_new(key, u64::MAX, 2, Epoch(1)),
373 Err(CoreError::SeqBlockOverflow {
374 start: u64::MAX,
375 count: 2
376 })
377 );
378 }
379
380 #[test]
381 fn try_new_accepts_max_boundary() {
382 let key = SeqKey::try_new("x").unwrap();
384 let g = SeqGrant::try_new(key, u64::MAX - 4, 5, Epoch(1)).unwrap();
385 assert_eq!(g.last(), u64::MAX);
386 }
387}
388
389#[cfg(test)]
390mod seqkey_tests {
391 use super::*;
392
393 #[test]
394 fn accepts_normal_key() {
395 let k = SeqKey::try_new("orders").unwrap();
396 assert_eq!(k.as_str(), "orders");
397 }
398
399 #[test]
400 fn rejects_empty() {
401 assert_eq!(SeqKey::try_new(""), Err(CoreError::SeqKeyEmpty));
402 }
403
404 #[test]
405 fn accepts_max_length() {
406 let s = "a".repeat(MAX_SEQ_KEY_LEN);
407 assert!(SeqKey::try_new(&s).is_ok());
408 }
409
410 #[test]
411 fn rejects_one_past_max_length() {
412 let s = "a".repeat(MAX_SEQ_KEY_LEN + 1);
413 assert_eq!(
414 SeqKey::try_new(&s),
415 Err(CoreError::SeqKeyTooLong {
416 len: MAX_SEQ_KEY_LEN + 1,
417 max: MAX_SEQ_KEY_LEN
418 })
419 );
420 }
421
422 #[test]
423 fn length_is_measured_in_utf8_bytes_not_chars() {
424 let ok = "é".repeat(MAX_SEQ_KEY_LEN / 2);
426 assert!(SeqKey::try_new(&ok).is_ok());
427 let too_long = "é".repeat(MAX_SEQ_KEY_LEN / 2 + 1);
428 assert!(matches!(
429 SeqKey::try_new(&too_long),
430 Err(CoreError::SeqKeyTooLong { .. })
431 ));
432 }
433}