Skip to main content

txn_db/
timestamp.rs

1//! Logical timestamps that order transactions.
2//!
3//! `txn-db` is a multi-version store: every committed write is tagged with the
4//! [`Timestamp`] at which it became visible, and every reader carries the
5//! timestamp of the snapshot it is reading. A read of a key returns the newest
6//! version whose commit timestamp is less than or equal to the reader's
7//! snapshot timestamp. Ordering transactions is therefore the whole job of this
8//! type, which is why it is a distinct, totally ordered value rather than a bare
9//! integer threaded through the code.
10//!
11//! Timestamps are *logical*, not wall-clock: they come from a single
12//! monotonic counter inside the database, so they are dense, gap-free in
13//! issuance order, and never run backwards. Wall-clock time plays no part in
14//! visibility, which keeps the isolation contract independent of clock skew.
15
16use core::fmt;
17
18/// A logical timestamp marking a point in a database's commit history.
19///
20/// Timestamps are issued by the database as a strictly increasing sequence.
21/// [`Timestamp::ZERO`] is the timestamp of the empty database before any commit;
22/// a reader that begins against an empty database reads at `ZERO` and sees
23/// nothing. The first commit is stamped `1`, the next `2`, and so on.
24///
25/// The type is `Copy` and totally ordered, so comparing visibility is a single
26/// integer compare on the hot read path.
27///
28/// # Examples
29///
30/// ```
31/// use txn_db::Timestamp;
32///
33/// let t = Timestamp::ZERO;
34/// assert_eq!(t.get(), 0);
35/// assert!(Timestamp::ZERO < Timestamp::from_raw(1));
36/// ```
37#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
38pub struct Timestamp(u64);
39
40impl Timestamp {
41    /// The timestamp of the empty database, before any transaction has
42    /// committed. A snapshot taken at `ZERO` observes no keys.
43    pub const ZERO: Timestamp = Timestamp(0);
44
45    /// Wrap a raw counter value as a timestamp.
46    ///
47    /// This is the inverse of [`Timestamp::get`] and exists for tests,
48    /// serialization of the commit log, and custom
49    /// [`VersionStore`](crate::VersionStore) implementations that persist
50    /// timestamps. Application code rarely constructs timestamps directly — the
51    /// database issues them.
52    ///
53    /// # Examples
54    ///
55    /// ```
56    /// use txn_db::Timestamp;
57    ///
58    /// let ts = Timestamp::from_raw(42);
59    /// assert_eq!(ts.get(), 42);
60    /// ```
61    #[inline]
62    #[must_use]
63    pub const fn from_raw(value: u64) -> Self {
64        Timestamp(value)
65    }
66
67    /// The raw counter value behind this timestamp.
68    ///
69    /// # Examples
70    ///
71    /// ```
72    /// use txn_db::Timestamp;
73    ///
74    /// assert_eq!(Timestamp::from_raw(7).get(), 7);
75    /// ```
76    #[inline]
77    #[must_use]
78    pub const fn get(self) -> u64 {
79        self.0
80    }
81}
82
83impl fmt::Display for Timestamp {
84    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85        write!(f, "@{}", self.0)
86    }
87}
88
89#[cfg(test)]
90mod tests {
91    use super::*;
92
93    #[test]
94    fn test_zero_is_least() {
95        assert!(Timestamp::ZERO <= Timestamp::from_raw(0));
96        assert!(Timestamp::ZERO < Timestamp::from_raw(1));
97    }
98
99    #[test]
100    fn test_roundtrips_through_raw() {
101        for v in [0u64, 1, 2, u64::MAX] {
102            assert_eq!(Timestamp::from_raw(v).get(), v);
103        }
104    }
105
106    #[test]
107    fn test_ordering_matches_integer_ordering() {
108        assert!(Timestamp::from_raw(5) < Timestamp::from_raw(9));
109        assert!(Timestamp::from_raw(9) > Timestamp::from_raw(5));
110    }
111
112    #[test]
113    fn test_display_prefixes_at_sign() {
114        assert_eq!(Timestamp::from_raw(12).to_string(), "@12");
115    }
116}