1use super::*;
4use crate::storage::collection_read::{
5 CollectionCursor, CollectionRead, FrozenOverlay, MergedCollection,
6};
7use crate::storage::error::StoreError;
8use crate::storage::metadata_codec::serialize_entity_record;
9use std::collections::HashSet;
10
11pub(super) struct BackingRead {
12 pub source: Arc<dyn CollectionRead + Send + Sync>,
13 pub root_page: u32,
14 pub total_entities: u64,
15 pub format_version: u32,
16 pub read_bytes_max: usize,
17 pub removed: HashSet<EntityId>,
18}
19
20pub struct PagedEntityCursor {
23 cursor: Box<dyn CollectionCursor>,
24 format_version: u32,
25 read_bytes_max: usize,
26 snapshot: Option<SnapshotContext>,
27 visible_only: bool,
28 schema: Option<Arc<Vec<String>>>,
29 key: Option<String>,
30 finished: bool,
31}
32
33impl Iterator for PagedEntityCursor {
34 type Item = Result<(UnifiedEntity, Option<Metadata>), StoreError>;
35 fn next(&mut self) -> Option<Self::Item> {
36 if self.finished {
37 return None;
38 }
39 loop {
40 let record = match self.cursor.next_record(self.read_bytes_max) {
41 Ok(Some(record)) => record,
42 Ok(None) => {
43 self.finished = true;
44 return None;
45 }
46 Err(error) => {
47 self.finished = true;
48 return Some(Err(error));
49 }
50 };
51 let (mut entity, metadata) =
52 match record.decode_with_budget(self.format_version, self.read_bytes_max) {
53 Ok(record) => record,
54 Err(error) => {
55 self.finished = true;
56 return Some(Err(error));
57 }
58 };
59 if self.visible_only
60 && !SegmentManager::scan_entity_visible(self.snapshot.as_ref(), &entity)
61 {
62 continue;
63 }
64 if let super::super::entity::EntityData::Row(row) = &mut entity.data {
65 if row.named.is_none() && row.schema.is_none() {
66 row.schema = self.schema.clone();
67 }
68 }
69 if self.key.as_deref().is_some_and(|key| {
70 crate::storage::segment_key_lookup::SegmentKeyLookup::key(&entity) != Some(key)
71 }) {
72 continue;
73 }
74 return Some(Ok((entity, metadata)));
75 }
76 }
77}
78
79impl SegmentManager {
80 pub fn try_for_each_entity_unordered(
86 &self,
87 limit_bytes: usize,
88 mut callback: impl FnMut(&UnifiedEntity) -> bool,
89 ) -> Result<(), StoreError> {
90 let backing_guard = self.backing.read();
91 let Some(backing) = backing_guard.as_ref() else {
92 self.for_each_entity(callback);
93 return Ok(());
94 };
95 let limit_bytes = limit_bytes.min(backing.read_bytes_max);
96 let mut cursor = backing.source.cursor()?;
97 while let Some(id) = cursor.peek_id()? {
98 if backing.removed.contains(&id) || self.get_with(id, |_| ()).is_some() {
99 cursor.skip_record()?;
100 continue;
101 }
102 let record = cursor
103 .next_record(limit_bytes)?
104 .expect("invariant: peeked physical row exists");
105 let (mut entity, _) = record.decode_with_budget(backing.format_version, limit_bytes)?;
106 self.restore_owned_row_schemas(std::iter::once(&mut entity));
107 if !callback(&entity) {
108 return Ok(());
109 }
110 }
111 let mut result = Ok(());
112 self.for_each_entity(|entity| {
113 let required_bytes = crate::storage::memory_size::entity_bytes(entity);
114 if required_bytes > limit_bytes {
115 result = Err(StoreError::ReadLimitExceeded {
116 required_bytes,
117 limit_bytes,
118 });
119 return false;
120 }
121 callback(entity)
122 });
123 result
124 }
125
126 pub fn try_paged_entity_cursor(
129 &self,
130 snapshot: Option<SnapshotContext>,
131 ) -> Result<Option<PagedEntityCursor>, StoreError> {
132 self.try_paged_cursor(snapshot, true, None)
133 }
134
135 pub fn try_paged_physical_cursor(&self) -> Result<Option<PagedEntityCursor>, StoreError> {
138 self.try_paged_cursor(None, false, None)
139 }
140
141 pub fn try_paged_key_entity_cursor(
144 &self,
145 key: &str,
146 snapshot: Option<SnapshotContext>,
147 ) -> Result<Option<PagedEntityCursor>, StoreError> {
148 self.try_paged_cursor(snapshot, true, Some(key))
149 }
150
151 pub fn try_paged_key_physical_cursor(
153 &self,
154 key: &str,
155 ) -> Result<Option<PagedEntityCursor>, StoreError> {
156 self.try_paged_cursor(None, false, Some(key))
157 }
158
159 fn try_paged_cursor(
160 &self,
161 snapshot: Option<SnapshotContext>,
162 visible_only: bool,
163 key: Option<&str>,
164 ) -> Result<Option<PagedEntityCursor>, StoreError> {
165 let Some((format_version, read_bytes_max)) = self
166 .backing
167 .read()
168 .as_ref()
169 .map(|base| (base.format_version, base.read_bytes_max))
170 else {
171 return Ok(None);
172 };
173 let view = match key {
174 Some(key) => self.current_backing_key_overlay(key)?,
175 None => self.current_backing_overlay(),
176 };
177 let Some((base, overlay)) = view else {
178 return Ok(None);
179 };
180 let merged = MergedCollection::new(base.as_ref(), overlay);
181 let cursor = match key {
182 Some(key) => match merged.owned_key_cursor(key)? {
183 Some(cursor) => cursor,
184 None => merged.owned_cursor()?,
185 },
186 None => merged.owned_cursor()?,
187 };
188 Ok(Some(PagedEntityCursor {
189 cursor,
190 format_version,
191 read_bytes_max,
192 snapshot,
193 visible_only,
194 schema: self.column_schema(),
195 key: key.map(str::to_owned),
196 finished: false,
197 }))
198 }
199
200 pub fn has_backing(&self) -> bool {
201 self.backing.read().is_some()
202 }
203
204 pub fn backing_root_page(&self) -> Option<u32> {
206 self.backing.read().as_ref().map(|base| base.root_page)
207 }
208
209 pub fn backing_read_bytes_max(&self) -> Option<usize> {
212 self.backing
213 .read()
214 .as_ref()
215 .map(|backing| backing.read_bytes_max)
216 }
217
218 pub fn install_backing(
221 &self,
222 source: Arc<dyn CollectionRead + Send + Sync>,
223 root_page: u32,
224 format_version: u32,
225 read_bytes_max: usize,
226 total_entities: u64,
227 ) {
228 let mut backing = self.backing.write();
229 let mut consolidation = self.consolidation.write();
230 let mut growing = self.growing.write();
231 let mut sealed = self.sealed.write();
232 let mut retired = self.retired.write();
233 if let Some(source) = growing.take() {
234 self.restore_sequence_id_next(source.read().sequence_id_next());
235 retired.push(Arc::downgrade(&source));
236 }
237 for source in sealed.drain(..) {
238 self.restore_sequence_id_next(source.read().sequence_id_next());
239 retired.push(Arc::downgrade(&source));
240 }
241 *consolidation = None;
242 self.entity_segment.write().clear();
243 self.total_entities_atomic
244 .store(total_entities, Ordering::Relaxed);
245 *backing = Some(BackingRead {
246 source,
247 root_page,
248 total_entities,
249 format_version,
250 read_bytes_max,
251 removed: HashSet::new(),
252 });
253 }
254
255 #[doc(hidden)]
261 pub fn try_with_unshared_backing<T>(
262 &self,
263 root_page: u32,
264 update: impl FnOnce() -> (T, Option<(u32, u64)>),
265 ) -> Option<T> {
266 let mut backing = self.backing.write();
267 let backing = backing.as_mut()?;
268 if backing.root_page != root_page
269 || !backing.removed.is_empty()
270 || Arc::get_mut(&mut backing.source).is_none()
271 {
272 return None;
273 }
274 let (result, published) = update();
275 if let Some((root_page, total_entities)) = published {
276 backing.root_page = root_page;
277 backing.total_entities = total_entities;
278 }
279 Some(result)
280 }
281
282 pub fn backing_count_at_root(&self, root_page: u32) -> Option<u64> {
283 self.backing
284 .read()
285 .as_ref()
286 .filter(|base| base.root_page == root_page)
287 .map(|base| base.total_entities)
288 }
289
290 pub fn backing_row_is_shadowed(&self, id: EntityId) -> bool {
294 let backing = self.backing.read();
295 backing
296 .as_ref()
297 .is_none_or(|base| base.removed.contains(&id))
298 || self.get_with(id, |_| ()).is_some()
299 }
300
301 pub fn try_get(
303 &self,
304 id: EntityId,
305 limit_bytes: usize,
306 ) -> Result<Option<UnifiedEntity>, StoreError> {
307 let backing = self.backing.read();
308 let limit_bytes = backing
309 .as_ref()
310 .map_or(limit_bytes, |base| limit_bytes.min(base.read_bytes_max));
311 if let Some(result) = self.get_with(id, |entity| {
312 let required_bytes = if limit_bytes == usize::MAX {
313 0
314 } else {
315 crate::storage::memory_size::entity_bytes(entity)
316 };
317 if required_bytes > limit_bytes {
318 Err(StoreError::ReadLimitExceeded {
319 required_bytes,
320 limit_bytes,
321 })
322 } else {
323 Ok(entity.clone())
324 }
325 }) {
326 let mut entity = result?;
327 self.restore_owned_row_schemas(std::iter::once(&mut entity));
328 return Ok(Some(entity));
329 }
330 let Some(backing) = backing.as_ref() else {
331 return Ok(None);
332 };
333 if backing.removed.contains(&id) {
334 return Ok(None);
335 }
336 let Some(record) = backing
337 .source
338 .get_record(id, limit_bytes.min(backing.read_bytes_max))?
339 else {
340 return Ok(None);
341 };
342 let (mut entity, _) = record.decode_with_budget(
343 backing.format_version,
344 limit_bytes.min(backing.read_bytes_max),
345 )?;
346 self.restore_owned_row_schemas(std::iter::once(&mut entity));
347 Ok(Some(entity))
348 }
349
350 pub fn try_project_key_entity<T>(
355 &self,
356 key: &str,
357 snapshot: Option<&SnapshotContext>,
358 versioned: bool,
359 limit_bytes: usize,
360 mut project: impl FnMut(&UnifiedEntity) -> T,
361 ) -> Result<Option<T>, StoreError> {
362 let backing = self.backing.read();
363 let limit_bytes = backing
364 .as_ref()
365 .map_or(limit_bytes, |base| limit_bytes.min(base.read_bytes_max));
366 let mut best = self
367 .project_key_entity(key, snapshot, versioned, |entity| {
368 let required_bytes = if limit_bytes == usize::MAX {
369 0
370 } else {
371 crate::storage::memory_size::entity_bytes(entity)
372 };
373 if required_bytes > limit_bytes {
374 Err(StoreError::ReadLimitExceeded {
375 required_bytes,
376 limit_bytes,
377 })
378 } else {
379 Ok((entity.xmin, project(entity)))
380 }
381 })
382 .transpose()?;
383 let Some(backing) = backing.as_ref() else {
384 return Ok(best.map(|(_, projected)| projected));
385 };
386 if !versioned && best.is_some() {
387 return Ok(best.map(|(_, projected)| projected));
388 }
389 let mut cursor = match backing.source.key_cursor(key)? {
390 Some(cursor) => cursor,
391 None => backing.source.cursor()?,
392 };
393 while let Some(id) = cursor.peek_id()? {
394 if backing.removed.contains(&id) || self.get_with(id, |_| ()).is_some() {
397 cursor.skip_record()?;
398 continue;
399 }
400 let record = cursor
401 .next_record(limit_bytes)?
402 .expect("invariant: peeked physical row exists");
403 let (mut entity, _) = record.decode_with_budget(backing.format_version, limit_bytes)?;
404 self.restore_owned_row_schemas(std::iter::once(&mut entity));
405 if crate::storage::segment_key_lookup::SegmentKeyLookup::key(&entity) != Some(key)
406 || (versioned && !Self::scan_entity_visible(snapshot, &entity))
407 {
408 continue;
409 }
410 if best.as_ref().is_none_or(|(xmin, _)| entity.xmin >= *xmin) {
411 best = Some((entity.xmin, project(&entity)));
412 }
413 if !versioned {
414 break;
415 }
416 }
417 Ok(best.map(|(_, projected)| projected))
418 }
419
420 pub fn try_for_each_entity(
424 &self,
425 limit_bytes: usize,
426 mut callback: impl FnMut(&UnifiedEntity) -> bool,
427 ) -> Result<(), StoreError> {
428 let backing_guard = self.backing.read();
429 let Some(backing) = backing_guard.as_ref() else {
430 self.for_each_entity(callback);
431 return Ok(());
432 };
433 self.try_for_each_backed_entity(backing, limit_bytes, 0, |entity| callback(&entity))
434 }
435
436 fn try_for_each_backed_entity(
437 &self,
438 backing: &BackingRead,
439 limit_bytes: usize,
440 mut skip: usize,
441 mut callback: impl FnMut(std::borrow::Cow<'_, UnifiedEntity>) -> bool,
442 ) -> Result<(), StoreError> {
443 let limit_bytes = limit_bytes.min(backing.read_bytes_max);
444 let mut identities = Vec::new();
445 self.for_each_entity(|entity| {
446 identities.push(entity.id);
447 true
448 });
449 identities.sort_unstable();
450 identities.dedup();
451 let mut identities = identities.into_iter().peekable();
452 let mut cursor = backing.source.cursor()?;
453 loop {
454 let base = cursor.peek_id()?;
455 let resident = identities.peek().copied();
456 if let Some(id) = resident.filter(|id| base.is_none_or(|base| *id <= base)) {
457 if base == Some(id) {
458 cursor.skip_record()?;
459 }
460 identities.next();
461 let result = self.get_with(id, |entity| {
462 if skip > 0 {
463 skip -= 1;
464 return Ok(true);
465 }
466 let required_bytes = crate::storage::memory_size::entity_bytes(entity);
467 if required_bytes > limit_bytes {
468 return Err(StoreError::ReadLimitExceeded {
469 required_bytes,
470 limit_bytes,
471 });
472 }
473 Ok(callback(std::borrow::Cow::Borrowed(entity)))
474 });
475 if result.transpose()? == Some(false) {
476 return Ok(());
477 }
478 continue;
479 }
480 let Some(id) = base else {
481 return Ok(());
482 };
483 if backing.removed.contains(&id) {
484 cursor.skip_record()?;
485 continue;
486 }
487 if skip > 0 {
488 skip -= 1;
489 cursor.skip_record()?;
490 continue;
491 }
492 let record = cursor
493 .next_record(limit_bytes)?
494 .expect("invariant: peeked physical row exists");
495 let (mut entity, _) = record.decode_with_budget(
496 backing.format_version,
497 limit_bytes.min(backing.read_bytes_max),
498 )?;
499 self.restore_owned_row_schemas(std::iter::once(&mut entity));
500 if !callback(std::borrow::Cow::Owned(entity)) {
501 return Ok(());
502 }
503 }
504 }
505
506 pub fn try_page_in_id_order(
510 &self,
511 offset: usize,
512 limit: usize,
513 limit_bytes: usize,
514 ) -> Result<(usize, Vec<UnifiedEntity>), StoreError> {
515 let backing_guard = self.backing.read();
516 let total = self.count();
517 if offset >= total || limit == 0 {
518 return Ok((total, Vec::new()));
519 }
520 let mut items = Vec::new();
521 let mut retained_bytes = 0_usize;
522 let mut error = None;
523 let mut capture = |mut entity: std::borrow::Cow<'_, UnifiedEntity>| {
524 if let std::borrow::Cow::Owned(entity) = &mut entity {
528 self.restore_recovery_row_layout(entity, None);
529 }
530 let required_bytes =
531 retained_bytes.saturating_add(crate::storage::memory_size::entity_bytes(&entity));
532 if required_bytes > limit_bytes {
533 error = Some(StoreError::ReadLimitExceeded {
534 required_bytes,
535 limit_bytes,
536 });
537 return false;
538 }
539 retained_bytes = required_bytes;
540 items.push(entity.into_owned());
541 items.len() < limit
542 };
543 if let Some(backing) = backing_guard.as_ref() {
544 self.try_for_each_backed_entity(backing, limit_bytes, offset, &mut capture)?;
545 } else {
546 let mut skip = offset;
547 self.for_each_entity_in_id_order(|entity| {
548 if skip > 0 {
549 skip -= 1;
550 return true;
551 }
552 capture(std::borrow::Cow::Borrowed(entity))
553 });
554 }
555 if let Some(error) = error {
556 return Err(error);
557 }
558 self.restore_owned_row_schemas(items.iter_mut());
559 Ok((total, items))
560 }
561
562 pub fn try_page_in_id_order_where(
566 &self,
567 offset: usize,
568 limit: usize,
569 limit_bytes: usize,
570 mut predicate: impl FnMut(&UnifiedEntity) -> bool,
571 ) -> Result<(usize, Vec<UnifiedEntity>), StoreError> {
572 let mut visible = 0_usize;
573 let mut retained_bytes = 0_usize;
574 let mut items = Vec::new();
575 let mut error = None;
576 self.try_scan_for_each(None, limit_bytes, |entity| {
577 if !predicate(entity) {
578 return true;
579 }
580 let position = visible;
581 visible = visible.saturating_add(1);
582 if position < offset || items.len() >= limit {
583 return true;
584 }
585 let required_bytes =
586 retained_bytes.saturating_add(crate::storage::memory_size::entity_bytes(entity));
587 if required_bytes > limit_bytes {
588 error = Some(StoreError::ReadLimitExceeded {
589 required_bytes,
590 limit_bytes,
591 });
592 return false;
593 }
594 retained_bytes = required_bytes;
595 items.push(entity.clone());
596 true
597 })?;
598 if let Some(error) = error {
599 return Err(error);
600 }
601 self.restore_owned_row_schemas(items.iter_mut());
602 Ok((visible, items))
603 }
604
605 pub fn try_for_each_key_entity(
608 &self,
609 key: &str,
610 mut callback: impl FnMut(&UnifiedEntity) -> bool,
611 ) -> Result<(), StoreError> {
612 if let Some(cursor) = self.try_paged_key_physical_cursor(key)? {
613 for record in cursor {
614 let (entity, _) = record?;
615 if !callback(&entity) {
616 break;
617 }
618 }
619 return Ok(());
620 }
621 self.try_for_each_entity(usize::MAX, |entity| {
622 crate::storage::segment_key_lookup::SegmentKeyLookup::key(entity) != Some(key)
623 || callback(entity)
624 })
625 }
626
627 pub fn try_scan_for_each(
628 &self,
629 snapshot: Option<&SnapshotContext>,
630 limit_bytes: usize,
631 mut callback: impl FnMut(&UnifiedEntity) -> bool,
632 ) -> Result<(), StoreError> {
633 self.try_for_each_entity(limit_bytes, |entity| {
634 !Self::scan_entity_visible(snapshot, entity) || callback(entity)
635 })
636 }
637
638 pub fn try_scan_for_each_zoned_with_stats(
641 &self,
642 snapshot: Option<&SnapshotContext>,
643 zone_preds: &[(&str, ZoneColPred<'_>)],
644 limit_bytes: usize,
645 callback: impl FnMut(&UnifiedEntity) -> bool,
646 ) -> Result<SegmentScanStats, StoreError> {
647 if !self.has_backing() {
648 return Ok(self.scan_for_each_zoned_with_stats(snapshot, zone_preds, callback));
649 }
650 self.try_scan_for_each(snapshot, limit_bytes, callback)?;
651 Ok(SegmentScanStats {
652 segments_total: 1,
653 segments_scanned: 1,
654 segments_pruned: 0,
655 })
656 }
657
658 pub fn try_scan_project_zoned_with_stats<T, F>(
661 &self,
662 snapshot: Option<&SnapshotContext>,
663 zone_preds: &[(&str, ZoneColPred<'_>)],
664 read_bytes_max: usize,
665 project: F,
666 ) -> Result<(Vec<T>, SegmentScanStats), StoreError>
667 where
668 T: Send,
669 F: Fn(&UnifiedEntity) -> Option<T> + Sync,
670 {
671 if !self.has_backing() {
672 return Ok(self.scan_project_zoned_with_stats(snapshot, zone_preds, project));
673 }
674 let mut projected = Vec::new();
675 let stats = self.try_scan_for_each_zoned_with_stats(
676 snapshot,
677 zone_preds,
678 read_bytes_max,
679 |entity| {
680 if let Some(value) = project(entity) {
681 projected.push(value);
682 }
683 true
684 },
685 )?;
686 Ok((projected, stats))
687 }
688
689 pub fn try_get_many(
692 &self,
693 ids: &[EntityId],
694 read_bytes_max: usize,
695 ) -> Result<Vec<Option<UnifiedEntity>>, StoreError> {
696 if !self.has_backing() && read_bytes_max == usize::MAX {
697 return Ok(self.get_many(ids));
698 }
699 ids.iter()
700 .map(|id| self.try_get(*id, read_bytes_max))
701 .collect()
702 }
703
704 pub fn try_for_each_id(
705 &self,
706 ids: &[EntityId],
707 read_bytes_max: usize,
708 mut visit: impl FnMut(usize, &UnifiedEntity) -> bool,
709 ) -> Result<(), StoreError> {
710 if self.has_backing() {
711 for (position, id) in ids.iter().enumerate() {
712 if let Some(entity) = self.try_get(*id, read_bytes_max)? {
713 if !visit(position, &entity) {
714 break;
715 }
716 }
717 }
718 return Ok(());
719 }
720 let mut stopped = false;
721 let mut failure = None;
722 self.for_each_id(ids, |position, entity| {
723 if stopped {
724 return;
725 }
726 let required_bytes = if read_bytes_max == usize::MAX {
727 0
728 } else {
729 crate::storage::memory_size::entity_bytes(entity)
730 };
731 if required_bytes > read_bytes_max {
732 failure = Some(StoreError::ReadLimitExceeded {
733 required_bytes,
734 limit_bytes: read_bytes_max,
735 });
736 stopped = true;
737 } else {
738 stopped = !visit(position, entity);
739 }
740 });
741 failure.map_or(Ok(()), Err)
742 }
743
744 pub(super) fn reject_backing_duplicate(&self, id: EntityId) -> Result<(), SegmentError> {
746 let backing = self.backing.read();
747 let Some(backing) = backing.as_ref() else {
748 return Ok(());
749 };
750 if !backing.removed.contains(&id)
751 && backing
752 .source
753 .get_record(id, backing.read_bytes_max)
754 .map_err(|error| SegmentError::BackingRead(error.to_string()))?
755 .is_some()
756 {
757 return Err(SegmentError::AlreadyExists(id));
758 }
759 Ok(())
760 }
761
762 pub fn try_get_metadata(
764 &self,
765 id: EntityId,
766 limit_bytes: usize,
767 ) -> Result<Option<Metadata>, StoreError> {
768 let backing = self.backing.read();
769 if self.get_with(id, |_| ()).is_some() {
770 return Ok(self.get_metadata(id));
771 }
772 let Some(backing) = backing.as_ref() else {
773 return Ok(None);
774 };
775 if backing.removed.contains(&id) {
776 return Ok(None);
777 }
778 backing
779 .source
780 .get_record(id, limit_bytes.min(backing.read_bytes_max))?
781 .map(|record| {
782 record
783 .decode_with_budget(
784 backing.format_version,
785 limit_bytes.min(backing.read_bytes_max),
786 )
787 .map(|(_, metadata)| metadata)
788 })
789 .transpose()
790 .map(Option::flatten)
791 }
792
793 pub(super) fn materialize_backing_entity(&self, id: EntityId) -> Result<(), SegmentError> {
794 if self.get_with(id, |_| ()).is_some() {
795 return Ok(());
796 }
797 let record = {
798 let backing = self.backing.read();
799 let Some(backing) = backing.as_ref() else {
800 return Ok(());
801 };
802 if backing.removed.contains(&id) {
803 return Ok(());
804 }
805 backing
806 .source
807 .get_record(id, backing.read_bytes_max)
808 .and_then(|record| {
809 record
810 .map(|record| {
811 record
812 .decode_with_budget(backing.format_version, backing.read_bytes_max)
813 })
814 .transpose()
815 })
816 .map_err(|error| SegmentError::BackingRead(error.to_string()))?
817 };
818 let Some((mut entity, metadata)) = record else {
819 return Ok(());
820 };
821 self.restore_owned_row_schemas(std::iter::once(&mut entity));
822 self.restore_snapshot_entity(entity)?;
823 self.total_entities_atomic.fetch_sub(1, Ordering::Relaxed);
825 if let Some(metadata) = metadata {
826 self.set_metadata(id, metadata)?;
827 }
828 Ok(())
829 }
830
831 pub(super) fn remember_backing_delete(&self, id: EntityId) {
832 if let Some(backing) = self.backing.write().as_mut() {
833 backing.removed.insert(id);
834 }
835 }
836
837 pub fn backing_overlay(
838 &self,
839 mut encode: impl FnMut(&UnifiedEntity, Option<Metadata>) -> Option<Vec<u8>>,
840 ) -> Option<(Arc<dyn CollectionRead + Send + Sync>, Arc<FrozenOverlay>)> {
841 let backing = self.backing.read();
842 let backing = backing.as_ref()?;
843 let mut overlay: FrozenOverlay = backing.removed.iter().map(|id| (*id, None)).collect();
844 self.for_each_entity_with_metadata(|entity, metadata| {
845 if let Some(bytes) = encode(entity, metadata) {
846 overlay.insert(entity.id, Some(Arc::from(bytes)));
847 }
848 true
849 });
850 Some((Arc::clone(&backing.source), Arc::new(overlay)))
851 }
852
853 pub fn current_backing_overlay(
854 &self,
855 ) -> Option<(Arc<dyn CollectionRead + Send + Sync>, Arc<FrozenOverlay>)> {
856 let version = self.backing.read().as_ref()?.format_version;
857 self.backing_overlay(|entity, metadata| {
858 Some(serialize_entity_record(entity, metadata.as_ref(), version))
859 })
860 }
861
862 fn current_backing_key_overlay(
863 &self,
864 key: &str,
865 ) -> Result<Option<(Arc<dyn CollectionRead + Send + Sync>, Arc<FrozenOverlay>)>, StoreError>
866 {
867 let backing = self.backing.read();
868 let Some(backing) = backing.as_ref() else {
869 return Ok(None);
870 };
871 let mut overlay: FrozenOverlay = backing.removed.iter().map(|id| (*id, None)).collect();
872 let schema = self.column_schema();
873 let key_column = schema
874 .as_ref()
875 .and_then(|schema| schema.iter().position(|name| name == "key"));
876 let mut failure = None;
877 self.for_each_entity_source(|segment, entity| {
878 let key_matches = match (&entity.data, key_column) {
879 (crate::storage::entity::EntityData::Row(row), Some(index)) if row.named.is_none() && row.schema.is_none() => {
880 matches!(row.columns.get(index), Some(reddb_types::Value::Text(stored)) if stored.as_ref() == key)
881 }
882 _ => crate::storage::segment_key_lookup::SegmentKeyLookup::key(entity) == Some(key),
883 };
884 let bytes = if key_matches {
885 let required_bytes = crate::storage::memory_size::entity_bytes(entity);
886 if required_bytes > backing.read_bytes_max {
887 failure = Some(StoreError::ReadLimitExceeded {
888 required_bytes,
889 limit_bytes: backing.read_bytes_max,
890 });
891 return false;
892 }
893 let metadata = segment.get_metadata(entity.id);
894 Some(Arc::from(serialize_entity_record(entity, metadata.as_ref(), backing.format_version)))
895 } else {
896 None
899 };
900 overlay.insert(entity.id, bytes);
901 true
902 });
903 if let Some(error) = failure {
904 return Err(error);
905 }
906 Ok(Some((Arc::clone(&backing.source), Arc::new(overlay))))
907 }
908}