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