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}