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_for_each_key_entity(
565 &self,
566 key: &str,
567 mut callback: impl FnMut(&UnifiedEntity) -> bool,
568 ) -> Result<(), StoreError> {
569 if let Some(cursor) = self.try_paged_key_physical_cursor(key)? {
570 for record in cursor {
571 let (entity, _) = record?;
572 if !callback(&entity) {
573 break;
574 }
575 }
576 return Ok(());
577 }
578 self.try_for_each_entity(usize::MAX, |entity| {
579 crate::storage::segment_key_lookup::SegmentKeyLookup::key(entity) != Some(key)
580 || callback(entity)
581 })
582 }
583
584 pub fn try_scan_for_each(
585 &self,
586 snapshot: Option<&SnapshotContext>,
587 limit_bytes: usize,
588 mut callback: impl FnMut(&UnifiedEntity) -> bool,
589 ) -> Result<(), StoreError> {
590 self.try_for_each_entity(limit_bytes, |entity| {
591 !Self::scan_entity_visible(snapshot, entity) || callback(entity)
592 })
593 }
594
595 pub fn try_scan_for_each_zoned_with_stats(
598 &self,
599 snapshot: Option<&SnapshotContext>,
600 zone_preds: &[(&str, ZoneColPred<'_>)],
601 limit_bytes: usize,
602 callback: impl FnMut(&UnifiedEntity) -> bool,
603 ) -> Result<SegmentScanStats, StoreError> {
604 if !self.has_backing() {
605 return Ok(self.scan_for_each_zoned_with_stats(snapshot, zone_preds, callback));
606 }
607 self.try_scan_for_each(snapshot, limit_bytes, callback)?;
608 Ok(SegmentScanStats {
609 segments_total: 1,
610 segments_scanned: 1,
611 segments_pruned: 0,
612 })
613 }
614
615 pub fn try_scan_project_zoned_with_stats<T, F>(
618 &self,
619 snapshot: Option<&SnapshotContext>,
620 zone_preds: &[(&str, ZoneColPred<'_>)],
621 read_bytes_max: usize,
622 project: F,
623 ) -> Result<(Vec<T>, SegmentScanStats), StoreError>
624 where
625 T: Send,
626 F: Fn(&UnifiedEntity) -> Option<T> + Sync,
627 {
628 if !self.has_backing() {
629 return Ok(self.scan_project_zoned_with_stats(snapshot, zone_preds, project));
630 }
631 let mut projected = Vec::new();
632 let stats = self.try_scan_for_each_zoned_with_stats(
633 snapshot,
634 zone_preds,
635 read_bytes_max,
636 |entity| {
637 if let Some(value) = project(entity) {
638 projected.push(value);
639 }
640 true
641 },
642 )?;
643 Ok((projected, stats))
644 }
645
646 pub fn try_get_many(
649 &self,
650 ids: &[EntityId],
651 read_bytes_max: usize,
652 ) -> Result<Vec<Option<UnifiedEntity>>, StoreError> {
653 if !self.has_backing() && read_bytes_max == usize::MAX {
654 return Ok(self.get_many(ids));
655 }
656 ids.iter()
657 .map(|id| self.try_get(*id, read_bytes_max))
658 .collect()
659 }
660
661 pub fn try_for_each_id(
662 &self,
663 ids: &[EntityId],
664 read_bytes_max: usize,
665 mut visit: impl FnMut(usize, &UnifiedEntity) -> bool,
666 ) -> Result<(), StoreError> {
667 if self.has_backing() {
668 for (position, id) in ids.iter().enumerate() {
669 if let Some(entity) = self.try_get(*id, read_bytes_max)? {
670 if !visit(position, &entity) {
671 break;
672 }
673 }
674 }
675 return Ok(());
676 }
677 let mut stopped = false;
678 let mut failure = None;
679 self.for_each_id(ids, |position, entity| {
680 if stopped {
681 return;
682 }
683 let required_bytes = if read_bytes_max == usize::MAX {
684 0
685 } else {
686 crate::storage::memory_size::entity_bytes(entity)
687 };
688 if required_bytes > read_bytes_max {
689 failure = Some(StoreError::ReadLimitExceeded {
690 required_bytes,
691 limit_bytes: read_bytes_max,
692 });
693 stopped = true;
694 } else {
695 stopped = !visit(position, entity);
696 }
697 });
698 failure.map_or(Ok(()), Err)
699 }
700
701 pub(super) fn reject_backing_duplicate(&self, id: EntityId) -> Result<(), SegmentError> {
703 let backing = self.backing.read();
704 let Some(backing) = backing.as_ref() else {
705 return Ok(());
706 };
707 if !backing.removed.contains(&id)
708 && backing
709 .source
710 .get_record(id, backing.read_bytes_max)
711 .map_err(|error| SegmentError::BackingRead(error.to_string()))?
712 .is_some()
713 {
714 return Err(SegmentError::AlreadyExists(id));
715 }
716 Ok(())
717 }
718
719 pub fn try_get_metadata(
721 &self,
722 id: EntityId,
723 limit_bytes: usize,
724 ) -> Result<Option<Metadata>, StoreError> {
725 let backing = self.backing.read();
726 if self.get_with(id, |_| ()).is_some() {
727 return Ok(self.get_metadata(id));
728 }
729 let Some(backing) = backing.as_ref() else {
730 return Ok(None);
731 };
732 if backing.removed.contains(&id) {
733 return Ok(None);
734 }
735 backing
736 .source
737 .get_record(id, limit_bytes.min(backing.read_bytes_max))?
738 .map(|record| {
739 record
740 .decode_with_budget(
741 backing.format_version,
742 limit_bytes.min(backing.read_bytes_max),
743 )
744 .map(|(_, metadata)| metadata)
745 })
746 .transpose()
747 .map(Option::flatten)
748 }
749
750 pub(super) fn materialize_backing_entity(&self, id: EntityId) -> Result<(), SegmentError> {
751 if self.get_with(id, |_| ()).is_some() {
752 return Ok(());
753 }
754 let record = {
755 let backing = self.backing.read();
756 let Some(backing) = backing.as_ref() else {
757 return Ok(());
758 };
759 if backing.removed.contains(&id) {
760 return Ok(());
761 }
762 backing
763 .source
764 .get_record(id, backing.read_bytes_max)
765 .and_then(|record| {
766 record
767 .map(|record| {
768 record
769 .decode_with_budget(backing.format_version, backing.read_bytes_max)
770 })
771 .transpose()
772 })
773 .map_err(|error| SegmentError::BackingRead(error.to_string()))?
774 };
775 let Some((mut entity, metadata)) = record else {
776 return Ok(());
777 };
778 self.restore_owned_row_schemas(std::iter::once(&mut entity));
779 self.restore_snapshot_entity(entity)?;
780 self.total_entities_atomic.fetch_sub(1, Ordering::Relaxed);
782 if let Some(metadata) = metadata {
783 self.set_metadata(id, metadata)?;
784 }
785 Ok(())
786 }
787
788 pub(super) fn remember_backing_delete(&self, id: EntityId) {
789 if let Some(backing) = self.backing.write().as_mut() {
790 backing.removed.insert(id);
791 }
792 }
793
794 pub fn backing_overlay(
795 &self,
796 mut encode: impl FnMut(&UnifiedEntity, Option<Metadata>) -> Option<Vec<u8>>,
797 ) -> Option<(Arc<dyn CollectionRead + Send + Sync>, Arc<FrozenOverlay>)> {
798 let backing = self.backing.read();
799 let backing = backing.as_ref()?;
800 let mut overlay: FrozenOverlay = backing.removed.iter().map(|id| (*id, None)).collect();
801 self.for_each_entity_with_metadata(|entity, metadata| {
802 if let Some(bytes) = encode(entity, metadata) {
803 overlay.insert(entity.id, Some(Arc::from(bytes)));
804 }
805 true
806 });
807 Some((Arc::clone(&backing.source), Arc::new(overlay)))
808 }
809
810 pub fn current_backing_overlay(
811 &self,
812 ) -> Option<(Arc<dyn CollectionRead + Send + Sync>, Arc<FrozenOverlay>)> {
813 let version = self.backing.read().as_ref()?.format_version;
814 self.backing_overlay(|entity, metadata| {
815 Some(serialize_entity_record(entity, metadata.as_ref(), version))
816 })
817 }
818
819 fn current_backing_key_overlay(
820 &self,
821 key: &str,
822 ) -> Result<Option<(Arc<dyn CollectionRead + Send + Sync>, Arc<FrozenOverlay>)>, StoreError>
823 {
824 let backing = self.backing.read();
825 let Some(backing) = backing.as_ref() else {
826 return Ok(None);
827 };
828 let mut overlay: FrozenOverlay = backing.removed.iter().map(|id| (*id, None)).collect();
829 let schema = self.column_schema();
830 let key_column = schema
831 .as_ref()
832 .and_then(|schema| schema.iter().position(|name| name == "key"));
833 let mut failure = None;
834 self.for_each_entity_source(|segment, entity| {
835 let key_matches = match (&entity.data, key_column) {
836 (crate::storage::entity::EntityData::Row(row), Some(index)) if row.named.is_none() && row.schema.is_none() => {
837 matches!(row.columns.get(index), Some(reddb_types::Value::Text(stored)) if stored.as_ref() == key)
838 }
839 _ => crate::storage::segment_key_lookup::SegmentKeyLookup::key(entity) == Some(key),
840 };
841 let bytes = if key_matches {
842 let required_bytes = crate::storage::memory_size::entity_bytes(entity);
843 if required_bytes > backing.read_bytes_max {
844 failure = Some(StoreError::ReadLimitExceeded {
845 required_bytes,
846 limit_bytes: backing.read_bytes_max,
847 });
848 return false;
849 }
850 let metadata = segment.get_metadata(entity.id);
851 Some(Arc::from(serialize_entity_record(entity, metadata.as_ref(), backing.format_version)))
852 } else {
853 None
856 };
857 overlay.insert(entity.id, bytes);
858 true
859 });
860 if let Some(error) = failure {
861 return Err(error);
862 }
863 Ok(Some((Arc::clone(&backing.source), Arc::new(overlay))))
864 }
865}