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