1use std::sync::atomic::{AtomicU64, Ordering};
10
11use parking_lot::Mutex;
12use triomphe::Arc as StrongArc;
13use uqa_core::memory::{BudgetedSharedMap, BudgetedVec, MemoryBudget, MemoryReservation};
14
15use crate::read_control::StorageReadControl;
16use crate::StorageSavepointId;
17
18use super::key::RecordKey;
19use super::{PreparedRecordCommit, PreparedRecordWrite, RecordWrite, VersionError, VersionResult};
20
21mod retained;
22use retained::Sources;
23
24struct Change {
25 write: PreparedRecordWrite,
26 identity: PrivateRecordRevision,
27}
28
29type Records = BudgetedSharedMap<RecordKey, Change>;
30
31#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
33pub struct PrivateRecordRevision(u64);
34
35impl PrivateRecordRevision {
36 fn allocate() -> VersionResult<Self> {
37 static NEXT: AtomicU64 = AtomicU64::new(1);
38 NEXT.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |id| id.checked_add(1))
39 .map(Self)
40 .map_err(|_| VersionError::PrivateRevisionExhausted)
41 }
42
43 pub fn as_u64(self) -> u64 {
44 self.0
45 }
46}
47
48pub struct PrivateRecordKey {
50 key: RecordKey,
51 revision: PrivateRecordRevision,
52}
53
54impl PrivateRecordKey {
55 pub fn key(&self) -> &[u8] {
56 self.key.bytes()
57 }
58
59 pub fn revision(&self) -> PrivateRecordRevision {
60 self.revision
61 }
62}
63
64struct Savepoint {
65 id: StorageSavepointId,
66 records: Records,
67 sources: Sources,
68}
69
70struct State {
71 records: Records,
72 sources: Sources,
73 savepoints: BudgetedVec<Savepoint>,
74}
75
76impl State {
77 fn savepoint_position(&self, id: StorageSavepointId) -> VersionResult<usize> {
78 self.savepoints
79 .iter()
80 .rposition(|savepoint| savepoint.id == id)
81 .ok_or(VersionError::SavepointMissing(id))
82 }
83
84 fn truncate_savepoints(&mut self, len: usize) {
85 if len == 0 {
86 self.savepoints = BudgetedVec::new(self.records.budget());
87 } else {
88 self.savepoints.truncate(len);
89 }
90 }
91}
92
93struct Owner {
94 state: Mutex<State>,
95 memory: MemoryBudget,
96}
97
98pub struct PrivateRecordChanges {
102 owner: StrongArc<Owner>,
103}
104
105impl PrivateRecordChanges {
106 pub(super) fn share_owner(&self) -> Self {
107 Self {
108 owner: StrongArc::clone(&self.owner),
109 }
110 }
111
112 pub(super) fn write_kind(
113 &self,
114 key: &[u8],
115 control: &StorageReadControl,
116 ) -> VersionResult<Option<super::commit::RecordWriteKind>> {
117 control.check()?;
118 Ok(self
119 .owner
120 .state
121 .lock()
122 .records
123 .get(key)
124 .map(|change| change.write.kind()))
125 }
126
127 pub fn new(memory: &MemoryBudget) -> Self {
128 Self {
129 owner: StrongArc::new(Owner {
130 state: Mutex::new(State {
131 records: Records::new(memory),
132 sources: Sources::new(memory),
133 savepoints: BudgetedVec::new(memory),
134 }),
135 memory: memory.clone(),
136 }),
137 }
138 }
139
140 pub fn apply(
142 &self,
143 writes: &[RecordWrite<'_>],
144 control: &StorageReadControl,
145 ) -> VersionResult<()> {
146 let owned_control = StorageReadControl::new(&self.owner.memory, control.cancellation());
147 let prepared = PreparedRecordCommit::new(writes, &owned_control)?;
148 self.apply_owned(prepared.records(), control)
149 }
150
151 pub(crate) fn apply_owned(
152 &self,
153 writes: &[PreparedRecordWrite],
154 control: &StorageReadControl,
155 ) -> VersionResult<()> {
156 if writes.is_empty() {
157 return Ok(());
158 }
159 let mut state = self.owner.state.lock();
160 for (index, write) in writes.iter().enumerate() {
161 control.cancellation().check()?;
162 if let Some(previous) = state.records.get(write.key()) {
163 if previous.write.expected() != write.expected() {
164 return Err(VersionError::WriteConflict {
165 mutation: index,
166 expected: write.expected(),
167 actual: previous.write.expected(),
168 });
169 }
170 }
171 }
172 let identity = PrivateRecordRevision::allocate()?;
173 let invalidates_source = writes
174 .iter()
175 .any(|write| state.sources.get(write.key()).is_some_and(Option::is_some));
176 if writes.len() == 1 && !invalidates_source {
177 let write = &writes[0];
178 control.cancellation().check()?;
179 state.records.try_insert(
180 write.shared_key(),
181 Change {
182 write: write.clone(),
183 identity,
184 },
185 )?;
186 return Ok(());
187 }
188 let mut records = state.records.clone();
189 let mut sources = state.sources.clone();
190 for write in writes {
191 control.cancellation().check()?;
192 records.try_insert(
193 write.shared_key(),
194 Change {
195 write: write.clone(),
196 identity,
197 },
198 )?;
199 if sources.get(write.key()).is_some_and(Option::is_some) {
200 sources.try_insert(write.shared_key(), None)?;
201 }
202 }
203 control.cancellation().check()?;
204 state.records = records;
206 state.sources = sources;
207 Ok(())
208 }
209
210 pub fn has_written(&self) -> bool {
211 !self.owner.state.lock().records.is_empty()
212 }
213
214 pub fn snapshot(&self) -> VersionResult<PrivateRecordSnapshot> {
215 let memory = self
216 .owner
217 .memory
218 .reserve(std::mem::size_of::<PrivateRecordSnapshot>())?;
219 let state = self.owner.state.lock();
220 Ok(PrivateRecordSnapshot {
221 records: state.records.clone(),
222 sources: state.sources.clone(),
223 _memory: memory,
224 })
225 }
226
227 pub fn savepoint(&self, id: StorageSavepointId) -> VersionResult<()> {
228 let mut state = self.owner.state.lock();
229 let records = state.records.clone();
230 let sources = state.sources.clone();
231 state.savepoints.push(Savepoint {
232 id,
233 records,
234 sources,
235 })?;
236 Ok(())
237 }
238
239 pub fn release_savepoint(&self, id: StorageSavepointId) -> VersionResult<()> {
241 let mut state = self.owner.state.lock();
242 let position = state.savepoint_position(id)?;
243 state.truncate_savepoints(position);
244 Ok(())
245 }
246
247 pub fn rollback_to_savepoint(&self, id: StorageSavepointId) -> VersionResult<()> {
249 let mut state = self.owner.state.lock();
250 let position = state.savepoint_position(id)?;
251 state.records = state.savepoints[position].records.clone();
252 state.sources = state.savepoints[position].sources.clone();
253 state.truncate_savepoints(position + 1);
254 Ok(())
255 }
256
257 pub fn rollback(&self) -> VersionResult<()> {
258 let mut state = self.owner.state.lock();
259 state.records = Records::new(&self.owner.memory);
260 state.sources = Sources::new(&self.owner.memory);
261 state.truncate_savepoints(0);
262 Ok(())
263 }
264
265 pub fn prepare(&self, control: &StorageReadControl) -> VersionResult<PreparedRecordCommit> {
267 control.cancellation().check()?;
268 let state = self.owner.state.lock();
269 let mut writes = BudgetedVec::new(control.memory());
270 for (_, change) in &state.records {
271 control.cancellation().check()?;
272 writes.push(change.write.clone())?;
273 }
274 PreparedRecordCommit::from_unique_owned(writes, control)
275 }
276}
277
278pub struct PrivateRecordSnapshot {
280 records: Records,
281 sources: Sources,
282 _memory: MemoryReservation,
283}
284
285impl PrivateRecordSnapshot {
286 pub(super) fn try_clone(&self) -> VersionResult<Self> {
288 let memory = self.records.budget().reserve(std::mem::size_of::<Self>())?;
289 Ok(Self {
290 records: self.records.clone(),
291 sources: self.sources.clone(),
292 _memory: memory,
293 })
294 }
295
296 pub fn scan_keys(
298 &self,
299 prefix: &[u8],
300 after: Option<&[u8]>,
301 limit: usize,
302 control: &StorageReadControl,
303 ) -> VersionResult<BudgetedVec<PrivateRecordKey>> {
304 control.check()?;
305 let mut result = BudgetedVec::new(control.memory());
306 if limit == 0 {
307 return Ok(result);
308 }
309 let start = after.filter(|after| *after >= prefix).unwrap_or(prefix);
310 for (key, change) in self.records.range_from(std::ops::Bound::Included(start)) {
311 control.check()?;
312 let key = key.bytes();
313 if !key.starts_with(prefix) {
314 break;
315 }
316 if after.is_some_and(|after| key <= after) {
317 continue;
318 }
319 result.push(PrivateRecordKey {
320 key: change.write.shared_key(),
321 revision: change.identity,
322 })?;
323 if result.len() == limit {
324 break;
325 }
326 }
327 Ok(result)
328 }
329
330 pub(super) fn visit_merged<P: super::projection::Projection>(
331 &self,
332 committed: &dyn super::CommittedRecordSnapshot,
333 prefix: &[u8],
334 after: Option<&[u8]>,
335 limit: usize,
336 control: &StorageReadControl,
337 visit: &mut super::projection::Visitor<'_, P>,
338 ) -> VersionResult<()> {
339 control.cancellation().check()?;
340 if limit == 0 {
341 return Ok(());
342 }
343 let start = after.filter(|after| *after >= prefix).unwrap_or(prefix);
344 let mut entries = self.records.range_from(std::ops::Bound::Included(start));
345 let mut next_private = || -> VersionResult<Option<&PreparedRecordWrite>> {
346 for (key, change) in entries.by_ref() {
347 control.cancellation().check()?;
348 if !key.bytes().starts_with(prefix) {
349 return Ok(None);
350 }
351 if after.is_some_and(|after| key.bytes() <= after) {
352 continue;
353 }
354 return Ok(Some(&change.write));
355 }
356 Ok(None)
357 };
358 let mut pending = next_private()?;
359 let mut count = 0;
360 let mut running = true;
361 let mut emit = |key: &[u8], record: P::Record<'_>| -> VersionResult<bool> {
362 control.cancellation().check()?;
363 count += 1;
364 let more = visit(key, record)?;
365 control.cancellation().check()?;
366 Ok(more && count < limit)
367 };
368 P::visit(committed, prefix, after, control, &mut |key, record| {
369 while let Some(write) = pending.filter(|write| write.key() < key) {
370 running = emit(write.key(), P::private(write))?;
371 if !running {
372 return Ok(false);
373 }
374 pending = next_private()?;
375 }
376 if let Some(write) = pending.filter(|write| write.key() == key) {
377 running = emit(key, P::private(write))?;
378 pending = next_private()?;
379 } else {
380 running = emit(key, record)?;
381 }
382 Ok(running)
383 })?;
384 while running {
385 let Some(write) = pending else {
386 break;
387 };
388 running = emit(write.key(), P::private(write))?;
389 pending = next_private()?;
390 }
391 Ok(())
392 }
393
394 pub fn get(
395 &self,
396 key: &[u8],
397 control: &StorageReadControl,
398 ) -> VersionResult<Option<PreparedRecordWrite>> {
399 control.cancellation().check()?;
400 Ok(self.records.get(key).map(|change| change.write.clone()))
401 }
402
403 pub fn scan(
405 &self,
406 prefix: &[u8],
407 after: Option<&[u8]>,
408 limit: usize,
409 control: &StorageReadControl,
410 ) -> VersionResult<BudgetedVec<PreparedRecordWrite>> {
411 control.cancellation().check()?;
412 let mut result = BudgetedVec::new(control.memory());
413 if limit == 0 {
414 return Ok(result);
415 }
416 let start = after.filter(|after| *after >= prefix).unwrap_or(prefix);
417 for (key, change) in self.records.range_from(std::ops::Bound::Included(start)) {
418 control.cancellation().check()?;
419 let key = key.bytes();
420 if !key.starts_with(prefix) {
421 break;
422 }
423 if after.is_some_and(|after| key <= after) {
424 continue;
425 }
426 result.push(change.write.clone())?;
427 if result.len() == limit {
428 break;
429 }
430 }
431 Ok(result)
432 }
433}
434
435#[cfg(test)]
436mod tests {
437 use super::*;
438
439 #[test]
440 fn shared_changes_keep_one_strong_counter_until_the_final_owner_drops() {
441 let memory = MemoryBudget::new(1024);
442 let bytes = size_of::<Owner>() + size_of::<usize>();
443 let mut changes = None;
444 let allocated = allocation_counter::measure(|| {
445 changes = Some(PrivateRecordChanges::new(&memory));
446 });
447 assert_eq!(allocated.count_total, 1);
448 assert_eq!(allocated.bytes_total, bytes as u64);
449 let retained = changes.as_ref().unwrap().share_owner();
450 let shared = allocation_counter::measure(|| drop(changes));
451 assert_eq!(shared.count_total, 0);
452 assert_eq!(shared.bytes_current, 0);
453 let released = allocation_counter::measure(|| drop(retained));
454 assert_eq!(released.count_current, -1);
455 assert_eq!(released.bytes_current, -(bytes as i64));
456 }
457}