uqa_storage/mvcc/serializable/
liveness.rs1use std::sync::{Arc, Weak};
10
11use parking_lot::Mutex;
12use uqa_core::memory::{BudgetedVec, MemoryBudget, MemoryReservation};
13
14use super::{
15 DatabaseId, ParticipantOwner, SerializableGraph, SerializableTransactionId, VersionError,
16 VersionResult,
17};
18use crate::{
19 mvcc::{CommitStatus, StorageTransactionId},
20 read_control::StorageReadControl,
21};
22
23struct Participant<T> {
24 id: SerializableTransactionId,
25 _retained: T,
26 _memory: MemoryReservation,
27}
28
29trait ParticipantLease: Send + Sync {
30 fn id(&self) -> SerializableTransactionId;
31}
32
33impl<T: Send + Sync> ParticipantLease for Participant<T> {
34 fn id(&self) -> SerializableTransactionId {
35 self.id
36 }
37}
38
39#[derive(Clone)]
41pub struct SerializableParticipant(Arc<dyn ParticipantLease>);
42
43impl SerializableParticipant {
44 pub fn retain(
45 id: SerializableTransactionId,
46 lease: Box<dyn Send + Sync>,
47 control: &StorageReadControl,
48 ) -> VersionResult<Self> {
49 Self::retain_value(id, lease, control)
50 }
51
52 fn retain_value<T: Send + Sync + 'static>(
53 id: SerializableTransactionId,
54 lease: T,
55 control: &StorageReadControl,
56 ) -> VersionResult<Self> {
57 let memory = control
58 .memory()
59 .reserve(std::mem::size_of::<Participant<T>>() + 2 * std::mem::size_of::<usize>())?;
60 Ok(Self(Arc::new(Participant {
61 id,
62 _retained: lease,
63 _memory: memory,
64 })))
65 }
66
67 pub fn id(&self) -> SerializableTransactionId {
68 self.0.id()
69 }
70}
71
72struct LocalEntry {
73 id: SerializableTransactionId,
74 lease: Weak<dyn ParticipantLease>,
75}
76
77struct LocalLeaseOwner<T> {
78 registry: Arc<LocalSerializableLeases>,
79 _retained: T,
80}
81
82impl<T> Drop for LocalLeaseOwner<T> {
83 fn drop(&mut self) {
84 self.registry.reclaim();
86 }
87}
88
89pub struct LocalSerializableLeases(Mutex<BudgetedVec<LocalEntry>>);
91
92impl LocalSerializableLeases {
93 pub(super) fn shared(&self, id: SerializableTransactionId) -> Option<SerializableParticipant> {
94 let entries = self.0.lock();
95 let position = entries
96 .binary_search_by_key(&key(id), |entry| key(entry.id))
97 .ok()?;
98 entries[position]
99 .lease
100 .upgrade()
101 .map(SerializableParticipant)
102 }
103
104 pub fn new(memory: &MemoryBudget) -> Self {
105 Self(Mutex::new(BudgetedVec::new(memory)))
106 }
107
108 pub fn retain(
109 self: &Arc<Self>,
110 id: SerializableTransactionId,
111 control: &StorageReadControl,
112 ) -> VersionResult<SerializableParticipant> {
113 self.retain_with(id, (), control)
114 }
115
116 pub fn retain_with<T: Send + Sync + 'static>(
118 self: &Arc<Self>,
119 id: SerializableTransactionId,
120 retained: T,
121 control: &StorageReadControl,
122 ) -> VersionResult<SerializableParticipant> {
123 control.check()?;
124 let participant = SerializableParticipant::retain_value(
125 id,
126 LocalLeaseOwner {
127 registry: Arc::clone(self),
128 _retained: retained,
129 },
130 control,
131 )?;
132 let mut entries = self.0.lock();
133 prune(&mut entries);
134 if entries.is_empty() {
135 *entries = BudgetedVec::new(control.memory());
136 }
137 let position = entries
138 .binary_search_by_key(&key(id), |entry| key(entry.id))
139 .err()
140 .ok_or(VersionError::InvalidEncoding(
141 "duplicate serializable participant lease",
142 ))?;
143 entries.reserve(1)?;
144 entries.push(LocalEntry {
145 id,
146 lease: Arc::downgrade(&participant.0),
147 })?;
148 entries[position..].rotate_right(1);
149 Ok(participant)
150 }
151
152 pub fn is_alive(&self, id: SerializableTransactionId) -> bool {
153 let entries = self.0.lock();
154 entries
155 .binary_search_by_key(&key(id), |entry| key(entry.id))
156 .is_ok_and(|position| entries[position].lease.strong_count() != 0)
157 }
158
159 pub fn reclaim(&self) {
160 prune(&mut self.0.lock());
161 }
162}
163
164fn prune(entries: &mut BudgetedVec<LocalEntry>) {
165 let mut kept = 0;
166 for index in 0..entries.len() {
167 if entries[index].lease.strong_count() != 0 {
168 entries.swap(kept, index);
169 kept += 1;
170 }
171 }
172 entries.truncate(kept);
173 if entries.is_empty() {
174 *entries = BudgetedVec::new(entries.budget());
175 }
176}
177
178fn key(id: SerializableTransactionId) -> ([u8; 16], [u8; 16], u64) {
179 (id.database().as_bytes(), id.coordinator(), id.allocation())
180}
181
182impl SerializableGraph {
183 pub fn database(&self) -> DatabaseId {
184 self.database
185 }
186 pub fn coordinator(&self) -> [u8; 16] {
187 self.coordinator
188 }
189
190 pub fn admit_with_lease(
192 &mut self,
193 read_only: bool,
194 control: &StorageReadControl,
195 retain: impl FnOnce(SerializableTransactionId) -> VersionResult<SerializableParticipant>,
196 ) -> VersionResult<SerializableParticipant> {
197 let id = self.admit(read_only, control)?;
198 let retained = retain(id).and_then(|participant| {
199 if participant.id() != id {
200 return Err(VersionError::InvalidEncoding(
201 "serializable lease identity mismatch",
202 ));
203 }
204 Ok(participant)
205 });
206 match retained {
207 Ok(participant) => {
208 let position = self.position(id)?;
209 self.transactions[position].owner = ParticipantOwner::Leased;
210 self.checkpoint_changed = true;
211 Ok(participant)
212 }
213 Err(error) => {
214 self.rollback(id)?;
215 Err(error)
216 }
217 }
218 }
219
220 pub fn recover_abandoned(
222 &mut self,
223 control: &StorageReadControl,
224 mut is_alive: impl FnMut(SerializableTransactionId) -> VersionResult<bool>,
225 mut finish: impl FnMut(StorageTransactionId) -> VersionResult<CommitStatus>,
226 ) -> VersionResult<()> {
227 for index in 0..self.transactions.len() {
228 control.check()?;
229 let entry = self.transactions[index];
230 if entry.owner != ParticipantOwner::Leased {
231 continue;
232 }
233 let participant =
234 SerializableTransactionId::new(self.database, self.coordinator, entry.id)?;
235 if is_alive(participant)? {
236 continue;
237 }
238 if !entry.live() {
239 self.transactions[index].owner = ParticipantOwner::Manual;
240 self.checkpoint_changed = true;
241 continue;
242 }
243 let Some(publication) = self.publication(participant)? else {
244 self.rollback(participant)?;
245 self.transactions[index].owner = ParticipantOwner::Manual;
246 self.checkpoint_changed = true;
247 continue;
248 };
249 let status = match finish(publication.transaction()) {
250 Ok(status) => status,
251 Err(VersionError::AlreadyCommitted(receipt)) => CommitStatus::Committed(receipt),
252 Err(VersionError::AlreadyAborted(transaction))
253 if transaction == publication.transaction() =>
254 {
255 CommitStatus::Aborted
256 }
257 Err(error) => return Err(error),
258 };
259 match self.resolve_publication(publication, status)? {
260 CommitStatus::Unknown => return Err(VersionError::UnknownTransaction),
261 CommitStatus::Pending | CommitStatus::Committed(_) | CommitStatus::Aborted => {}
262 }
263 if !self.transactions[index].live() {
264 self.transactions[index].owner = ParticipantOwner::Manual;
265 self.checkpoint_changed = true;
266 }
267 }
268 Ok(())
269 }
270}
271
272#[cfg(test)]
273mod tests;