uqa_storage/mvcc/
history.rs1use std::sync::Arc;
10use uqa_core::memory::{BudgetedVec, MemoryBudget};
11
12use super::{CommitSequence, VersionError, VersionResult};
13
14pub type SharedRecordValue = Arc<BudgetedVec<u8>>;
15
16pub struct ScannedRecord {
17 pub key: BudgetedVec<u8>,
18 pub version: RecordVersion<SharedRecordValue>,
19}
20
21#[derive(Debug, Clone)]
22pub struct RecordVersion<T> {
23 sequence: CommitSequence,
24 value: Option<T>,
25}
26
27impl<T> RecordVersion<T> {
28 pub fn new(sequence: CommitSequence, value: Option<T>) -> VersionResult<Self> {
29 if sequence == CommitSequence::INITIAL {
30 return Err(VersionError::InvalidEncoding(
31 "record version has the initial sequence",
32 ));
33 }
34 Ok(Self { sequence, value })
35 }
36 pub fn sequence(&self) -> CommitSequence {
37 self.sequence
38 }
39
40 pub fn value(&self) -> Option<&T> {
42 self.value.as_ref()
43 }
44
45 pub fn into_parts(self) -> (CommitSequence, Option<T>) {
46 (self.sequence, self.value)
47 }
48}
49
50impl RecordVersion<SharedRecordValue> {
51 pub fn copy_bytes(
53 sequence: CommitSequence,
54 value: Option<&[u8]>,
55 control: &crate::read_control::StorageReadControl,
56 ) -> VersionResult<Self> {
57 control.cancellation().check()?;
58 let mut record = Self::new(sequence, None)?;
59 record.value = value
60 .map(|bytes| {
61 let mut value = BudgetedVec::new(control.memory());
62 value.extend_from_slice(bytes)?;
63 Ok::<_, VersionError>(Arc::new(value))
64 })
65 .transpose()?;
66 Ok(record)
67 }
68}
69
70impl ScannedRecord {
71 pub fn copy_key(
72 key: &[u8],
73 version: RecordVersion<SharedRecordValue>,
74 control: &crate::read_control::StorageReadControl,
75 ) -> VersionResult<Self> {
76 control.cancellation().check()?;
77 let mut owned = BudgetedVec::new(control.memory());
78 owned.extend_from_slice(key)?;
79 Ok(Self {
80 key: owned,
81 version,
82 })
83 }
84}
85
86#[derive(Debug)]
88pub struct RecordHistory<T> {
89 versions: BudgetedVec<RecordVersion<T>>,
90}
91
92impl<T> RecordHistory<T> {
93 pub fn new(memory: &MemoryBudget) -> Self {
94 Self {
95 versions: BudgetedVec::new(memory),
96 }
97 }
98
99 pub fn len(&self) -> usize {
100 self.versions.len()
101 }
102
103 pub fn is_empty(&self) -> bool {
104 self.versions.is_empty()
105 }
106
107 pub fn head(&self) -> Option<&RecordVersion<T>> {
108 self.versions.last()
109 }
110
111 pub fn visible_at(&self, snapshot: CommitSequence) -> Option<&RecordVersion<T>> {
112 let position = self
113 .versions
114 .partition_point(|version| version.sequence <= snapshot);
115 position
116 .checked_sub(1)
117 .map(|position| &self.versions[position])
118 }
119
120 pub fn append(&mut self, sequence: CommitSequence, value: Option<T>) -> VersionResult<()> {
121 self.check_sequence(sequence)?;
122 self.versions.push(RecordVersion { sequence, value })?;
123 Ok(())
124 }
125
126 fn check_sequence(&self, sequence: CommitSequence) -> VersionResult<()> {
127 let previous = self
128 .head()
129 .map_or(CommitSequence::INITIAL, RecordVersion::sequence);
130 if sequence <= previous {
131 return Err(VersionError::CommitOrder {
132 previous,
133 next: sequence,
134 });
135 }
136 Ok(())
137 }
138
139 pub fn reclaim_before(&mut self, horizon: CommitSequence) -> VersionResult<usize> {
145 let removed = self
146 .versions
147 .partition_point(|version| version.sequence <= horizon)
148 .saturating_sub(1);
149 if removed != 0 {
150 let retained = self.versions.len() - removed;
151 self.versions.rotate_left(removed);
152 self.versions.truncate(retained);
153 }
154 self.versions.shrink_to_fit()?;
155 Ok(removed)
156 }
157}
158
159impl<T: Clone> RecordHistory<T> {
160 pub fn fork_appending(
162 &self,
163 sequence: CommitSequence,
164 value: Option<T>,
165 ) -> VersionResult<Self> {
166 self.check_sequence(sequence)?;
167 let mut candidate = Self::new(self.versions.budget());
168 let capacity = self
169 .versions
170 .len()
171 .checked_add(1)
172 .ok_or(uqa_core::memory::MemoryError::SizeOverflow)?;
173 candidate.versions.reserve(capacity)?;
174 for version in self.versions.iter() {
175 candidate.versions.push(version.clone())?;
176 }
177 candidate.append(sequence, value)?;
178 Ok(candidate)
179 }
180}