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}