1use super::{
4 prefix_range, redb_backend, Arc, BTreeMap, DependencySet, Included, Op, OverlayView,
5 ReadLimits, ReadView, RefCell, ScanBounds, ScanCursor, ScanProjection, ScanRow, StorageError,
6 Unbounded,
7};
8
9pub(super) enum TxnBase<'a> {
15 Overlay(Arc<OverlayView>),
20 Snapshot(Arc<ReadView>),
26 Native(redb_backend::MergedViews),
28 Live(&'a BTreeMap<Vec<u8>, Vec<u8>>),
31}
32
33impl<'a> TxnBase<'a> {
34 pub(super) fn resolve(
46 overlay: Option<Arc<OverlayView>>,
47 snapshot: Option<Arc<ReadView>>,
48 native: Option<redb_backend::MergedViews>,
49 live: Option<&'a BTreeMap<Vec<u8>, Vec<u8>>>,
50 ) -> Self {
51 if let Some(overlay) = overlay {
52 return TxnBase::Overlay(overlay);
53 }
54 if let Some(snapshot) = snapshot {
55 return TxnBase::Snapshot(snapshot);
56 }
57 if let Some(native) = native {
58 return TxnBase::Native(native);
59 }
60 TxnBase::Live(live.expect(
61 "a transaction without an overlay, snapshot, or native view always has the live guard",
62 ))
63 }
64}
65
66pub struct Txn<'a> {
69 pub(super) base: TxnBase<'a>,
70 pub(super) buffer: BTreeMap<Vec<u8>, Option<Vec<u8>>>,
71 pub(super) purge_keys: std::collections::BTreeSet<Vec<u8>>,
74 pub(super) set_keys: std::collections::BTreeSet<Vec<u8>>,
77 pub(super) deps: Option<&'a RefCell<DependencySet>>,
78}
79
80fn snapshot_scan(
84 snapshot: &ReadView,
85 start: &[u8],
86 end: &[u8],
87 projection: ScanProjection,
88 max_rows: usize,
89) -> Result<Vec<(Vec<u8>, Vec<u8>)>, StorageError> {
90 let bounds = ScanBounds {
91 start: Included(start.to_vec()),
92 end: if end.is_empty() {
93 Unbounded
94 } else {
95 std::ops::Bound::Excluded(end.to_vec())
96 },
97 };
98 let mut cursor = snapshot.scan(bounds, projection)?;
99 let mut rows: Vec<(Vec<u8>, Vec<u8>)> = Vec::new();
100 loop {
101 let remaining = if max_rows == 0 {
102 1024
103 } else {
104 max_rows.saturating_sub(rows.len()).clamp(1, 1024)
105 };
106 let batch = cursor.next_batch(remaining, 8 * 1024 * 1024)?;
107 for row in batch.rows {
108 rows.push((row.key, row.value.unwrap_or_default()));
109 }
110 if batch.done || (max_rows != 0 && rows.len() >= max_rows) {
111 break;
112 }
113 }
114 Ok(rows)
115}
116
117struct SnapshotKeyIter<'view> {
121 cursor: ScanCursor<'view>,
122 pending: std::vec::IntoIter<ScanRow>,
123 done: bool,
124}
125
126impl<'view> SnapshotKeyIter<'view> {
127 fn new(snapshot: &'view ReadView, start: &[u8], end: &[u8]) -> Result<Self, StorageError> {
128 let bounds = ScanBounds {
129 start: Included(start.to_vec()),
130 end: if end.is_empty() {
131 Unbounded
132 } else {
133 std::ops::Bound::Excluded(end.to_vec())
134 },
135 };
136 Ok(Self {
137 cursor: snapshot.scan(bounds, ScanProjection::Keys)?,
138 pending: Vec::new().into_iter(),
139 done: false,
140 })
141 }
142}
143
144impl Iterator for SnapshotKeyIter<'_> {
145 type Item = Result<Vec<u8>, StorageError>;
146
147 fn next(&mut self) -> Option<Self::Item> {
148 loop {
149 if let Some(row) = self.pending.next() {
150 return Some(Ok(row.key));
151 }
152 if self.done {
153 return None;
154 }
155 match self.cursor.next_batch(1024, 8 * 1024 * 1024) {
156 Ok(batch) => {
157 self.done = batch.done;
158 self.pending = batch.rows.into_iter();
159 }
160 Err(error) => {
161 self.done = true;
162 return Some(Err(error));
163 }
164 }
165 }
166 }
167}
168
169enum Take {
171 Base,
173 Overlay { key: Vec<u8>, live: bool },
175}
176
177impl Txn<'_> {
178 fn track(&self, k: &[u8]) {
179 if let Some(deps) = &self.deps {
180 deps.borrow_mut().record(k.to_vec(), k.to_vec());
181 }
182 }
183
184 pub fn get(&self, k: &[u8]) -> Result<Option<Vec<u8>>, StorageError> {
191 self.track(k);
192 if let Some(v) = self.buffer.get(k) {
193 ReadLimits::default().check_item(k.len(), v.as_ref().map_or(0, Vec::len))?;
194 return Ok(v.clone());
195 }
196 match &self.base {
197 TxnBase::Overlay(overlay) => {
198 let value = overlay.get(k)?;
199 if let Some(v) = &value {
200 ReadLimits::default().check_item(k.len(), v.len())?;
201 }
202 Ok(value)
203 }
204 TxnBase::Snapshot(snapshot) => snapshot.get(k),
205 TxnBase::Native(native) => {
206 native.get_current(k, ReadLimits::default(), ScanProjection::KeyValue)
207 }
208 TxnBase::Live(data) => data
209 .get(k)
210 .map(|value| {
211 ReadLimits::default().check_item(k.len(), value.len())?;
212 Ok(value.clone())
213 })
214 .transpose(),
215 }
216 }
217
218 pub fn get_tracked(&mut self, k: &[u8]) -> Result<Option<Vec<u8>>, StorageError> {
224 self.get(k)
225 }
226
227 pub fn range(&self, start: &[u8], end: &[u8]) -> Result<Vec<(Vec<u8>, Vec<u8>)>, StorageError> {
234 self.range_projected(start, end, ScanProjection::KeyValue)
235 }
236
237 #[expect(
238 clippy::too_many_lines,
239 reason = "one linear merge of base, overlay and staged rows; splitting it obscures the precedence order"
240 )]
241 fn range_projected(
242 &self,
243 start: &[u8],
244 end: &[u8],
245 projection: ScanProjection,
246 ) -> Result<Vec<(Vec<u8>, Vec<u8>)>, StorageError> {
247 if let Some(deps) = &self.deps {
248 deps.borrow_mut().record(start.to_vec(), end.to_vec());
249 }
250 if !end.is_empty() && start >= end {
251 return Ok(Vec::new());
252 }
253 let bounds = (
254 Included(start),
255 if end.is_empty() {
256 Unbounded
257 } else {
258 std::ops::Bound::Excluded(end)
259 },
260 );
261 let source: Box<dyn Iterator<Item = Result<(Vec<u8>, Vec<u8>), StorageError>> + '_> =
262 match &self.base {
263 TxnBase::Overlay(overlay) => {
264 if projection == ScanProjection::Keys {
265 Box::new(
266 overlay
267 .range_keys(start, end)?
268 .into_iter()
269 .map(|key| Ok((key, Vec::new()))),
270 )
271 } else {
272 Box::new(overlay.range(start, end)?.into_iter().map(Ok))
273 }
274 }
275 TxnBase::Snapshot(snapshot) => {
276 let rows = snapshot_scan(snapshot, start, end, projection, 0)?;
277 Box::new(rows.into_iter().map(Ok::<_, StorageError>))
278 }
279 TxnBase::Native(native) => {
280 Box::new(native.current_range(start, end, ReadLimits::default(), projection)?)
281 }
282 TxnBase::Live(data) => Box::new(data.range::<[u8], _>(bounds).map(|(k, v)| {
283 let v = if projection == ScanProjection::Keys {
284 &[][..]
285 } else {
286 v.as_slice()
287 };
288 ReadLimits::default().check_item(k.len(), v.len())?;
289 Ok((k.clone(), v.to_vec()))
290 })),
291 };
292 let mut base = source.peekable();
293 let mut overlay = self.buffer.range::<[u8], _>(bounds).peekable();
294 let mut out: Vec<(Vec<u8>, Vec<u8>)> = Vec::new();
295 let mut bytes = 0usize;
296 while base.peek().is_some() || overlay.peek().is_some() {
297 if base.peek().is_some_and(Result::is_err) {
298 return Err(base.next().unwrap().unwrap_err());
299 }
300 let row = match (base.peek(), overlay.peek()) {
301 (Some(Ok((bk, _))), Some((ok, _))) if bk < *ok => Some(base.next().unwrap()?),
302 (Some(Ok((bk, _))), Some((ok, _))) if bk == *ok => {
303 base.next();
304 let (k, value) = overlay.next().unwrap();
305 value
306 .as_ref()
307 .map(|v| {
308 let v = if projection == ScanProjection::Keys {
309 &[][..]
310 } else {
311 v.as_slice()
312 };
313 ReadLimits::default().check_item(k.len(), v.len())?;
314 Ok((k.clone(), v.to_vec()))
315 })
316 .transpose()?
317 }
318 (_, Some(_)) => {
319 let (k, value) = overlay.next().unwrap();
320 value
321 .as_ref()
322 .map(|v| {
323 let v = if projection == ScanProjection::Keys {
324 &[][..]
325 } else {
326 v.as_slice()
327 };
328 ReadLimits::default().check_item(k.len(), v.len())?;
329 Ok((k.clone(), v.to_vec()))
330 })
331 .transpose()?
332 }
333 (Some(_), None) => Some(base.next().unwrap()?),
334 (None, None) => break,
335 };
336 if let Some((k, v)) = row {
337 ReadLimits::default().check_item(k.len(), v.len())?;
338 bytes = bytes.checked_add(k.len() + v.len()).ok_or_else(|| {
339 StorageError::ResourceExhausted("transaction range size overflow".into())
340 })?;
341 if bytes > 64 * 1024 * 1024 {
342 return Err(StorageError::ResourceExhausted(
343 "materialized transaction range exceeds 64 MiB; use a bounded operation"
344 .into(),
345 ));
346 }
347 out.push((k, v));
348 }
349 }
350 Ok(out)
351 }
352
353 pub fn range_prefix_tracked(
359 &mut self,
360 prefix: &[u8],
361 ) -> Result<Vec<(Vec<u8>, Vec<u8>)>, StorageError> {
362 self.range_prefix(prefix)
363 }
364
365 pub fn range_prefix(&self, prefix: &[u8]) -> Result<Vec<(Vec<u8>, Vec<u8>)>, StorageError> {
371 let (s, e) = prefix_range(prefix);
372 let out = self.range_projected(&s, &e, ScanProjection::KeyValue)?;
373 if let Some(deps) = &self.deps {
374 deps.borrow_mut().record_prefix(prefix);
375 }
376 Ok(out)
377 }
378
379 pub fn keys_prefix(&self, prefix: &[u8]) -> Result<Vec<Vec<u8>>, StorageError> {
387 let (start, end) = prefix_range(prefix);
388 let out = self
389 .range_projected(&start, &end, ScanProjection::Keys)?
390 .into_iter()
391 .map(|(key, _)| key)
392 .collect();
393 if let Some(deps) = &self.deps {
394 deps.borrow_mut().record_prefix(prefix);
395 }
396 Ok(out)
397 }
398
399 pub fn keys_prefix_limited(
415 &self,
416 prefix: &[u8],
417 limit: usize,
418 ) -> Result<Vec<Vec<u8>>, StorageError> {
419 if limit == 0 {
420 return Ok(Vec::new());
421 }
422 let (start, end) = prefix_range(prefix);
423 if !end.is_empty() && start >= end {
424 return Ok(Vec::new());
425 }
426 let bounds = (
427 Included(start.as_slice()),
428 if end.is_empty() {
429 Unbounded
430 } else {
431 std::ops::Bound::Excluded(end.as_slice())
432 },
433 );
434 let merged;
437 let staged: &BTreeMap<Vec<u8>, Option<Vec<u8>>> =
438 if let TxnBase::Overlay(overlay) = &self.base {
439 let mut map = overlay.buffer().clone();
440 for (key, value) in &self.buffer {
441 map.insert(key.clone(), value.clone());
442 }
443 merged = map;
444 &merged
445 } else {
446 &self.buffer
447 };
448 let source: Box<dyn Iterator<Item = Result<Vec<u8>, StorageError>> + '_> = match &self.base
449 {
450 TxnBase::Overlay(overlay) => {
451 Box::new(SnapshotKeyIter::new(overlay.base(), &start, &end)?)
452 }
453 TxnBase::Snapshot(snapshot) => Box::new(
454 snapshot_scan(snapshot, &start, &end, ScanProjection::Keys, limit)?
455 .into_iter()
456 .map(|(key, _)| key)
457 .map(Ok::<_, StorageError>),
458 ),
459 TxnBase::Native(native) => Box::new(
460 native
461 .current_range(&start, &end, ReadLimits::default(), ScanProjection::Keys)?
462 .map(|row| row.map(|(key, _)| key)),
463 ),
464 TxnBase::Live(data) => Box::new(data.range::<[u8], _>(bounds).map(|(key, _)| {
465 ReadLimits::default().check_item(key.len(), 0)?;
466 Ok(key.clone())
467 })),
468 };
469 let mut base = source.peekable();
470 let mut staged_iter = staged.range::<[u8], _>(bounds).peekable();
471 let mut out = Vec::with_capacity(limit.min(4096));
472 while out.len() < limit && (base.peek().is_some() || staged_iter.peek().is_some()) {
473 if base.peek().is_some_and(Result::is_err) {
474 return Err(base.next().unwrap().unwrap_err());
475 }
476 let take = match (base.peek(), staged_iter.peek()) {
477 (Some(Ok(bk)), Some((ok, _))) if bk < *ok => Take::Base,
478 (Some(Ok(bk)), Some((ok, _))) if bk == *ok => {
479 base.next();
480 let entry = staged_iter.next().unwrap();
481 Take::Overlay {
482 key: entry.0.clone(),
483 live: entry.1.is_some(),
484 }
485 }
486 (_, Some(_)) => {
487 let entry = staged_iter.next().unwrap();
488 Take::Overlay {
489 key: entry.0.clone(),
490 live: entry.1.is_some(),
491 }
492 }
493 (Some(_), None) => Take::Base,
494 (None, None) => break,
495 };
496 match take {
497 Take::Base => {
498 let key = base.next().unwrap()?;
499 ReadLimits::default().check_item(key.len(), 0)?;
500 out.push(key);
501 }
502 Take::Overlay { key, live } => {
503 if live {
504 ReadLimits::default().check_item(key.len(), 0)?;
505 out.push(key);
506 }
507 }
508 }
509 }
510 if let Some(deps) = &self.deps {
511 deps.borrow_mut().record_prefix(prefix);
512 }
513 Ok(out)
514 }
515
516 pub fn put(&mut self, k: Vec<u8>, v: Vec<u8>) {
518 self.buffer.insert(k, Some(v));
519 }
520
521 pub fn del(&mut self, k: Vec<u8>) {
523 self.buffer.insert(k, None);
524 }
525
526 pub fn purge(&mut self, k: Vec<u8>) {
530 self.purge_keys.insert(k.clone());
531 self.buffer.insert(k, None);
532 }
533
534 pub fn set(&mut self, k: Vec<u8>, v: Vec<u8>) {
538 self.set_keys.insert(k.clone());
539 self.buffer.insert(k, Some(v));
540 }
541
542 pub(super) fn drain(self) -> Vec<Op> {
544 let Txn {
545 buffer,
546 purge_keys,
547 set_keys,
548 ..
549 } = self;
550 buffer
551 .into_iter()
552 .map(|(k, v)| match v {
553 Some(v) if set_keys.contains(&k) => Op::Set(k, v),
554 Some(v) => Op::Put(k, v),
555 None if purge_keys.contains(&k) => Op::Purge(k),
556 None => Op::Del(k),
557 })
558 .collect()
559 }
560}