Skip to main content

lock_db/
id.rs

1//! Opaque identifiers for transactions and lockable resources.
2//!
3//! Both identifiers are thin newtypes over `u64`. lock-db does not assign or
4//! interpret them: the transaction layer decides what a transaction id means,
5//! and the storage layer decides how to map a database, table, page, or row to
6//! a single [`ResourceId`]. Keeping them opaque integers means the lock table
7//! never owns variable-length keys, so a lookup is a hash of one machine word
8//! with no allocation on the hot path.
9
10/// Identifies the transaction that owns a lock request.
11///
12/// The lock manager uses this only for equality and hashing: it tracks which
13/// requests belong together so a transaction can re-acquire, upgrade, or
14/// release its own locks. Reusing a retired id for a new transaction is safe as
15/// long as the previous transaction has released everything first.
16///
17/// # Examples
18///
19/// ```
20/// use lock_db::TxnId;
21///
22/// let t = TxnId::new(42);
23/// assert_eq!(t.get(), 42);
24/// assert_eq!(TxnId::from(42), t);
25/// ```
26#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
27#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
28#[repr(transparent)]
29pub struct TxnId(u64);
30
31impl TxnId {
32    /// Wraps a raw transaction number.
33    #[inline]
34    #[must_use]
35    pub const fn new(id: u64) -> Self {
36        Self(id)
37    }
38
39    /// Returns the underlying number.
40    #[inline]
41    #[must_use]
42    pub const fn get(self) -> u64 {
43        self.0
44    }
45}
46
47impl From<u64> for TxnId {
48    #[inline]
49    fn from(id: u64) -> Self {
50        Self(id)
51    }
52}
53
54impl From<TxnId> for u64 {
55    #[inline]
56    fn from(id: TxnId) -> Self {
57        id.0
58    }
59}
60
61/// Identifies a lockable resource.
62///
63/// A resource is whatever the caller decides to protect with a lock: an entire
64/// database, a table, a page, or a single row. The caller is responsible for
65/// mapping its own object identity to a stable, collision-free `u64`. Two
66/// distinct resources that map to the same id will share a lock queue, which is
67/// a correctness bug in the caller's id scheme, not in lock-db.
68///
69/// # Examples
70///
71/// ```
72/// use lock_db::ResourceId;
73///
74/// let page = ResourceId::new(0xDEAD_BEEF);
75/// assert_eq!(page.get(), 0xDEAD_BEEF);
76/// ```
77#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
78#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
79#[repr(transparent)]
80pub struct ResourceId(u64);
81
82impl ResourceId {
83    /// Wraps a raw resource number.
84    #[inline]
85    #[must_use]
86    pub const fn new(id: u64) -> Self {
87        Self(id)
88    }
89
90    /// Returns the underlying number.
91    #[inline]
92    #[must_use]
93    pub const fn get(self) -> u64 {
94        self.0
95    }
96}
97
98impl From<u64> for ResourceId {
99    #[inline]
100    fn from(id: u64) -> Self {
101        Self(id)
102    }
103}
104
105impl From<ResourceId> for u64 {
106    #[inline]
107    fn from(id: ResourceId) -> Self {
108        id.0
109    }
110}
111
112#[cfg(test)]
113mod tests {
114    use super::{ResourceId, TxnId};
115
116    #[test]
117    fn test_txn_roundtrip_through_u64() {
118        let t = TxnId::new(u64::MAX);
119        assert_eq!(t.get(), u64::MAX);
120        assert_eq!(u64::from(t), u64::MAX);
121        assert_eq!(TxnId::from(7), TxnId::new(7));
122    }
123
124    #[test]
125    fn test_resource_roundtrip_through_u64() {
126        let r = ResourceId::new(0);
127        assert_eq!(r.get(), 0);
128        assert_eq!(u64::from(r), 0);
129        assert_eq!(ResourceId::from(7), ResourceId::new(7));
130    }
131
132    #[test]
133    fn test_distinct_ids_are_unequal() {
134        assert_ne!(TxnId::new(1), TxnId::new(2));
135        assert_ne!(ResourceId::new(1), ResourceId::new(2));
136    }
137
138    #[test]
139    fn test_ids_are_ordered_by_value() {
140        assert!(TxnId::new(1) < TxnId::new(2));
141        assert!(ResourceId::new(1) < ResourceId::new(2));
142    }
143}