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 fn new(memory: &MemoryBudget) -> Self {
94 Self(Mutex::new(BudgetedVec::new(memory)))
95 }
96
97 pub fn retain(
98 self: &Arc<Self>,
99 id: SerializableTransactionId,
100 control: &StorageReadControl,
101 ) -> VersionResult<SerializableParticipant> {
102 self.retain_with(id, (), control)
103 }
104
105 pub fn retain_with<T: Send + Sync + 'static>(
107 self: &Arc<Self>,
108 id: SerializableTransactionId,
109 retained: T,
110 control: &StorageReadControl,
111 ) -> VersionResult<SerializableParticipant> {
112 control.check()?;
113 let participant = SerializableParticipant::retain_value(
114 id,
115 LocalLeaseOwner {
116 registry: Arc::clone(self),
117 _retained: retained,
118 },
119 control,
120 )?;
121 let mut entries = self.0.lock();
122 prune(&mut entries);
123 if entries.is_empty() {
124 *entries = BudgetedVec::new(control.memory());
125 }
126 let position = entries
127 .binary_search_by_key(&key(id), |entry| key(entry.id))
128 .err()
129 .ok_or(VersionError::InvalidEncoding(
130 "duplicate serializable participant lease",
131 ))?;
132 entries.reserve(1)?;
133 entries.push(LocalEntry {
134 id,
135 lease: Arc::downgrade(&participant.0),
136 })?;
137 entries[position..].rotate_right(1);
138 Ok(participant)
139 }
140
141 pub fn is_alive(&self, id: SerializableTransactionId) -> bool {
142 let entries = self.0.lock();
143 entries
144 .binary_search_by_key(&key(id), |entry| key(entry.id))
145 .is_ok_and(|position| entries[position].lease.strong_count() != 0)
146 }
147
148 pub fn reclaim(&self) {
149 prune(&mut self.0.lock());
150 }
151}
152
153fn prune(entries: &mut BudgetedVec<LocalEntry>) {
154 let mut kept = 0;
155 for index in 0..entries.len() {
156 if entries[index].lease.strong_count() != 0 {
157 entries.swap(kept, index);
158 kept += 1;
159 }
160 }
161 entries.truncate(kept);
162 if entries.is_empty() {
163 *entries = BudgetedVec::new(entries.budget());
164 }
165}
166
167fn key(id: SerializableTransactionId) -> ([u8; 16], [u8; 16], u64) {
168 (id.database().as_bytes(), id.coordinator(), id.allocation())
169}
170
171impl SerializableGraph {
172 pub fn database(&self) -> DatabaseId {
173 self.database
174 }
175 pub fn coordinator(&self) -> [u8; 16] {
176 self.coordinator
177 }
178
179 pub fn admit_with_lease(
181 &mut self,
182 read_only: bool,
183 control: &StorageReadControl,
184 retain: impl FnOnce(SerializableTransactionId) -> VersionResult<SerializableParticipant>,
185 ) -> VersionResult<SerializableParticipant> {
186 let id = self.admit(read_only, control)?;
187 let retained = retain(id).and_then(|participant| {
188 if participant.id() != id {
189 return Err(VersionError::InvalidEncoding(
190 "serializable lease identity mismatch",
191 ));
192 }
193 Ok(participant)
194 });
195 match retained {
196 Ok(participant) => {
197 let position = self.position(id)?;
198 self.transactions[position].owner = ParticipantOwner::Leased;
199 self.checkpoint_changed = true;
200 Ok(participant)
201 }
202 Err(error) => {
203 self.rollback(id)?;
204 Err(error)
205 }
206 }
207 }
208
209 pub fn recover_abandoned(
211 &mut self,
212 control: &StorageReadControl,
213 mut is_alive: impl FnMut(SerializableTransactionId) -> VersionResult<bool>,
214 mut finish: impl FnMut(StorageTransactionId) -> VersionResult<CommitStatus>,
215 ) -> VersionResult<()> {
216 for index in 0..self.transactions.len() {
217 control.check()?;
218 let entry = self.transactions[index];
219 if entry.owner != ParticipantOwner::Leased {
220 continue;
221 }
222 let participant =
223 SerializableTransactionId::new(self.database, self.coordinator, entry.id)?;
224 if is_alive(participant)? {
225 continue;
226 }
227 if !entry.live() {
228 self.transactions[index].owner = ParticipantOwner::Manual;
229 self.checkpoint_changed = true;
230 continue;
231 }
232 let Some(publication) = self.publication(participant)? else {
233 self.rollback(participant)?;
234 self.transactions[index].owner = ParticipantOwner::Manual;
235 self.checkpoint_changed = true;
236 continue;
237 };
238 let status = match finish(publication.transaction()) {
239 Ok(status) => status,
240 Err(VersionError::AlreadyCommitted(receipt)) => CommitStatus::Committed(receipt),
241 Err(VersionError::AlreadyAborted(transaction))
242 if transaction == publication.transaction() =>
243 {
244 CommitStatus::Aborted
245 }
246 Err(error) => return Err(error),
247 };
248 match self.resolve_publication(publication, status)? {
249 CommitStatus::Unknown => return Err(VersionError::UnknownTransaction),
250 CommitStatus::Pending | CommitStatus::Committed(_) | CommitStatus::Aborted => {}
251 }
252 if !self.transactions[index].live() {
253 self.transactions[index].owner = ParticipantOwner::Manual;
254 self.checkpoint_changed = true;
255 }
256 }
257 Ok(())
258 }
259}
260
261#[cfg(test)]
262mod tests;