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 cursor;
23pub use cursor::{PrivateRecordCursor, PrivateRecordEntry};
24mod selection;
25pub(super) use selection::PrivateRecordSelection;
26mod retained;
27mod scopes;
28pub use scopes::PrivateRevisionScope;
29use scopes::RevisionScopes;
30pub(in crate::mvcc) mod run;
31mod run_set;
32mod tiers;
33use retained::{RetainedSources, Sources};
34use run::SpilledRunWriter;
35use run_set::RunSet;
36use tiers::{TieredChange, TieredCursor};
37
38const CHANGE_OVERHEAD: usize = 192;
40const PRESSURE_DIVISOR: usize = 2;
42const RESIDENT_DIVISOR: usize = 16;
44
45struct Change {
46 write: PreparedRecordWrite,
47 identity: PrivateRecordRevision,
48}
49
50type Records = BudgetedSharedMap<RecordKey, Change>;
51
52#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
54pub struct PrivateRecordRevision(NonZeroU64);
55
56impl PrivateRecordRevision {
57 pub(in crate::mvcc) fn allocate() -> VersionResult<Self> {
58 static NEXT: AtomicU64 = AtomicU64::new(1);
59 uqa_core::atomic::try_update_u64(&NEXT, Ordering::Relaxed, Ordering::Relaxed, |id| {
60 id.checked_add(1)
61 })
62 .map(|id| Self(NonZeroU64::new(id).expect("private revisions start at one")))
63 .map_err(|_| VersionError::PrivateRevisionExhausted)
64 }
65
66 #[cfg(test)]
67 pub(crate) fn for_tests() -> Self {
68 Self::allocate().expect("test private revision")
69 }
70
71 pub fn as_u64(self) -> u64 {
72 self.0.get()
73 }
74
75 fn from_u64(value: u64) -> VersionResult<Self> {
77 NonZeroU64::new(value)
78 .map(Self)
79 .ok_or(VersionError::InvalidEncoding(
80 "zero private record revision",
81 ))
82 }
83}
84
85pub struct PrivateRecordKey {
87 key: RecordKey,
88 revision: PrivateRecordRevision,
89}
90
91impl PrivateRecordKey {
92 pub fn key(&self) -> &[u8] {
93 self.key.bytes()
94 }
95
96 pub fn revision(&self) -> PrivateRecordRevision {
97 self.revision
98 }
99}
100
101struct Savepoint {
102 id: StorageSavepointId,
103 records: Records,
104 resident: usize,
105 runs: RunSet,
106 sources: RetainedSources,
107 scopes: Option<RevisionScopes>,
108 revision: Option<PrivateRecordRevision>,
109}
110
111struct State {
113 records: Records,
114 resident: usize,
116 runs: RunSet,
117 sources: Sources,
118 scopes: Option<RevisionScopes>,
119 revision: Option<PrivateRecordRevision>,
120 savepoints: BudgetedVec<Savepoint>,
121}
122
123impl State {
124 fn change(
126 &self,
127 key: &[u8],
128 control: &StorageReadControl,
129 ) -> VersionResult<Option<TieredChange<'_>>> {
130 tiers::lookup(&self.records, &self.runs, key, control)
131 }
132
133 fn least_spill(&self) -> usize {
135 self.records.budget().limit() / RESIDENT_DIVISOR
136 }
137
138 fn make_room(&mut self, control: &StorageReadControl) -> VersionResult<()> {
140 let budget = self.records.budget();
141 if self.records.is_empty()
142 || self.resident < self.least_spill()
143 || budget.used() <= budget.limit() / PRESSURE_DIVISOR
144 {
145 return Ok(());
146 }
147 let memory = self.records.budget().clone();
148 let mut writer =
149 SpilledRunWriter::new(self.records.len() as u64, self.resident as u64, &memory)?;
150 for (_, change) in &self.records {
151 writer.push(
152 change.write.key(),
153 change.write.expected(),
154 change.write.kind(),
155 change.identity,
156 change.write.value(),
157 control,
158 )?;
159 }
160 let run = writer
161 .finish()?
162 .expect("a nonempty memory tier spills a nonempty run");
163 let base = self.least_spill() as u64;
164 self.runs = self.runs.with_run(run, base, &memory, control)?;
165 self.records = Records::new(&memory);
166 self.resident = 0;
167 Ok(())
168 }
169
170 fn savepoint_position(&self, id: StorageSavepointId) -> VersionResult<usize> {
171 self.savepoints
172 .iter()
173 .rposition(|savepoint| savepoint.id == id)
174 .ok_or(VersionError::SavepointMissing(id))
175 }
176
177 fn truncate_savepoints(&mut self, len: usize) {
178 if len == 0 {
179 self.savepoints = BudgetedVec::new(self.records.budget());
180 } else {
181 self.savepoints.truncate(len);
182 }
183 }
184}
185
186struct Owner {
187 state: Mutex<State>,
188}
189
190pub struct PrivateRecordChanges {
194 owner: StrongArc<Owner>,
195}
196
197impl PrivateRecordChanges {
198 pub fn fork(&self) -> Self {
200 let state = self.owner.state.lock();
201 Self {
202 owner: StrongArc::new(Owner {
203 state: Mutex::new(State {
204 records: state.records.clone(),
205 resident: state.resident,
206 runs: state.runs.clone(),
207 sources: state.sources.clone(),
208 scopes: state.scopes.as_ref().map(RevisionScopes::fork),
209 revision: state.revision,
210 savepoints: BudgetedVec::new(state.records.budget()),
211 }),
212 }),
213 }
214 }
215
216 pub(super) fn share_owner(&self) -> Self {
217 Self {
218 owner: StrongArc::clone(&self.owner),
219 }
220 }
221
222 pub(super) fn write_kind(
223 &self,
224 key: &[u8],
225 control: &StorageReadControl,
226 ) -> VersionResult<Option<super::commit::RecordWriteKind>> {
227 control.check()?;
228 let state = self.owner.state.lock();
229 Ok(state.change(key, control)?.map(|change| change.kind()))
230 }
231
232 pub fn new(memory: &MemoryBudget) -> Self {
233 Self::with_revision_scope(memory, None)
234 }
235
236 pub(in crate::mvcc) fn with_revision_scope(
237 memory: &MemoryBudget,
238 scope: Option<PrivateRevisionScope>,
239 ) -> Self {
240 Self {
241 owner: StrongArc::new(Owner {
242 state: Mutex::new(State {
243 records: Records::new(memory),
244 resident: 0,
245 runs: RunSet::empty(),
246 sources: Sources::new(memory),
247 scopes: scope.map(|scope| RevisionScopes::new(memory, scope)),
248 revision: None,
249 savepoints: BudgetedVec::new(memory),
250 }),
251 }),
252 }
253 }
254
255 pub fn apply(
257 &self,
258 writes: &[RecordWrite<'_>],
259 control: &StorageReadControl,
260 ) -> VersionResult<()> {
261 let memory = self.owner.state.lock().records.budget().clone();
262 let owned_control = StorageReadControl::new(&memory, control.cancellation());
263 let prepared = PreparedRecordCommit::new(writes, &owned_control)?;
264 let writes = prepared.resident().ok_or(VersionError::InvalidEncoding(
265 "a supplied batch is in memory",
266 ))?;
267 self.apply_owned(writes, control)
268 }
269
270 pub(crate) fn apply_owned(
271 &self,
272 writes: &[PreparedRecordWrite],
273 control: &StorageReadControl,
274 ) -> VersionResult<()> {
275 if writes.is_empty() {
276 return Ok(());
277 }
278 let mut state = self.owner.state.lock();
279 for (index, write) in writes.iter().enumerate() {
280 control.cancellation().check()?;
281 if let Some(previous) = state.change(write.key(), control)? {
282 if previous.expected() != write.expected() {
283 return Err(VersionError::WriteConflict {
284 mutation: index,
285 expected: write.expected(),
286 actual: previous.expected(),
287 });
288 }
289 }
290 }
291 state.make_room(control)?;
292 let identity = PrivateRecordRevision::allocate()?;
293 let scopes = state
294 .scopes
295 .as_ref()
296 .map(|scopes| scopes.with_writes(writes, identity, control))
297 .transpose()?;
298 let invalidates_source = writes
299 .iter()
300 .any(|write| state.sources.get(write.key()).is_some_and(Option::is_some));
301 if writes.len() == 1 && !invalidates_source {
302 let write = &writes[0];
303 control.cancellation().check()?;
304 let resident = replaced_resident(&state.records, state.resident, write);
305 state.records.try_insert(
306 write.shared_key(),
307 Change {
308 write: write.clone(),
309 identity,
310 },
311 )?;
312 state.resident = resident;
313 state.scopes = scopes;
314 state.revision = Some(identity);
315 return Ok(());
316 }
317 let mut records = state.records.clone();
318 let mut sources = state.sources.clone();
319 let mut resident = state.resident;
320 for write in writes {
321 control.cancellation().check()?;
322 resident = replaced_resident(&records, resident, write);
323 records.try_insert(
324 write.shared_key(),
325 Change {
326 write: write.clone(),
327 identity,
328 },
329 )?;
330 if sources.get(write.key()).is_some_and(Option::is_some) {
331 sources.try_insert(write.shared_key(), None)?;
332 }
333 }
334 control.cancellation().check()?;
335 state.records = records;
337 state.sources = sources;
338 state.scopes = scopes;
339 state.resident = resident;
340 state.revision = Some(identity);
341 Ok(())
342 }
343
344 pub(in crate::mvcc) fn apply_prepared(
346 &self,
347 prepared: &PreparedRecordCommit,
348 control: &StorageReadControl,
349 ) -> VersionResult<()> {
350 let mut writes = prepared.writes();
351 while let Some(write) = writes.next(control)? {
352 self.apply_owned(std::slice::from_ref(&write), control)?;
353 }
354 Ok(())
355 }
356
357 pub fn has_written(&self) -> bool {
358 let state = self.owner.state.lock();
359 !state.records.is_empty() || !state.runs.is_empty()
360 }
361
362 pub fn snapshot(&self) -> VersionResult<PrivateRecordSnapshot> {
363 let state = self.owner.state.lock();
364 let memory = state
365 .records
366 .budget()
367 .reserve(std::mem::size_of::<PrivateRecordSnapshot>())?;
368 Ok(PrivateRecordSnapshot {
369 records: state.records.clone(),
370 runs: state.runs.clone(),
371 sources: state.sources.snapshot(),
372 scopes: state
373 .scopes
374 .as_ref()
375 .map(RevisionScopes::snapshot)
376 .transpose()?,
377 revision: state.revision,
378 _memory: memory,
379 })
380 }
381
382 pub fn savepoint(&self, id: StorageSavepointId) -> VersionResult<()> {
383 let mut state = self.owner.state.lock();
384 let records = state.records.clone();
385 let resident = state.resident;
386 let runs = state.runs.clone();
387 let sources = state.sources.snapshot();
388 let scopes = state.scopes.as_ref().map(RevisionScopes::fork);
389 let revision = state.revision;
390 state.savepoints.push(Savepoint {
391 id,
392 records,
393 resident,
394 runs,
395 sources,
396 scopes,
397 revision,
398 })?;
399 Ok(())
400 }
401
402 pub fn release_savepoint(&self, id: StorageSavepointId) -> VersionResult<()> {
404 let mut state = self.owner.state.lock();
405 let position = state.savepoint_position(id)?;
406 state.truncate_savepoints(position);
407 Ok(())
408 }
409
410 pub fn rollback_to_savepoint(&self, id: StorageSavepointId) -> VersionResult<()> {
412 let mut state = self.owner.state.lock();
413 let position = state.savepoint_position(id)?;
414 state.records = state.savepoints[position].records.clone();
415 state.resident = state.savepoints[position].resident;
416 state.runs = state.savepoints[position].runs.clone();
417 let sources = state.savepoints[position].sources.clone();
418 state.sources.restore(&sources);
419 state.scopes = state.savepoints[position]
420 .scopes
421 .as_ref()
422 .map(RevisionScopes::fork);
423 state.revision = state.savepoints[position].revision;
424 state.truncate_savepoints(position + 1);
425 Ok(())
426 }
427
428 pub fn rollback(&self) -> VersionResult<()> {
429 let mut state = self.owner.state.lock();
430 let memory = state.records.budget().clone();
431 state.records = Records::new(&memory);
432 state.resident = 0;
433 state.runs = RunSet::empty();
434 state.sources = Sources::new(&memory);
435 state.scopes = state.scopes.as_ref().map(RevisionScopes::empty);
436 state.revision = None;
437 state.truncate_savepoints(0);
438 Ok(())
439 }
440
441 pub fn prepare(&self, control: &StorageReadControl) -> VersionResult<PreparedRecordCommit> {
443 control.cancellation().check()?;
444 let state = self.owner.state.lock();
445 let mut changes = TieredCursor::new(
446 Some(&state.records),
447 &state.runs,
448 std::ops::Bound::Unbounded,
449 control,
450 )?;
451 if state.runs.is_empty() {
452 let mut writes = BudgetedVec::new(control.memory());
453 writes.reserve(state.records.len())?;
454 while let Some(change) = changes.next(control)? {
455 writes.push(change.write(control)?)?;
456 }
457 return PreparedRecordCommit::from_unique_owned(writes, control);
458 }
459 let (entries, entry_bytes) = state.runs.size();
460 let mut writer = SpilledRunWriter::new(
461 entries.saturating_add(state.records.len() as u64),
462 entry_bytes.saturating_add(state.resident as u64),
463 control.memory(),
464 )?;
465 while let Some(change) = changes.next(control)? {
466 match &change {
467 TieredChange::Resident(resident) => writer.push(
468 resident.write.key(),
469 resident.write.expected(),
470 resident.write.kind(),
471 resident.identity,
472 resident.write.value(),
473 control,
474 )?,
475 TieredChange::Spilled { entry, run } => writer.push_spilled(entry, run, control)?,
476 }
477 }
478 let run = writer.finish()?.expect("a spilled transaction has changes");
479 PreparedRecordCommit::from_spilled_run(run, control)
480 }
481}
482
483pub struct PrivateRecordSnapshot {
485 records: Records,
486 runs: RunSet,
487 sources: RetainedSources,
488 scopes: Option<Box<PrivateRecordSnapshot>>,
489 revision: Option<PrivateRecordRevision>,
490 _memory: MemoryReservation,
491}
492
493impl PrivateRecordSnapshot {
494 pub fn revision(&self) -> Option<PrivateRecordRevision> {
496 self.revision
497 }
498
499 pub(super) fn last_before(
500 &self,
501 prefix: &[u8],
502 before: Option<&[u8]>,
503 control: &StorageReadControl,
504 ) -> VersionResult<Option<PreparedRecordWrite>> {
505 control.check()?;
506 let mut upper = BudgetedVec::new(control.memory());
507 upper.extend_from_slice(prefix)?;
508 let upper = match upper.iter().rposition(|byte| *byte != u8::MAX) {
509 Some(last) => {
510 upper[last] += 1;
511 upper.truncate(last + 1);
512 Some(upper)
513 }
514 None => None,
515 };
516 let end = match (before, upper.as_deref()) {
517 (Some(before), Some(upper)) => Some(before.min(upper)),
518 (before, upper) => before.or(upper),
519 };
520 let bound = end.map_or(std::ops::Bound::Unbounded, std::ops::Bound::Excluded);
521 let resident = self
522 .records
523 .last_before::<[u8]>(bound)
524 .map(|(_, change)| TieredChange::Resident(change));
525 let spilled = self.runs.last_before(bound, control)?;
526 let last = match (resident, spilled) {
528 (Some(resident), Some(spilled)) if spilled.key() > resident.key() => Some(spilled),
529 (Some(resident), _) => Some(resident),
530 (None, spilled) => spilled,
531 };
532 last.filter(|change| change.key().starts_with(prefix))
533 .map(|change| change.write(control))
534 .transpose()
535 }
536
537 pub(super) fn try_clone(&self) -> VersionResult<Self> {
539 let memory = self.records.budget().reserve(std::mem::size_of::<Self>())?;
540 Ok(Self {
541 records: self.records.clone(),
542 runs: self.runs.clone(),
543 sources: self.sources.clone(),
544 scopes: self
545 .scopes
546 .as_ref()
547 .map(|scopes| scopes.try_clone().map(Box::new))
548 .transpose()?,
549 revision: self.revision,
550 _memory: memory,
551 })
552 }
553
554 pub fn scan_keys(
556 &self,
557 prefix: &[u8],
558 after: Option<&[u8]>,
559 limit: usize,
560 control: &StorageReadControl,
561 ) -> VersionResult<BudgetedVec<PrivateRecordKey>> {
562 control.check()?;
563 let mut result = BudgetedVec::new(control.memory());
564 if limit == 0 {
565 return Ok(result);
566 }
567 let start = after.filter(|after| *after >= prefix).unwrap_or(prefix);
568 let mut changes = TieredCursor::new(
569 Some(&self.records),
570 &self.runs,
571 std::ops::Bound::Included(start),
572 control,
573 )?;
574 while let Some(change) = changes.next(control)? {
575 control.check()?;
576 let key = change.key();
577 if !key.starts_with(prefix) {
578 break;
579 }
580 if after.is_some_and(|after| key <= after) {
581 continue;
582 }
583 result.push(PrivateRecordKey {
584 key: change.shared_key(),
585 revision: change.identity(),
586 })?;
587 if result.len() == limit {
588 break;
589 }
590 }
591 Ok(result)
592 }
593
594 pub(super) fn visit_merged<P: super::projection::Projection>(
595 &self,
596 committed: &dyn super::CommittedRecordSnapshot,
597 prefix: &[u8],
598 after: Option<&[u8]>,
599 limit: usize,
600 control: &StorageReadControl,
601 visit: &mut super::projection::Visitor<'_, P>,
602 ) -> VersionResult<()> {
603 control.cancellation().check()?;
604 if limit == 0 {
605 return Ok(());
606 }
607 let mut changes = self.cursor(prefix, after, control)?;
608 let mut pending = changes.next(control)?;
609 let mut count = 0;
610 let mut running = true;
611 let mut emit = |key: &[u8], record: P::Record<'_>| -> VersionResult<bool> {
612 control.cancellation().check()?;
613 count += 1;
614 let more = visit(key, record)?;
615 control.cancellation().check()?;
616 Ok(more && count < limit)
617 };
618 P::visit(committed, prefix, after, control, &mut |key, record| {
619 while let Some(entry) = pending.as_ref().filter(|entry| entry.key() < key) {
620 running = P::private(entry, control, &mut emit)?;
621 if !running {
622 return Ok(false);
623 }
624 pending = changes.next(control)?;
625 }
626 if let Some(entry) = pending.as_ref().filter(|entry| entry.key() == key) {
627 running = P::private(entry, control, &mut emit)?;
628 if running {
629 pending = changes.next(control)?;
630 }
631 } else {
632 running = emit(key, record)?;
633 }
634 Ok(running)
635 })?;
636 while running {
637 let Some(entry) = pending.as_ref() else {
638 break;
639 };
640 running = P::private(entry, control, &mut emit)?;
641 if running {
642 pending = changes.next(control)?;
643 }
644 }
645 Ok(())
646 }
647
648 pub(super) fn metadata(
650 &self,
651 key: &[u8],
652 control: &StorageReadControl,
653 ) -> VersionResult<Option<super::RecordMetadata>> {
654 control.cancellation().check()?;
655 Ok(tiers::lookup(&self.records, &self.runs, key, control)?.map(|change| change.metadata()))
656 }
657
658 pub fn get(
659 &self,
660 key: &[u8],
661 control: &StorageReadControl,
662 ) -> VersionResult<Option<PreparedRecordWrite>> {
663 control.cancellation().check()?;
664 tiers::lookup(&self.records, &self.runs, key, control)?
665 .map(|change| change.write(control))
666 .transpose()
667 }
668
669 pub fn visit(
671 &self,
672 prefix: &[u8],
673 after: Option<&[u8]>,
674 control: &StorageReadControl,
675 visit: &mut dyn FnMut(&PreparedRecordWrite) -> VersionResult<bool>,
676 ) -> VersionResult<()> {
677 control.cancellation().check()?;
678 let start = after.filter(|after| *after >= prefix).unwrap_or(prefix);
679 let mut changes = TieredCursor::new(
680 Some(&self.records),
681 &self.runs,
682 std::ops::Bound::Included(start),
683 control,
684 )?;
685 while let Some(change) = changes.next(control)? {
686 control.cancellation().check()?;
687 let key = change.key();
688 if !key.starts_with(prefix) {
689 break;
690 }
691 if after.is_some_and(|after| key <= after) {
692 continue;
693 }
694 if !visit(&change.write(control)?)? {
695 break;
696 }
697 }
698 Ok(())
699 }
700
701 pub fn scan(
703 &self,
704 prefix: &[u8],
705 after: Option<&[u8]>,
706 limit: usize,
707 control: &StorageReadControl,
708 ) -> VersionResult<BudgetedVec<PreparedRecordWrite>> {
709 control.cancellation().check()?;
710 let mut result = BudgetedVec::new(control.memory());
711 if limit == 0 {
712 return Ok(result);
713 }
714 let start = after.filter(|after| *after >= prefix).unwrap_or(prefix);
715 let mut changes = TieredCursor::new(
716 Some(&self.records),
717 &self.runs,
718 std::ops::Bound::Included(start),
719 control,
720 )?;
721 while let Some(change) = changes.next(control)? {
722 control.cancellation().check()?;
723 let key = change.key();
724 if !key.starts_with(prefix) {
725 break;
726 }
727 if after.is_some_and(|after| key <= after) {
728 continue;
729 }
730 result.push(change.write(control)?)?;
731 if result.len() == limit {
732 break;
733 }
734 }
735 Ok(result)
736 }
737}
738
739fn resident_bytes(write: &PreparedRecordWrite) -> usize {
741 write
742 .key()
743 .len()
744 .saturating_add(write.value().map_or(0, <[u8]>::len))
745 .saturating_add(CHANGE_OVERHEAD)
746}
747
748fn replaced_resident(records: &Records, resident: usize, write: &PreparedRecordWrite) -> usize {
749 let previous = records
750 .get(write.key())
751 .map_or(0, |change| resident_bytes(&change.write));
752 resident
753 .saturating_sub(previous)
754 .saturating_add(resident_bytes(write))
755}
756
757#[cfg(test)]
758mod tests;