uqa_storage/mvcc/serializable/
identity.rs1use super::{DatabaseId, VersionError, VersionResult};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
13pub struct SerializableTransactionId {
14 database: DatabaseId,
15 coordinator: [u8; 16],
16 allocation: u64,
17}
18
19impl SerializableTransactionId {
20 pub fn new(
22 database: DatabaseId,
23 coordinator: [u8; 16],
24 allocation: u64,
25 ) -> VersionResult<Self> {
26 validate_coordinator(coordinator)?;
27 if allocation == 0 {
28 return Err(VersionError::InvalidTransactionId);
29 }
30 Ok(Self {
31 database,
32 coordinator,
33 allocation,
34 })
35 }
36
37 pub const fn database(self) -> DatabaseId {
38 self.database
39 }
40
41 pub const fn coordinator(self) -> [u8; 16] {
42 self.coordinator
43 }
44
45 pub const fn allocation(self) -> u64 {
46 self.allocation
47 }
48}
49
50pub(super) fn validate_coordinator(coordinator: [u8; 16]) -> VersionResult<()> {
51 if coordinator == [0; 16] {
52 return Err(VersionError::InvalidEncoding(
53 "serializable coordinator identity must be nonzero",
54 ));
55 }
56 Ok(())
57}
58
59#[cfg(test)]
60mod tests {
61 use super::*;
62 use crate::{mvcc::SerializableGraph, read_control::StorageReadControl};
63
64 const DATABASE: DatabaseId = DatabaseId::from_bytes([17; 16]);
65
66 #[test]
67 fn read_only_participants_allocate_without_physical_transactions_and_never_reuse_ids() {
68 let control = StorageReadControl::with_limit(64 * 1024);
69 let mut graph = SerializableGraph::new(DATABASE, [1; 16], control.memory()).unwrap();
70 let first = graph.admit(true, &control).unwrap();
71 assert_eq!(first.allocation(), 1);
72 graph.prepare_commit(first, &control).unwrap();
73 graph.commit(first).unwrap();
74 graph.reclaim();
75 assert_eq!(control.memory().used(), 0);
76 let second = graph.admit(true, &control).unwrap();
77 assert_eq!(second.allocation(), 2);
78 assert!(matches!(
79 graph.check_active(first),
80 Err(VersionError::UnknownTransaction)
81 ));
82 graph.check_active(second).unwrap();
83 }
84
85 #[test]
86 fn a_new_coordinator_rejects_old_participants_even_when_allocations_match() {
87 let control = StorageReadControl::with_limit(64 * 1024);
88 let mut previous = SerializableGraph::new(DATABASE, [1; 16], control.memory()).unwrap();
89 let old = previous.admit(false, &control).unwrap();
90 let old_mark = previous.write_mark(old).unwrap();
91 let mut replacement = SerializableGraph::new(DATABASE, [2; 16], control.memory()).unwrap();
92 let current = replacement.admit(false, &control).unwrap();
93 assert_eq!(old.allocation(), current.allocation());
94 assert_ne!(old, current);
95 assert!(matches!(
96 replacement.check_active(old),
97 Err(VersionError::WrongSerializableCoordinator)
98 ));
99 assert!(matches!(
100 replacement.rollback(old),
101 Err(VersionError::WrongSerializableCoordinator)
102 ));
103 assert!(matches!(
104 replacement.rollback_writes(old_mark),
105 Err(VersionError::WrongSerializableCoordinator)
106 ));
107 assert!(matches!(
108 replacement.observe_rw(current, current, old, &control),
109 Err(VersionError::WrongSerializableCoordinator)
110 ));
111 replacement.check_active(current).unwrap();
112 }
113
114 #[test]
115 fn invalid_identity_or_failed_admission_cannot_consume_a_participant_number() {
116 let control = StorageReadControl::with_limit(64 * 1024);
117 assert!(matches!(
118 SerializableGraph::new(DATABASE, [0; 16], control.memory()),
119 Err(VersionError::InvalidEncoding(_))
120 ));
121 assert!(matches!(
122 SerializableTransactionId::new(DATABASE, [0; 16], 1),
123 Err(VersionError::InvalidEncoding(_))
124 ));
125 assert!(matches!(
126 SerializableTransactionId::new(DATABASE, [1; 16], 0),
127 Err(VersionError::InvalidTransactionId)
128 ));
129 let mut graph = SerializableGraph::new(DATABASE, [1; 16], control.memory()).unwrap();
130 let occupied = control.memory().reserve(control.memory().limit()).unwrap();
131 assert!(matches!(
132 graph.admit(true, &control),
133 Err(VersionError::Memory(_))
134 ));
135 drop(occupied);
136 assert_eq!(graph.admit(true, &control).unwrap().allocation(), 1);
137 }
138
139 #[test]
140 fn exhausted_participant_numbers_do_not_prevent_prepared_completion() {
141 let control = StorageReadControl::with_limit(64 * 1024);
142 let mut graph = SerializableGraph::new(DATABASE, [1; 16], control.memory()).unwrap();
143 graph.last_allocation = u64::MAX - 1;
144 let last = graph.admit(false, &control).unwrap();
145 graph.prepare_commit(last, &control).unwrap();
146 assert!(matches!(
147 graph.admit(false, &control),
148 Err(VersionError::SequenceExhausted)
149 ));
150 graph.commit(last).unwrap();
151 graph.reclaim();
152 assert!(matches!(
153 graph.admit(false, &control),
154 Err(VersionError::SequenceExhausted)
155 ));
156 assert_eq!(control.memory().used(), 0);
157 }
158}