1use super::{
4 prefix_range, redb_backend, Arc, BTreeMap, DependencySet, IdAllocator, Included, Op,
5 OverlayView, ReadLimits, ReadView, RefCell, ScanBounds, ScanCursor, ScanProjection, ScanRow,
6 StorageError, 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) watermark_keys: std::collections::BTreeSet<Vec<u8>>,
80 pub(super) deps: Option<&'a RefCell<DependencySet>>,
81 pub(super) ids: &'a IdAllocator,
83}
84
85fn snapshot_scan(
89 snapshot: &ReadView,
90 start: &[u8],
91 end: &[u8],
92 projection: ScanProjection,
93 max_rows: usize,
94) -> Result<Vec<(Vec<u8>, Vec<u8>)>, StorageError> {
95 let bounds = ScanBounds {
96 start: Included(start.to_vec()),
97 end: if end.is_empty() {
98 Unbounded
99 } else {
100 std::ops::Bound::Excluded(end.to_vec())
101 },
102 };
103 let mut cursor = snapshot.scan(bounds, projection)?;
104 let mut rows: Vec<(Vec<u8>, Vec<u8>)> = Vec::new();
105 loop {
106 let remaining = if max_rows == 0 {
107 1024
108 } else {
109 max_rows.saturating_sub(rows.len()).clamp(1, 1024)
110 };
111 let batch = cursor.next_batch(remaining, 8 * 1024 * 1024)?;
112 for row in batch.rows {
113 rows.push((row.key, row.value.unwrap_or_default()));
114 }
115 if batch.done || (max_rows != 0 && rows.len() >= max_rows) {
116 break;
117 }
118 }
119 Ok(rows)
120}
121
122struct SnapshotKeyIter<'view> {
126 cursor: ScanCursor<'view>,
127 pending: std::vec::IntoIter<ScanRow>,
128 done: bool,
129}
130
131impl<'view> SnapshotKeyIter<'view> {
132 fn new(snapshot: &'view ReadView, start: &[u8], end: &[u8]) -> Result<Self, StorageError> {
133 let bounds = ScanBounds {
134 start: Included(start.to_vec()),
135 end: if end.is_empty() {
136 Unbounded
137 } else {
138 std::ops::Bound::Excluded(end.to_vec())
139 },
140 };
141 Ok(Self {
142 cursor: snapshot.scan(bounds, ScanProjection::Keys)?,
143 pending: Vec::new().into_iter(),
144 done: false,
145 })
146 }
147}
148
149impl Iterator for SnapshotKeyIter<'_> {
150 type Item = Result<Vec<u8>, StorageError>;
151
152 fn next(&mut self) -> Option<Self::Item> {
153 loop {
154 if let Some(row) = self.pending.next() {
155 return Some(Ok(row.key));
156 }
157 if self.done {
158 return None;
159 }
160 match self.cursor.next_batch(1024, 8 * 1024 * 1024) {
161 Ok(batch) => {
162 self.done = batch.done;
163 self.pending = batch.rows.into_iter();
164 }
165 Err(error) => {
166 self.done = true;
167 return Some(Err(error));
168 }
169 }
170 }
171 }
172}
173
174enum Take {
176 Base,
178 Overlay { key: Vec<u8>, live: bool },
180}
181
182impl Txn<'_> {
183 fn track(&self, k: &[u8]) {
184 if let Some(deps) = &self.deps {
185 deps.borrow_mut().record(k.to_vec(), k.to_vec());
186 }
187 }
188
189 pub fn get(&self, k: &[u8]) -> Result<Option<Vec<u8>>, StorageError> {
196 self.track(k);
197 self.get_untracked(k)
198 }
199
200 pub fn allocate(&self, key: &[u8]) -> Result<u64, StorageError> {
209 let watermark = self.watermark(key)?;
210 self.ids.reserve(key, watermark)
211 }
212
213 pub fn set_watermark(&mut self, key: Vec<u8>, next: u64) -> Result<(), StorageError> {
223 let value = self.watermark(&key)?.max(next);
224 self.watermark_keys.insert(key.clone());
225 self.set(key, value.to_be_bytes().to_vec());
226 Ok(())
227 }
228
229 fn watermark(&self, key: &[u8]) -> Result<u64, StorageError> {
230 self.get_untracked(key)?
231 .map(|raw| {
232 <[u8; 8]>::try_from(raw.as_slice())
233 .map(u64::from_be_bytes)
234 .map_err(|_| StorageError::Builder("invalid u64 metadata".into()))
235 })
236 .transpose()
237 .map(Option::unwrap_or_default)
238 }
239
240 fn get_untracked(&self, k: &[u8]) -> Result<Option<Vec<u8>>, StorageError> {
241 if let Some(v) = self.buffer.get(k) {
242 ReadLimits::default().check_item(k.len(), v.as_ref().map_or(0, Vec::len))?;
243 return Ok(v.clone());
244 }
245 match &self.base {
246 TxnBase::Overlay(overlay) => {
247 let value = overlay.get(k)?;
248 if let Some(v) = &value {
249 ReadLimits::default().check_item(k.len(), v.len())?;
250 }
251 Ok(value)
252 }
253 TxnBase::Snapshot(snapshot) => snapshot.get(k),
254 TxnBase::Native(native) => {
255 native.get_current(k, ReadLimits::default(), ScanProjection::KeyValue)
256 }
257 TxnBase::Live(data) => data
258 .get(k)
259 .map(|value| {
260 ReadLimits::default().check_item(k.len(), value.len())?;
261 Ok(value.clone())
262 })
263 .transpose(),
264 }
265 }
266
267 pub fn get_tracked(&mut self, k: &[u8]) -> Result<Option<Vec<u8>>, StorageError> {
273 self.get(k)
274 }
275
276 pub fn range(&self, start: &[u8], end: &[u8]) -> Result<Vec<(Vec<u8>, Vec<u8>)>, StorageError> {
283 self.range_projected(start, end, ScanProjection::KeyValue)
284 }
285
286 #[expect(
287 clippy::too_many_lines,
288 reason = "one linear merge of base, overlay and staged rows; splitting it obscures the precedence order"
289 )]
290 fn range_projected(
291 &self,
292 start: &[u8],
293 end: &[u8],
294 projection: ScanProjection,
295 ) -> Result<Vec<(Vec<u8>, Vec<u8>)>, StorageError> {
296 if let Some(deps) = &self.deps {
297 deps.borrow_mut().record(start.to_vec(), end.to_vec());
298 }
299 if !end.is_empty() && start >= end {
300 return Ok(Vec::new());
301 }
302 let bounds = (
303 Included(start),
304 if end.is_empty() {
305 Unbounded
306 } else {
307 std::ops::Bound::Excluded(end)
308 },
309 );
310 let source: Box<dyn Iterator<Item = Result<(Vec<u8>, Vec<u8>), StorageError>> + '_> =
311 match &self.base {
312 TxnBase::Overlay(overlay) => {
313 if projection == ScanProjection::Keys {
314 Box::new(
315 overlay
316 .range_keys(start, end)?
317 .into_iter()
318 .map(|key| Ok((key, Vec::new()))),
319 )
320 } else {
321 Box::new(overlay.range(start, end)?.into_iter().map(Ok))
322 }
323 }
324 TxnBase::Snapshot(snapshot) => {
325 let rows = snapshot_scan(snapshot, start, end, projection, 0)?;
326 Box::new(rows.into_iter().map(Ok::<_, StorageError>))
327 }
328 TxnBase::Native(native) => {
329 Box::new(native.current_range(start, end, ReadLimits::default(), projection)?)
330 }
331 TxnBase::Live(data) => Box::new(data.range::<[u8], _>(bounds).map(|(k, v)| {
332 let v = if projection == ScanProjection::Keys {
333 &[][..]
334 } else {
335 v.as_slice()
336 };
337 ReadLimits::default().check_item(k.len(), v.len())?;
338 Ok((k.clone(), v.to_vec()))
339 })),
340 };
341 let mut base = source.peekable();
342 let mut overlay = self.buffer.range::<[u8], _>(bounds).peekable();
343 let mut out: Vec<(Vec<u8>, Vec<u8>)> = Vec::new();
344 let mut bytes = 0usize;
345 while base.peek().is_some() || overlay.peek().is_some() {
346 if base.peek().is_some_and(Result::is_err) {
347 return Err(base.next().unwrap().unwrap_err());
348 }
349 let row = match (base.peek(), overlay.peek()) {
350 (Some(Ok((bk, _))), Some((ok, _))) if bk < *ok => Some(base.next().unwrap()?),
351 (Some(Ok((bk, _))), Some((ok, _))) if bk == *ok => {
352 base.next();
353 let (k, value) = overlay.next().unwrap();
354 value
355 .as_ref()
356 .map(|v| {
357 let v = if projection == ScanProjection::Keys {
358 &[][..]
359 } else {
360 v.as_slice()
361 };
362 ReadLimits::default().check_item(k.len(), v.len())?;
363 Ok((k.clone(), v.to_vec()))
364 })
365 .transpose()?
366 }
367 (_, Some(_)) => {
368 let (k, value) = overlay.next().unwrap();
369 value
370 .as_ref()
371 .map(|v| {
372 let v = if projection == ScanProjection::Keys {
373 &[][..]
374 } else {
375 v.as_slice()
376 };
377 ReadLimits::default().check_item(k.len(), v.len())?;
378 Ok((k.clone(), v.to_vec()))
379 })
380 .transpose()?
381 }
382 (Some(_), None) => Some(base.next().unwrap()?),
383 (None, None) => break,
384 };
385 if let Some((k, v)) = row {
386 ReadLimits::default().check_item(k.len(), v.len())?;
387 bytes = bytes.checked_add(k.len() + v.len()).ok_or_else(|| {
388 StorageError::ResourceExhausted("transaction range size overflow".into())
389 })?;
390 if bytes > 64 * 1024 * 1024 {
391 return Err(StorageError::ResourceExhausted(
392 "materialized transaction range exceeds 64 MiB; use a bounded operation"
393 .into(),
394 ));
395 }
396 out.push((k, v));
397 }
398 }
399 Ok(out)
400 }
401
402 pub fn range_prefix_tracked(
408 &mut self,
409 prefix: &[u8],
410 ) -> Result<Vec<(Vec<u8>, Vec<u8>)>, StorageError> {
411 self.range_prefix(prefix)
412 }
413
414 pub fn range_prefix(&self, prefix: &[u8]) -> Result<Vec<(Vec<u8>, Vec<u8>)>, StorageError> {
420 let (s, e) = prefix_range(prefix);
421 let out = self.range_projected(&s, &e, ScanProjection::KeyValue)?;
422 if let Some(deps) = &self.deps {
423 deps.borrow_mut().record_prefix(prefix);
424 }
425 Ok(out)
426 }
427
428 pub fn keys_prefix(&self, prefix: &[u8]) -> Result<Vec<Vec<u8>>, StorageError> {
436 let (start, end) = prefix_range(prefix);
437 let out = self
438 .range_projected(&start, &end, ScanProjection::Keys)?
439 .into_iter()
440 .map(|(key, _)| key)
441 .collect();
442 if let Some(deps) = &self.deps {
443 deps.borrow_mut().record_prefix(prefix);
444 }
445 Ok(out)
446 }
447
448 pub fn keys_prefix_limited(
464 &self,
465 prefix: &[u8],
466 limit: usize,
467 ) -> Result<Vec<Vec<u8>>, StorageError> {
468 if limit == 0 {
469 return Ok(Vec::new());
470 }
471 let (start, end) = prefix_range(prefix);
472 if !end.is_empty() && start >= end {
473 return Ok(Vec::new());
474 }
475 let bounds = (
476 Included(start.as_slice()),
477 if end.is_empty() {
478 Unbounded
479 } else {
480 std::ops::Bound::Excluded(end.as_slice())
481 },
482 );
483 let merged;
486 let staged: &BTreeMap<Vec<u8>, Option<Vec<u8>>> =
487 if let TxnBase::Overlay(overlay) = &self.base {
488 let mut map = overlay.buffer().clone();
489 for (key, value) in &self.buffer {
490 map.insert(key.clone(), value.clone());
491 }
492 merged = map;
493 &merged
494 } else {
495 &self.buffer
496 };
497 let source: Box<dyn Iterator<Item = Result<Vec<u8>, StorageError>> + '_> = match &self.base
498 {
499 TxnBase::Overlay(overlay) => {
500 Box::new(SnapshotKeyIter::new(overlay.base(), &start, &end)?)
501 }
502 TxnBase::Snapshot(snapshot) => Box::new(
503 snapshot_scan(snapshot, &start, &end, ScanProjection::Keys, limit)?
504 .into_iter()
505 .map(|(key, _)| key)
506 .map(Ok::<_, StorageError>),
507 ),
508 TxnBase::Native(native) => Box::new(
509 native
510 .current_range(&start, &end, ReadLimits::default(), ScanProjection::Keys)?
511 .map(|row| row.map(|(key, _)| key)),
512 ),
513 TxnBase::Live(data) => Box::new(data.range::<[u8], _>(bounds).map(|(key, _)| {
514 ReadLimits::default().check_item(key.len(), 0)?;
515 Ok(key.clone())
516 })),
517 };
518 let mut base = source.peekable();
519 let mut staged_iter = staged.range::<[u8], _>(bounds).peekable();
520 let mut out = Vec::with_capacity(limit.min(4096));
521 while out.len() < limit && (base.peek().is_some() || staged_iter.peek().is_some()) {
522 if base.peek().is_some_and(Result::is_err) {
523 return Err(base.next().unwrap().unwrap_err());
524 }
525 let take = match (base.peek(), staged_iter.peek()) {
526 (Some(Ok(bk)), Some((ok, _))) if bk < *ok => Take::Base,
527 (Some(Ok(bk)), Some((ok, _))) if bk == *ok => {
528 base.next();
529 let entry = staged_iter.next().unwrap();
530 Take::Overlay {
531 key: entry.0.clone(),
532 live: entry.1.is_some(),
533 }
534 }
535 (_, Some(_)) => {
536 let entry = staged_iter.next().unwrap();
537 Take::Overlay {
538 key: entry.0.clone(),
539 live: entry.1.is_some(),
540 }
541 }
542 (Some(_), None) => Take::Base,
543 (None, None) => break,
544 };
545 match take {
546 Take::Base => {
547 let key = base.next().unwrap()?;
548 ReadLimits::default().check_item(key.len(), 0)?;
549 out.push(key);
550 }
551 Take::Overlay { key, live } => {
552 if live {
553 ReadLimits::default().check_item(key.len(), 0)?;
554 out.push(key);
555 }
556 }
557 }
558 }
559 if let Some(deps) = &self.deps {
560 deps.borrow_mut().record_prefix(prefix);
561 }
562 Ok(out)
563 }
564
565 pub fn put(&mut self, k: Vec<u8>, v: Vec<u8>) {
567 self.buffer.insert(k, Some(v));
568 }
569
570 pub fn del(&mut self, k: Vec<u8>) {
572 self.buffer.insert(k, None);
573 }
574
575 pub fn purge(&mut self, k: Vec<u8>) {
579 self.purge_keys.insert(k.clone());
580 self.buffer.insert(k, None);
581 }
582
583 pub fn set(&mut self, k: Vec<u8>, v: Vec<u8>) {
587 self.set_keys.insert(k.clone());
588 self.buffer.insert(k, Some(v));
589 }
590
591 pub(super) fn drain(self) -> Vec<Op> {
593 let Txn {
594 buffer,
595 purge_keys,
596 set_keys,
597 ..
598 } = self;
599 buffer
600 .into_iter()
601 .map(|(k, v)| match v {
602 Some(v) if set_keys.contains(&k) => Op::Set(k, v),
603 Some(v) => Op::Put(k, v),
604 None if purge_keys.contains(&k) => Op::Purge(k),
605 None => Op::Del(k),
606 })
607 .collect()
608 }
609}