Skip to main content

wal_db/
lsn.rs

1//! Log sequence numbers.
2
3use std::fmt;
4
5/// A log sequence number: a record's byte position in the log, assigned at
6/// append time.
7///
8/// An LSN is the byte offset where the record's frame begins. The first record
9/// appended to a fresh log is [`Lsn(0)`](Lsn); the next starts at the first
10/// record's end, and so on. LSNs are therefore **monotonic and unique but not
11/// consecutive** — each one is larger than the last by exactly the previous
12/// record's framed size. Defining the LSN as the offset is what lets the
13/// multi-writer append path stay lock-free: a single atomic reservation hands
14/// out the offset, and offset order is append order by construction, so two
15/// records can never share an LSN or land out of order.
16///
17/// A record's LSN is stable for the life of the log and is the value returned by
18/// [`Record::lsn`](crate::Record::lsn). Comparing two LSNs establishes which
19/// record came first.
20///
21/// The number is a `u64`. Even an exabyte log uses only 60 bits of it, so
22/// wraparound is not a concern this type guards against.
23///
24/// # Examples
25///
26/// ```
27/// use wal_db::Lsn;
28///
29/// // The first record always starts at offset 0.
30/// let first = Lsn::new(0);
31/// // A later record sits at a higher offset; the exact value depends on the
32/// // sizes of the records before it.
33/// let later = Lsn::new(64);
34///
35/// assert!(first < later);
36/// assert_eq!(later.get(), 64);
37/// assert_eq!(u64::from(later), 64);
38/// ```
39#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
40#[must_use]
41pub struct Lsn(u64);
42
43impl Lsn {
44    /// Construct an LSN from its raw `u64` value.
45    ///
46    /// This is rarely needed directly — [`Wal::append`](crate::Wal::append)
47    /// returns the LSN it assigned — but it is useful for comparisons and tests.
48    ///
49    /// ```
50    /// use wal_db::Lsn;
51    /// assert_eq!(Lsn::new(42).get(), 42);
52    /// ```
53    #[inline]
54    pub const fn new(value: u64) -> Self {
55        Lsn(value)
56    }
57
58    /// Return the raw `u64` value.
59    ///
60    /// ```
61    /// use wal_db::Lsn;
62    /// let lsn = Lsn::new(7);
63    /// assert_eq!(lsn.get(), 7);
64    /// ```
65    #[inline]
66    #[must_use]
67    pub const fn get(self) -> u64 {
68        self.0
69    }
70}
71
72impl fmt::Display for Lsn {
73    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74        write!(f, "{}", self.0)
75    }
76}
77
78impl From<Lsn> for u64 {
79    #[inline]
80    fn from(lsn: Lsn) -> Self {
81        lsn.0
82    }
83}
84
85#[cfg(test)]
86mod tests {
87    use super::*;
88
89    #[test]
90    fn test_lsn_get_roundtrips_value() {
91        assert_eq!(Lsn::new(0).get(), 0);
92        assert_eq!(Lsn::new(u64::MAX).get(), u64::MAX);
93    }
94
95    #[test]
96    fn test_lsn_ordering_is_numeric() {
97        assert!(Lsn::new(0) < Lsn::new(1));
98        assert!(Lsn::new(100) > Lsn::new(99));
99        assert_eq!(Lsn::new(5), Lsn::new(5));
100    }
101
102    #[test]
103    fn test_lsn_converts_to_u64() {
104        assert_eq!(u64::from(Lsn::new(123)), 123);
105    }
106
107    #[test]
108    fn test_lsn_display_is_the_number() {
109        assert_eq!(Lsn::new(42).to_string(), "42");
110    }
111}