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 predicate: impl FnMut(&UnifiedEntity) -> bool,
586 ) -> Result<(usize, Vec<UnifiedEntity>), StoreError> {
587 self.try_page_in_id_order_where_at_snapshot(None, offset, limit, limit_bytes, predicate)
588 }
589
590 pub fn try_page_in_id_order_where_at_snapshot(
594 &self,
595 snapshot: Option<&SnapshotContext>,
596 offset: usize,
597 limit: usize,
598 limit_bytes: usize,
599 mut predicate: impl FnMut(&UnifiedEntity) -> bool,
600 ) -> Result<(usize, Vec<UnifiedEntity>), StoreError> {
601 let mut visible = 0_usize;
602 let mut retained_bytes = 0_usize;
603 let mut items = Vec::new();
604 let mut error = None;
605 self.try_scan_for_each(snapshot, limit_bytes, |entity| {
606 if !predicate(entity) {
607 return true;
608 }
609 let position = visible;
610 visible = visible.saturating_add(1);
611 if position < offset || items.len() >= limit {
612 return true;
613 }
614 let required_bytes =
615 retained_bytes.saturating_add(crate::storage::memory_size::entity_bytes(entity));
616 if required_bytes > limit_bytes {
617 error = Some(StoreError::ReadLimitExceeded {
618 required_bytes,
619 limit_bytes,
620 });
621 return false;
622 }
623 retained_bytes = required_bytes;
624 items.push(entity.clone());
625 true
626 })?;
627 if let Some(error) = error {
628 return Err(error);
629 }
630 self.restore_owned_row_schemas(items.iter_mut());
631 Ok((visible, items))
632 }
633
634 pub fn try_for_each_key_entity(
637 &self,
638 key: &str,
639 mut callback: impl FnMut(&UnifiedEntity) -> bool,
640 ) -> Result<(), StoreError> {
641 if let Some(cursor) = self.try_paged_key_physical_cursor(key)? {
642 for record in cursor {
643 let (entity, _) = record?;
644 if !callback(&entity) {
645 break;
646 }
647 }
648 return Ok(());
649 }
650 self.try_for_each_entity(usize::MAX, |entity| {
651 crate::storage::segment_key_lookup::SegmentKeyLookup::key(entity) != Some(key)
652 || callback(entity)
653 })
654 }
655
656 pub fn try_scan_for_each(
657 &self,
658 snapshot: Option<&SnapshotContext>,
659 limit_bytes: usize,
660 mut callback: impl FnMut(&UnifiedEntity) -> bool,
661 ) -> Result<(), StoreError> {
662 self.try_for_each_entity(limit_bytes, |entity| {
663 !Self::scan_entity_visible(snapshot, entity) || callback(entity)
664 })
665 }
666
667 pub fn try_scan_for_each_zoned_with_stats(
670 &self,
671 snapshot: Option<&SnapshotContext>,
672 zone_preds: &[(&str, ZoneColPred<'_>)],
673 limit_bytes: usize,
674 callback: impl FnMut(&UnifiedEntity) -> bool,
675 ) -> Result<SegmentScanStats, StoreError> {
676 if !self.has_backing() {
677 return Ok(self.scan_for_each_zoned_with_stats(snapshot, zone_preds, callback));
678 }
679 self.try_scan_for_each(snapshot, limit_bytes, callback)?;
680 Ok(SegmentScanStats {
681 segments_total: 1,
682 segments_scanned: 1,
683 segments_pruned: 0,
684 })
685 }
686
687 pub fn try_scan_project_zoned_with_stats<T, F>(
690 &self,
691 snapshot: Option<&SnapshotContext>,
692 zone_preds: &[(&str, ZoneColPred<'_>)],
693 read_bytes_max: usize,
694 project: F,
695 ) -> Result<(Vec<T>, SegmentScanStats), StoreError>
696 where
697 T: Send,
698 F: Fn(&UnifiedEntity) -> Option<T> + Sync,
699 {
700 if !self.has_backing() {
701 return Ok(self.scan_project_zoned_with_stats(snapshot, zone_preds, project));
702 }
703 let mut projected = Vec::new();
704 let stats = self.try_scan_for_each_zoned_with_stats(
705 snapshot,
706 zone_preds,
707 read_bytes_max,
708 |entity| {
709 if let Some(value) = project(entity) {
710 projected.push(value);
711 }
712 true
713 },
714 )?;
715 Ok((projected, stats))
716 }
717
718 pub fn try_get_many(
721 &self,
722 ids: &[EntityId],
723 read_bytes_max: usize,
724 ) -> Result<Vec<Option<UnifiedEntity>>, StoreError> {
725 if !self.has_backing() && read_bytes_max == usize::MAX {
726 return Ok(self.get_many(ids));
727 }
728 ids.iter()
729 .map(|id| self.try_get(*id, read_bytes_max))
730 .collect()
731 }
732
733 pub fn try_for_each_id(
734 &self,
735 ids: &[EntityId],
736 read_bytes_max: usize,
737 mut visit: impl FnMut(usize, &UnifiedEntity) -> bool,
738 ) -> Result<(), StoreError> {
739 if self.has_backing() {
740 for (position, id) in ids.iter().enumerate() {
741 if let Some(entity) = self.try_get(*id, read_bytes_max)? {
742 if !visit(position, &entity) {
743 break;
744 }
745 }
746 }
747 return Ok(());
748 }
749 let mut stopped = false;
750 let mut failure = None;
751 self.for_each_id(ids, |position, entity| {
752 if stopped {
753 return;
754 }
755 let required_bytes = if read_bytes_max == usize::MAX {
756 0
757 } else {
758 crate::storage::memory_size::entity_bytes(entity)
759 };
760 if required_bytes > read_bytes_max {
761 failure = Some(StoreError::ReadLimitExceeded {
762 required_bytes,
763 limit_bytes: read_bytes_max,
764 });
765 stopped = true;
766 } else {
767 stopped = !visit(position, entity);
768 }
769 });
770 failure.map_or(Ok(()), Err)
771 }
772
773 pub(super) fn reject_backing_duplicate(&self, id: EntityId) -> Result<(), SegmentError> {
775 let backing = self.backing.read();
776 let Some(backing) = backing.as_ref() else {
777 return Ok(());
778 };
779 if !backing.removed.contains(&id)
780 && backing
781 .source
782 .get_record(id, backing.read_bytes_max)
783 .map_err(|error| SegmentError::BackingRead(error.to_string()))?
784 .is_some()
785 {
786 return Err(SegmentError::AlreadyExists(id));
787 }
788 Ok(())
789 }
790
791 pub fn try_get_metadata(
793 &self,
794 id: EntityId,
795 limit_bytes: usize,
796 ) -> Result<Option<Metadata>, StoreError> {
797 let backing = self.backing.read();
798 if self.get_with(id, |_| ()).is_some() {
799 return Ok(self.get_metadata(id));
800 }
801 let Some(backing) = backing.as_ref() else {
802 return Ok(None);
803 };
804 if backing.removed.contains(&id) {
805 return Ok(None);
806 }
807 backing
808 .source
809 .get_record(id, limit_bytes.min(backing.read_bytes_max))?
810 .map(|record| {
811 record
812 .decode_with_budget(
813 backing.format_version,
814 limit_bytes.min(backing.read_bytes_max),
815 )
816 .map(|(_, metadata)| metadata)
817 })
818 .transpose()
819 .map(Option::flatten)
820 }
821
822 pub fn rebuild_metadata_expiry(&self, id: EntityId) -> Result<(), StoreError> {
825 if let Some(entity) = self.get(id) {
826 let metadata = self.get_metadata(id);
827 self.restore_metadata_expiry(&entity, metadata.as_ref());
828 return Ok(());
829 }
830 let backing = self.backing.read();
831 let Some(backing) = backing.as_ref() else {
832 self.remove_metadata_expiry(id);
833 return Ok(());
834 };
835 if backing.removed.contains(&id) {
836 self.remove_metadata_expiry(id);
837 return Ok(());
838 }
839 let record = backing.source.get_record(id, backing.read_bytes_max)?;
840 let Some(record) = record else {
841 self.remove_metadata_expiry(id);
842 return Ok(());
843 };
844 let (entity, metadata) =
845 record.decode_with_budget(backing.format_version, backing.read_bytes_max)?;
846 self.restore_metadata_expiry(&entity, metadata.as_ref());
847 Ok(())
848 }
849
850 pub(super) fn materialize_backing_entity(&self, id: EntityId) -> Result<(), SegmentError> {
851 if self.get_with(id, |_| ()).is_some() {
852 return Ok(());
853 }
854 let record = {
855 let backing = self.backing.read();
856 let Some(backing) = backing.as_ref() else {
857 return Ok(());
858 };
859 if backing.removed.contains(&id) {
860 return Ok(());
861 }
862 backing
863 .source
864 .get_record(id, backing.read_bytes_max)
865 .and_then(|record| {
866 record
867 .map(|record| {
868 record
869 .decode_with_budget(backing.format_version, backing.read_bytes_max)
870 })
871 .transpose()
872 })
873 .map_err(|error| SegmentError::BackingRead(error.to_string()))?
874 };
875 let Some((mut entity, metadata)) = record else {
876 return Ok(());
877 };
878 self.restore_owned_row_schemas(std::iter::once(&mut entity));
879 self.restore_snapshot_entity(entity)?;
880 self.total_entities_atomic.fetch_sub(1, Ordering::Relaxed);
882 if let Some(metadata) = metadata {
883 self.set_metadata(id, metadata)?;
884 }
885 Ok(())
886 }
887
888 pub(super) fn remember_backing_delete(&self, id: EntityId) {
889 if let Some(backing) = self.backing.write().as_mut() {
890 backing.removed.insert(id);
891 }
892 }
893
894 pub fn backing_overlay(
895 &self,
896 mut encode: impl FnMut(&UnifiedEntity, Option<Metadata>) -> Option<Vec<u8>>,
897 ) -> Option<(Arc<dyn CollectionRead + Send + Sync>, Arc<FrozenOverlay>)> {
898 let backing = self.backing.read();
899 let backing = backing.as_ref()?;
900 let mut overlay: FrozenOverlay = backing.removed.iter().map(|id| (*id, None)).collect();
901 self.for_each_entity_with_metadata(|entity, metadata| {
902 if let Some(bytes) = encode(entity, metadata) {
903 overlay.insert(entity.id, Some(Arc::from(bytes)));
904 }
905 true
906 });
907 Some((Arc::clone(&backing.source), Arc::new(overlay)))
908 }
909
910 pub fn current_backing_overlay(
911 &self,
912 ) -> Option<(Arc<dyn CollectionRead + Send + Sync>, Arc<FrozenOverlay>)> {
913 let version = self.backing.read().as_ref()?.format_version;
914 self.backing_overlay(|entity, metadata| {
915 Some(serialize_entity_record(entity, metadata.as_ref(), version))
916 })
917 }
918
919 fn current_backing_key_overlay(
920 &self,
921 key: &str,
922 ) -> Result<Option<(Arc<dyn CollectionRead + Send + Sync>, Arc<FrozenOverlay>)>, StoreError>
923 {
924 let backing = self.backing.read();
925 let Some(backing) = backing.as_ref() else {
926 return Ok(None);
927 };
928 let mut overlay: FrozenOverlay = backing.removed.iter().map(|id| (*id, None)).collect();
929 let schema = self.column_schema();
930 let key_column = schema
931 .as_ref()
932 .and_then(|schema| schema.iter().position(|name| name == "key"));
933 let mut failure = None;
934 self.for_each_entity_source(|segment, entity| {
935 let key_matches = match (&entity.data, key_column) {
936 (crate::storage::entity::EntityData::Row(row), Some(index)) if row.named.is_none() && row.schema.is_none() => {
937 matches!(row.columns.get(index), Some(reddb_types::Value::Text(stored)) if stored.as_ref() == key)
938 }
939 _ => crate::storage::segment_key_lookup::SegmentKeyLookup::key(entity) == Some(key),
940 };
941 let bytes = if key_matches {
942 let required_bytes = crate::storage::memory_size::entity_bytes(entity);
943 if required_bytes > backing.read_bytes_max {
944 failure = Some(StoreError::ReadLimitExceeded {
945 required_bytes,
946 limit_bytes: backing.read_bytes_max,
947 });
948 return false;
949 }
950 let metadata = segment.get_metadata(entity.id);
951 Some(Arc::from(serialize_entity_record(entity, metadata.as_ref(), backing.format_version)))
952 } else {
953 None
956 };
957 overlay.insert(entity.id, bytes);
958 true
959 });
960 if let Some(error) = failure {
961 return Err(error);
962 }
963 Ok(Some((Arc::clone(&backing.source), Arc::new(overlay))))
964 }
965}