Skip to main content

lora_wal/
lsn.rs

1use std::fmt;
2
3use serde::{Deserialize, Serialize};
4
5/// Monotonic log sequence number.
6///
7/// LSN 0 is reserved for "empty / never written" so a snapshot's
8/// `wal_lsn = 0` cannot be mistaken for "I checkpointed at the very first
9/// record". Allocators advance from 1.
10///
11/// Internally an LSN is opaque: callers only rely on the total order. The
12/// representation is left as a single `u64` for now; a future change to a
13/// `(segment_id, offset)` packing is non-breaking because every consumer
14/// goes through these accessors.
15#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
16#[repr(transparent)]
17pub struct Lsn(u64);
18
19impl Lsn {
20    pub const ZERO: Lsn = Lsn(0);
21
22    pub const fn new(value: u64) -> Self {
23        Self(value)
24    }
25
26    pub const fn raw(self) -> u64 {
27        self.0
28    }
29
30    pub const fn is_zero(self) -> bool {
31        self.0 == 0
32    }
33
34    /// Returns the next LSN, or `None` if the counter is exhausted.
35    pub fn checked_next(self) -> Option<Self> {
36        self.0.checked_add(1).map(Self)
37    }
38
39    /// Returns the next LSN. Panics on `u64::MAX` — saturating would
40    /// silently violate monotonicity, and overflow at this scale means a
41    /// trillion records per second for ~580 million years.
42    pub fn next(self) -> Self {
43        self.checked_next()
44            .expect("Lsn overflowed; the WAL has been continuously running for ~580 My")
45    }
46}
47
48impl fmt::Display for Lsn {
49    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50        write!(f, "{}", self.0)
51    }
52}
53
54impl From<u64> for Lsn {
55    fn from(value: u64) -> Self {
56        Self(value)
57    }
58}
59
60impl From<Lsn> for u64 {
61    fn from(value: Lsn) -> Self {
62        value.0
63    }
64}
65
66#[cfg(test)]
67mod tests {
68    use super::*;
69
70    #[test]
71    fn next_advances_monotonically() {
72        let a = Lsn::new(7);
73        let b = a.next();
74        assert!(b > a);
75        assert_eq!(b.raw(), 8);
76    }
77
78    #[test]
79    fn zero_is_sentinel() {
80        assert!(Lsn::ZERO.is_zero());
81        assert!(!Lsn::new(1).is_zero());
82    }
83
84    #[test]
85    #[should_panic(expected = "Lsn overflowed")]
86    fn overflow_panics() {
87        let _ = Lsn::new(u64::MAX).next();
88    }
89}