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 #[doc(hidden)]
293 pub fn set_recovered_backing_count(&self, root_page: u32, count: u64) -> bool {
294 let mut backing = self.backing.write();
295 let Some(backing) = backing.as_mut() else {
296 return false;
297 };
298 if backing.root_page != root_page {
299 return false;
300 }
301 backing.total_entities = count;
302 true
303 }
304
305 pub fn backing_row_is_shadowed(&self, id: EntityId) -> bool {
309 let backing = self.backing.read();
310 backing
311 .as_ref()
312 .is_none_or(|base| base.removed.contains(&id))
313 || self.get_with(id, |_| ()).is_some()
314 }
315
316 pub fn try_get(
318 &self,
319 id: EntityId,
320 limit_bytes: usize,
321 ) -> Result<Option<UnifiedEntity>, StoreError> {
322 let backing = self.backing.read();
323 let limit_bytes = backing
324 .as_ref()
325 .map_or(limit_bytes, |base| limit_bytes.min(base.read_bytes_max));
326 if let Some(result) = self.get_with(id, |entity| {
327 let required_bytes = if limit_bytes == usize::MAX {
328 0
329 } else {
330 crate::storage::memory_size::entity_bytes(entity)
331 };
332 if required_bytes > limit_bytes {
333 Err(StoreError::ReadLimitExceeded {
334 required_bytes,
335 limit_bytes,
336 })
337 } else {
338 Ok(entity.clone())
339 }
340 }) {
341 let mut entity = result?;
342 self.restore_owned_row_schemas(std::iter::once(&mut entity));
343 return Ok(Some(entity));
344 }
345 let Some(backing) = backing.as_ref() else {
346 return Ok(None);
347 };
348 if backing.removed.contains(&id) {
349 return Ok(None);
350 }
351 let Some(record) = backing
352 .source
353 .get_record(id, limit_bytes.min(backing.read_bytes_max))?
354 else {
355 return Ok(None);
356 };
357 let (mut entity, _) = record.decode_with_budget(
358 backing.format_version,
359 limit_bytes.min(backing.read_bytes_max),
360 )?;
361 self.restore_owned_row_schemas(std::iter::once(&mut entity));
362 Ok(Some(entity))
363 }
364
365 pub fn try_project_key_entity<T>(
370 &self,
371 key: &str,
372 snapshot: Option<&SnapshotContext>,
373 versioned: bool,
374 limit_bytes: usize,
375 mut project: impl FnMut(&UnifiedEntity) -> T,
376 ) -> Result<Option<T>, StoreError> {
377 let backing = self.backing.read();
378 let limit_bytes = backing
379 .as_ref()
380 .map_or(limit_bytes, |base| limit_bytes.min(base.read_bytes_max));
381 let mut best = self
382 .project_key_entity(key, snapshot, versioned, |entity| {
383 let required_bytes = if limit_bytes == usize::MAX {
384 0
385 } else {
386 crate::storage::memory_size::entity_bytes(entity)
387 };
388 if required_bytes > limit_bytes {
389 Err(StoreError::ReadLimitExceeded {
390 required_bytes,
391 limit_bytes,
392 })
393 } else {
394 Ok((entity.xmin, project(entity)))
395 }
396 })
397 .transpose()?;
398 let Some(backing) = backing.as_ref() else {
399 return Ok(best.map(|(_, projected)| projected));
400 };
401 if !versioned && best.is_some() {
402 return Ok(best.map(|(_, projected)| projected));
403 }
404 let mut cursor = match backing.source.key_cursor(key)? {
405 Some(cursor) => cursor,
406 None => backing.source.cursor()?,
407 };
408 while let Some(id) = cursor.peek_id()? {
409 if backing.removed.contains(&id) || self.get_with(id, |_| ()).is_some() {
412 cursor.skip_record()?;
413 continue;
414 }
415 let record = cursor
416 .next_record(limit_bytes)?
417 .expect("invariant: peeked physical row exists");
418 let (mut entity, _) = record.decode_with_budget(backing.format_version, limit_bytes)?;
419 self.restore_owned_row_schemas(std::iter::once(&mut entity));
420 if crate::storage::segment_key_lookup::SegmentKeyLookup::key(&entity) != Some(key)
421 || (versioned && !Self::scan_entity_visible(snapshot, &entity))
422 {
423 continue;
424 }
425 if best.as_ref().is_none_or(|(xmin, _)| entity.xmin >= *xmin) {
426 best = Some((entity.xmin, project(&entity)));
427 }
428 if !versioned {
429 break;
430 }
431 }
432 Ok(best.map(|(_, projected)| projected))
433 }
434
435 pub fn try_for_each_entity(
439 &self,
440 limit_bytes: usize,
441 mut callback: impl FnMut(&UnifiedEntity) -> bool,
442 ) -> Result<(), StoreError> {
443 let backing_guard = self.backing.read();
444 let Some(backing) = backing_guard.as_ref() else {
445 self.for_each_entity(callback);
446 return Ok(());
447 };
448 self.try_for_each_backed_entity(backing, limit_bytes, 0, |entity| callback(&entity))
449 }
450
451 fn try_for_each_backed_entity(
452 &self,
453 backing: &BackingRead,
454 limit_bytes: usize,
455 mut skip: usize,
456 mut callback: impl FnMut(std::borrow::Cow<'_, UnifiedEntity>) -> bool,
457 ) -> Result<(), StoreError> {
458 let limit_bytes = limit_bytes.min(backing.read_bytes_max);
459 let mut identities = Vec::new();
460 self.for_each_entity(|entity| {
461 identities.push(entity.id);
462 true
463 });
464 identities.sort_unstable();
465 identities.dedup();
466 let mut identities = identities.into_iter().peekable();
467 let mut cursor = backing.source.cursor()?;
468 loop {
469 let base = cursor.peek_id()?;
470 let resident = identities.peek().copied();
471 if let Some(id) = resident.filter(|id| base.is_none_or(|base| *id <= base)) {
472 if base == Some(id) {
473 cursor.skip_record()?;
474 }
475 identities.next();
476 let result = self.get_with(id, |entity| {
477 if skip > 0 {
478 skip -= 1;
479 return Ok(true);
480 }
481 let required_bytes = crate::storage::memory_size::entity_bytes(entity);
482 if required_bytes > limit_bytes {
483 return Err(StoreError::ReadLimitExceeded {
484 required_bytes,
485 limit_bytes,
486 });
487 }
488 Ok(callback(std::borrow::Cow::Borrowed(entity)))
489 });
490 if result.transpose()? == Some(false) {
491 return Ok(());
492 }
493 continue;
494 }
495 let Some(id) = base else {
496 return Ok(());
497 };
498 if backing.removed.contains(&id) {
499 cursor.skip_record()?;
500 continue;
501 }
502 if skip > 0 {
503 skip -= 1;
504 cursor.skip_record()?;
505 continue;
506 }
507 let record = cursor
508 .next_record(limit_bytes)?
509 .expect("invariant: peeked physical row exists");
510 let (mut entity, _) = record.decode_with_budget(
511 backing.format_version,
512 limit_bytes.min(backing.read_bytes_max),
513 )?;
514 self.restore_owned_row_schemas(std::iter::once(&mut entity));
515 if !callback(std::borrow::Cow::Owned(entity)) {
516 return Ok(());
517 }
518 }
519 }
520
521 pub fn try_page_in_id_order(
525 &self,
526 offset: usize,
527 limit: usize,
528 limit_bytes: usize,
529 ) -> Result<(usize, Vec<UnifiedEntity>), StoreError> {
530 let backing_guard = self.backing.read();
531 let total = self.count();
532 if offset >= total || limit == 0 {
533 return Ok((total, Vec::new()));
534 }
535 let mut items = Vec::new();
536 let mut retained_bytes = 0_usize;
537 let mut error = None;
538 let mut capture = |mut entity: std::borrow::Cow<'_, UnifiedEntity>| {
539 if let std::borrow::Cow::Owned(entity) = &mut entity {
543 self.restore_recovery_row_layout(entity, None);
544 }
545 let required_bytes =
546 retained_bytes.saturating_add(crate::storage::memory_size::entity_bytes(&entity));
547 if required_bytes > limit_bytes {
548 error = Some(StoreError::ReadLimitExceeded {
549 required_bytes,
550 limit_bytes,
551 });
552 return false;
553 }
554 retained_bytes = required_bytes;
555 items.push(entity.into_owned());
556 items.len() < limit
557 };
558 if let Some(backing) = backing_guard.as_ref() {
559 self.try_for_each_backed_entity(backing, limit_bytes, offset, &mut capture)?;
560 } else {
561 let mut skip = offset;
562 self.for_each_entity_in_id_order(|entity| {
563 if skip > 0 {
564 skip -= 1;
565 return true;
566 }
567 capture(std::borrow::Cow::Borrowed(entity))
568 });
569 }
570 if let Some(error) = error {
571 return Err(error);
572 }
573 self.restore_owned_row_schemas(items.iter_mut());
574 Ok((total, items))
575 }
576
577 pub fn try_page_in_id_order_where(
581 &self,
582 offset: usize,
583 limit: usize,
584 limit_bytes: usize,
585 mut predicate: impl FnMut(&UnifiedEntity) -> bool,
586 ) -> Result<(usize, Vec<UnifiedEntity>), StoreError> {
587 let mut visible = 0_usize;
588 let mut retained_bytes = 0_usize;
589 let mut items = Vec::new();
590 let mut error = None;
591 self.try_scan_for_each(None, limit_bytes, |entity| {
592 if !predicate(entity) {
593 return true;
594 }
595 let position = visible;
596 visible = visible.saturating_add(1);
597 if position < offset || items.len() >= limit {
598 return true;
599 }
600 let required_bytes =
601 retained_bytes.saturating_add(crate::storage::memory_size::entity_bytes(entity));
602 if required_bytes > limit_bytes {
603 error = Some(StoreError::ReadLimitExceeded {
604 required_bytes,
605 limit_bytes,
606 });
607 return false;
608 }
609 retained_bytes = required_bytes;
610 items.push(entity.clone());
611 true
612 })?;
613 if let Some(error) = error {
614 return Err(error);
615 }
616 self.restore_owned_row_schemas(items.iter_mut());
617 Ok((visible, items))
618 }
619
620 pub fn try_for_each_key_entity(
623 &self,
624 key: &str,
625 mut callback: impl FnMut(&UnifiedEntity) -> bool,
626 ) -> Result<(), StoreError> {
627 if let Some(cursor) = self.try_paged_key_physical_cursor(key)? {
628 for record in cursor {
629 let (entity, _) = record?;
630 if !callback(&entity) {
631 break;
632 }
633 }
634 return Ok(());
635 }
636 self.try_for_each_entity(usize::MAX, |entity| {
637 crate::storage::segment_key_lookup::SegmentKeyLookup::key(entity) != Some(key)
638 || callback(entity)
639 })
640 }
641
642 pub fn try_scan_for_each(
643 &self,
644 snapshot: Option<&SnapshotContext>,
645 limit_bytes: usize,
646 mut callback: impl FnMut(&UnifiedEntity) -> bool,
647 ) -> Result<(), StoreError> {
648 self.try_for_each_entity(limit_bytes, |entity| {
649 !Self::scan_entity_visible(snapshot, entity) || callback(entity)
650 })
651 }
652
653 pub fn try_scan_for_each_zoned_with_stats(
656 &self,
657 snapshot: Option<&SnapshotContext>,
658 zone_preds: &[(&str, ZoneColPred<'_>)],
659 limit_bytes: usize,
660 callback: impl FnMut(&UnifiedEntity) -> bool,
661 ) -> Result<SegmentScanStats, StoreError> {
662 if !self.has_backing() {
663 return Ok(self.scan_for_each_zoned_with_stats(snapshot, zone_preds, callback));
664 }
665 self.try_scan_for_each(snapshot, limit_bytes, callback)?;
666 Ok(SegmentScanStats {
667 segments_total: 1,
668 segments_scanned: 1,
669 segments_pruned: 0,
670 })
671 }
672
673 pub fn try_scan_project_zoned_with_stats<T, F>(
676 &self,
677 snapshot: Option<&SnapshotContext>,
678 zone_preds: &[(&str, ZoneColPred<'_>)],
679 read_bytes_max: usize,
680 project: F,
681 ) -> Result<(Vec<T>, SegmentScanStats), StoreError>
682 where
683 T: Send,
684 F: Fn(&UnifiedEntity) -> Option<T> + Sync,
685 {
686 if !self.has_backing() {
687 return Ok(self.scan_project_zoned_with_stats(snapshot, zone_preds, project));
688 }
689 let mut projected = Vec::new();
690 let stats = self.try_scan_for_each_zoned_with_stats(
691 snapshot,
692 zone_preds,
693 read_bytes_max,
694 |entity| {
695 if let Some(value) = project(entity) {
696 projected.push(value);
697 }
698 true
699 },
700 )?;
701 Ok((projected, stats))
702 }
703
704 pub fn try_get_many(
707 &self,
708 ids: &[EntityId],
709 read_bytes_max: usize,
710 ) -> Result<Vec<Option<UnifiedEntity>>, StoreError> {
711 if !self.has_backing() && read_bytes_max == usize::MAX {
712 return Ok(self.get_many(ids));
713 }
714 ids.iter()
715 .map(|id| self.try_get(*id, read_bytes_max))
716 .collect()
717 }
718
719 pub fn try_for_each_id(
720 &self,
721 ids: &[EntityId],
722 read_bytes_max: usize,
723 mut visit: impl FnMut(usize, &UnifiedEntity) -> bool,
724 ) -> Result<(), StoreError> {
725 if self.has_backing() {
726 for (position, id) in ids.iter().enumerate() {
727 if let Some(entity) = self.try_get(*id, read_bytes_max)? {
728 if !visit(position, &entity) {
729 break;
730 }
731 }
732 }
733 return Ok(());
734 }
735 let mut stopped = false;
736 let mut failure = None;
737 self.for_each_id(ids, |position, entity| {
738 if stopped {
739 return;
740 }
741 let required_bytes = if read_bytes_max == usize::MAX {
742 0
743 } else {
744 crate::storage::memory_size::entity_bytes(entity)
745 };
746 if required_bytes > read_bytes_max {
747 failure = Some(StoreError::ReadLimitExceeded {
748 required_bytes,
749 limit_bytes: read_bytes_max,
750 });
751 stopped = true;
752 } else {
753 stopped = !visit(position, entity);
754 }
755 });
756 failure.map_or(Ok(()), Err)
757 }
758
759 pub(super) fn reject_backing_duplicate(&self, id: EntityId) -> Result<(), SegmentError> {
761 let backing = self.backing.read();
762 let Some(backing) = backing.as_ref() else {
763 return Ok(());
764 };
765 if !backing.removed.contains(&id)
766 && backing
767 .source
768 .get_record(id, backing.read_bytes_max)
769 .map_err(|error| SegmentError::BackingRead(error.to_string()))?
770 .is_some()
771 {
772 return Err(SegmentError::AlreadyExists(id));
773 }
774 Ok(())
775 }
776
777 pub fn try_get_metadata(
779 &self,
780 id: EntityId,
781 limit_bytes: usize,
782 ) -> Result<Option<Metadata>, StoreError> {
783 let backing = self.backing.read();
784 if self.get_with(id, |_| ()).is_some() {
785 return Ok(self.get_metadata(id));
786 }
787 let Some(backing) = backing.as_ref() else {
788 return Ok(None);
789 };
790 if backing.removed.contains(&id) {
791 return Ok(None);
792 }
793 backing
794 .source
795 .get_record(id, limit_bytes.min(backing.read_bytes_max))?
796 .map(|record| {
797 record
798 .decode_with_budget(
799 backing.format_version,
800 limit_bytes.min(backing.read_bytes_max),
801 )
802 .map(|(_, metadata)| metadata)
803 })
804 .transpose()
805 .map(Option::flatten)
806 }
807
808 pub(super) fn materialize_backing_entity(&self, id: EntityId) -> Result<(), SegmentError> {
809 if self.get_with(id, |_| ()).is_some() {
810 return Ok(());
811 }
812 let record = {
813 let backing = self.backing.read();
814 let Some(backing) = backing.as_ref() else {
815 return Ok(());
816 };
817 if backing.removed.contains(&id) {
818 return Ok(());
819 }
820 backing
821 .source
822 .get_record(id, backing.read_bytes_max)
823 .and_then(|record| {
824 record
825 .map(|record| {
826 record
827 .decode_with_budget(backing.format_version, backing.read_bytes_max)
828 })
829 .transpose()
830 })
831 .map_err(|error| SegmentError::BackingRead(error.to_string()))?
832 };
833 let Some((mut entity, metadata)) = record else {
834 return Ok(());
835 };
836 self.restore_owned_row_schemas(std::iter::once(&mut entity));
837 self.restore_snapshot_entity(entity)?;
838 self.total_entities_atomic.fetch_sub(1, Ordering::Relaxed);
840 if let Some(metadata) = metadata {
841 self.set_metadata(id, metadata)?;
842 }
843 Ok(())
844 }
845
846 pub(super) fn remember_backing_delete(&self, id: EntityId) {
847 if let Some(backing) = self.backing.write().as_mut() {
848 backing.removed.insert(id);
849 }
850 }
851
852 pub fn backing_overlay(
853 &self,
854 mut encode: impl FnMut(&UnifiedEntity, Option<Metadata>) -> Option<Vec<u8>>,
855 ) -> Option<(Arc<dyn CollectionRead + Send + Sync>, Arc<FrozenOverlay>)> {
856 let backing = self.backing.read();
857 let backing = backing.as_ref()?;
858 let mut overlay: FrozenOverlay = backing.removed.iter().map(|id| (*id, None)).collect();
859 self.for_each_entity_with_metadata(|entity, metadata| {
860 if let Some(bytes) = encode(entity, metadata) {
861 overlay.insert(entity.id, Some(Arc::from(bytes)));
862 }
863 true
864 });
865 Some((Arc::clone(&backing.source), Arc::new(overlay)))
866 }
867
868 pub fn current_backing_overlay(
869 &self,
870 ) -> Option<(Arc<dyn CollectionRead + Send + Sync>, Arc<FrozenOverlay>)> {
871 let version = self.backing.read().as_ref()?.format_version;
872 self.backing_overlay(|entity, metadata| {
873 Some(serialize_entity_record(entity, metadata.as_ref(), version))
874 })
875 }
876
877 fn current_backing_key_overlay(
878 &self,
879 key: &str,
880 ) -> Result<Option<(Arc<dyn CollectionRead + Send + Sync>, Arc<FrozenOverlay>)>, StoreError>
881 {
882 let backing = self.backing.read();
883 let Some(backing) = backing.as_ref() else {
884 return Ok(None);
885 };
886 let mut overlay: FrozenOverlay = backing.removed.iter().map(|id| (*id, None)).collect();
887 let schema = self.column_schema();
888 let key_column = schema
889 .as_ref()
890 .and_then(|schema| schema.iter().position(|name| name == "key"));
891 let mut failure = None;
892 self.for_each_entity_source(|segment, entity| {
893 let key_matches = match (&entity.data, key_column) {
894 (crate::storage::entity::EntityData::Row(row), Some(index)) if row.named.is_none() && row.schema.is_none() => {
895 matches!(row.columns.get(index), Some(reddb_types::Value::Text(stored)) if stored.as_ref() == key)
896 }
897 _ => crate::storage::segment_key_lookup::SegmentKeyLookup::key(entity) == Some(key),
898 };
899 let bytes = if key_matches {
900 let required_bytes = crate::storage::memory_size::entity_bytes(entity);
901 if required_bytes > backing.read_bytes_max {
902 failure = Some(StoreError::ReadLimitExceeded {
903 required_bytes,
904 limit_bytes: backing.read_bytes_max,
905 });
906 return false;
907 }
908 let metadata = segment.get_metadata(entity.id);
909 Some(Arc::from(serialize_entity_record(entity, metadata.as_ref(), backing.format_version)))
910 } else {
911 None
914 };
915 overlay.insert(entity.id, bytes);
916 true
917 });
918 if let Some(error) = failure {
919 return Err(error);
920 }
921 Ok(Some((Arc::clone(&backing.source), Arc::new(overlay))))
922 }
923}