1use crate::{
7 db::{
8 data::{CanonicalRow, RawDataStoreKey, RawRow},
9 direction::Direction,
10 ordered_overlay::{OrderedOverlayEntry, ordered_overlay_entries},
11 positioned_overlay::{
12 JournalOverlayPosition, PositionedOverlayMetadata, PositionedOverlayRetirement,
13 },
14 },
15 types::EntityTag,
16};
17use ic_memory::{DefaultMemoryImpl, RuntimeMemory};
18use ic_storage::adapter::StableBTreeMap;
19#[cfg(test)]
20use std::cell::Cell;
21use std::collections::{BTreeMap as HeapBTreeMap, BTreeSet};
22use std::convert::Infallible;
23use std::ops::{Bound, RangeBounds};
24
25#[cfg(test)]
26thread_local! {
27 static DATA_STORE_GET_CALL_COUNT: Cell<u64> = const { Cell::new(0) };
28}
29
30#[cfg(test)]
31fn record_data_store_get_call() {
32 DATA_STORE_GET_CALL_COUNT.with(|count| {
33 count.set(count.get().saturating_add(1));
34 });
35}
36
37pub struct DataStore {
47 backend: DataStoreBackend,
48 generation: u64,
49 entity_cardinality: EntityCardinality,
50}
51
52enum DataStoreBackend {
53 Heap(HeapBTreeMap<RawDataStoreKey, RawRow>),
54 Journaled {
55 canonical: StableBTreeMap<RawDataStoreKey, RawRow, DefaultMemoryImpl>,
56 live: HeapBTreeMap<RawDataStoreKey, RawRow>,
57 tombstones: BTreeSet<RawDataStoreKey>,
58 positions: PositionedOverlayMetadata<RawDataStoreKey>,
59 entity_cardinality_delta: EntityCardinalityDelta,
60 },
61}
62
63#[cfg(feature = "migration")]
65pub(in crate::db) struct PreparedDataPositionPublication {
66 keys: Vec<RawDataStoreKey>,
67 position: JournalOverlayPosition,
68}
69
70pub(in crate::db) struct PreparedDataPositionRetirement {
72 entries: Vec<(RawDataStoreKey, PositionedOverlayRetirement)>,
73}
74
75pub(in crate::db) enum StoredRowRead<'a> {
81 Missing,
82 Borrowed(&'a RawRow),
83 Owned(RawRow),
84}
85
86impl StoredRowRead<'_> {
87 #[must_use]
89 pub(in crate::db) const fn as_row(&self) -> Option<&RawRow> {
90 match self {
91 Self::Missing => None,
92 Self::Borrowed(row) => Some(row),
93 Self::Owned(row) => Some(row),
94 }
95 }
96
97 #[must_use]
99 fn into_owned(self) -> Option<RawRow> {
100 match self {
101 Self::Missing => None,
102 Self::Borrowed(row) => Some(row.clone()),
103 Self::Owned(row) => Some(row),
104 }
105 }
106}
107
108#[derive(Clone, Copy, Debug, Eq, PartialEq)]
110pub(in crate::db) enum StoreVisit {
111 Continue,
112 Stop,
113}
114
115impl StoreVisit {
116 const fn should_stop(self) -> bool {
117 matches!(self, Self::Stop)
118 }
119}
120
121impl DataStore {
122 #[must_use]
124 pub const fn init_heap() -> Self {
125 Self {
126 backend: DataStoreBackend::Heap(HeapBTreeMap::new()),
127 generation: 0,
128 entity_cardinality: EntityCardinality::empty(),
129 }
130 }
131
132 #[must_use]
138 pub fn init_journaled(memory: RuntimeMemory<DefaultMemoryImpl>) -> Self {
139 let canonical = StableBTreeMap::init(memory);
140 let entity_cardinality = if canonical.is_empty() {
141 EntityCardinality::empty()
142 } else {
143 EntityCardinality::unavailable()
144 };
145 Self {
146 backend: DataStoreBackend::Journaled {
147 canonical,
148 live: HeapBTreeMap::new(),
149 tombstones: BTreeSet::new(),
150 positions: PositionedOverlayMetadata::new(),
151 entity_cardinality_delta: EntityCardinalityDelta::empty(),
152 },
153 generation: 0,
154 entity_cardinality,
158 }
159 }
160
161 pub(in crate::db) fn insert(
163 &mut self,
164 key: RawDataStoreKey,
165 row: CanonicalRow,
166 ) -> Option<RawRow> {
167 let row = row.into_raw_row();
168 let previous_journaled = if matches!(self.backend, DataStoreBackend::Journaled { .. }) {
169 self.get(&key)
170 } else {
171 None
172 };
173 let entity = key.entity_tag_prefix();
176 let previous = match &mut self.backend {
177 DataStoreBackend::Heap(map) => map.insert(key, row),
178 DataStoreBackend::Journaled {
179 live, tombstones, ..
180 } => {
181 tombstones.remove(&key);
182 live.insert(key, row);
183 previous_journaled
184 }
185 };
186 self.entity_cardinality
187 .apply_insert(entity, previous.is_some());
188 self.apply_entity_overlay_delta(entity, previous.is_some(), true);
189 self.bump_generation();
190 previous
191 }
192
193 #[cfg(test)]
195 pub(in crate::db) fn insert_raw_for_test(
196 &mut self,
197 key: RawDataStoreKey,
198 row: RawRow,
199 ) -> Option<RawRow> {
200 let previous_journaled = if matches!(self.backend, DataStoreBackend::Journaled { .. }) {
201 self.get(&key)
202 } else {
203 None
204 };
205 let entity = key.entity_tag_prefix();
206 let previous = match &mut self.backend {
207 DataStoreBackend::Heap(map) => map.insert(key, row),
208 DataStoreBackend::Journaled {
209 live, tombstones, ..
210 } => {
211 tombstones.remove(&key);
212 live.insert(key, row);
213 previous_journaled
214 }
215 };
216 self.entity_cardinality
217 .apply_insert(entity, previous.is_some());
218 self.apply_entity_overlay_delta(entity, previous.is_some(), true);
219 self.bump_generation();
220 previous
221 }
222
223 pub(in crate::db) fn remove(&mut self, key: &RawDataStoreKey) -> Option<RawRow> {
225 let previous_journaled = if matches!(self.backend, DataStoreBackend::Journaled { .. }) {
226 self.get(key)
227 } else {
228 None
229 };
230 let previous = match &mut self.backend {
231 DataStoreBackend::Heap(map) => map.remove(key),
232 DataStoreBackend::Journaled {
233 live, tombstones, ..
234 } => {
235 live.remove(key);
236 tombstones.insert(key.clone());
237 previous_journaled
238 }
239 };
240 self.entity_cardinality
241 .apply_remove(key.entity_tag_prefix(), previous.is_some());
242 self.apply_entity_overlay_delta(key.entity_tag_prefix(), previous.is_some(), false);
243 self.bump_generation();
244 previous
245 }
246
247 pub(in crate::db) fn reset_journaled_live_projection(
250 &mut self,
251 ) -> Result<(), crate::error::InternalError> {
252 let DataStoreBackend::Journaled {
253 canonical,
254 live,
255 tombstones,
256 positions,
257 entity_cardinality_delta,
258 } = &mut self.backend
259 else {
260 return Err(crate::error::InternalError::store_invariant());
261 };
262
263 live.clear();
264 tombstones.clear();
265 positions.clear();
266 *entity_cardinality_delta = EntityCardinalityDelta::empty();
267 self.entity_cardinality = if canonical.is_empty() {
268 EntityCardinality::empty()
269 } else {
270 EntityCardinality::unavailable()
271 };
272 self.bump_generation();
273
274 Ok(())
275 }
276
277 #[cfg(any(test, feature = "migration"))]
279 pub(in crate::db) fn apply_recovered_journal_put(
280 &mut self,
281 key: RawDataStoreKey,
282 row: RawRow,
283 ) -> Result<Option<RawRow>, crate::error::InternalError> {
284 let DataStoreBackend::Journaled {
285 canonical,
286 live,
287 tombstones,
288 ..
289 } = &mut self.backend
290 else {
291 return Err(crate::error::InternalError::store_invariant());
292 };
293
294 let previous = if tombstones.contains(&key) {
295 None
296 } else {
297 live.get(&key).cloned().or_else(|| canonical.get(&key))
298 };
299 tombstones.remove(&key);
300 let entity = key.entity_tag_prefix();
301 live.insert(key, row);
302 self.entity_cardinality
303 .apply_insert(entity, previous.is_some());
304 self.apply_entity_overlay_delta(entity, previous.is_some(), true);
305 self.bump_generation();
306
307 Ok(previous)
308 }
309
310 pub(in crate::db) fn publish_preflighted_journal_entry(
312 &mut self,
313 key: RawDataStoreKey,
314 row: Option<RawRow>,
315 position: JournalOverlayPosition,
316 ) -> Result<(), crate::error::InternalError> {
317 let previous_present = self.contains(&key);
319 let DataStoreBackend::Journaled {
320 live,
321 tombstones,
322 positions,
323 entity_cardinality_delta,
324 ..
325 } = &mut self.backend
326 else {
327 return Err(crate::error::InternalError::store_invariant());
328 };
329 let next_present = row.is_some();
330 let entity = key.entity_tag_prefix();
331 if let Some(row) = row {
332 tombstones.remove(&key);
333 live.insert(key.clone(), row);
334 self.entity_cardinality
335 .apply_insert(entity, previous_present);
336 } else {
337 live.remove(&key);
338 tombstones.insert(key.clone());
339 self.entity_cardinality
340 .apply_remove(entity, previous_present);
341 }
342 entity_cardinality_delta.apply_presence_transition(entity, previous_present, next_present);
343 positions.publish_preflighted(key, position);
344 self.bump_generation();
345
346 Ok(())
347 }
348
349 #[cfg(test)]
351 pub(in crate::db) fn publish_positioned_journal_entry(
352 &mut self,
353 key: RawDataStoreKey,
354 row: Option<RawRow>,
355 position: JournalOverlayPosition,
356 ) -> Result<(), crate::error::InternalError> {
357 self.preflight_positioned_journal_entry(&key, position)?;
358 self.publish_preflighted_journal_entry(key, row, position)
359 }
360
361 pub(in crate::db) fn preflight_positioned_journal_entry(
363 &self,
364 key: &RawDataStoreKey,
365 position: JournalOverlayPosition,
366 ) -> Result<(), crate::error::InternalError> {
367 let DataStoreBackend::Journaled { positions, .. } = &self.backend else {
368 return Err(crate::error::InternalError::store_invariant());
369 };
370 positions.preflight_publish(key, position)
371 }
372
373 #[cfg(feature = "migration")]
375 pub(in crate::db) fn prepare_position_publication(
376 &self,
377 keys: impl IntoIterator<Item = RawDataStoreKey>,
378 position: JournalOverlayPosition,
379 ) -> Result<PreparedDataPositionPublication, crate::error::InternalError> {
380 let DataStoreBackend::Journaled { positions, .. } = &self.backend else {
381 return Err(crate::error::InternalError::store_invariant());
382 };
383 let keys = keys.into_iter().collect::<BTreeSet<_>>();
384 for key in &keys {
385 positions.preflight_publish(key, position)?;
386 }
387 Ok(PreparedDataPositionPublication {
388 keys: keys.into_iter().collect(),
389 position,
390 })
391 }
392
393 #[cfg(feature = "migration")]
395 pub(in crate::db) fn publish_prepared_positions(
396 &mut self,
397 prepared: PreparedDataPositionPublication,
398 ) {
399 let DataStoreBackend::Journaled { positions, .. } = &mut self.backend else {
400 debug_assert!(false, "preflighted row positions require a journaled store");
401 return;
402 };
403 for key in prepared.keys {
404 positions.publish_preflighted(key, prepared.position);
405 }
406 }
407
408 pub(in crate::db) fn prepare_position_retirement(
410 &self,
411 keys: impl IntoIterator<Item = RawDataStoreKey>,
412 position: JournalOverlayPosition,
413 ) -> Result<PreparedDataPositionRetirement, crate::error::InternalError> {
414 let DataStoreBackend::Journaled { positions, .. } = &self.backend else {
415 return Err(crate::error::InternalError::store_invariant());
416 };
417 let entries = keys
418 .into_iter()
419 .collect::<BTreeSet<_>>()
420 .into_iter()
421 .map(|key| {
422 positions
423 .preflight_retirement(&key, position)
424 .map(|retirement| (key, retirement))
425 })
426 .collect::<Result<Vec<_>, _>>()?;
427 Ok(PreparedDataPositionRetirement { entries })
428 }
429
430 pub(in crate::db) fn apply_prepared_position_retirement(
432 &mut self,
433 prepared: PreparedDataPositionRetirement,
434 ) {
435 let DataStoreBackend::Journaled {
436 live,
437 tombstones,
438 positions,
439 ..
440 } = &mut self.backend
441 else {
442 debug_assert!(
443 false,
444 "preflighted row retirement requires a journaled store"
445 );
446 return;
447 };
448 for (key, retirement) in prepared.entries {
449 if retirement == PositionedOverlayRetirement::Exact {
450 live.remove(&key);
451 tombstones.remove(&key);
452 positions.retire_preflighted(&key, retirement);
453 }
454 }
455 }
456
457 pub(in crate::db) fn fold_recovered_journal_put(
459 &mut self,
460 key: RawDataStoreKey,
461 row: RawRow,
462 ) -> Result<Option<RawRow>, crate::error::InternalError> {
463 let DataStoreBackend::Journaled {
464 canonical,
465 live,
466 tombstones,
467 ..
468 } = &mut self.backend
469 else {
470 return Err(crate::error::InternalError::store_invariant());
471 };
472
473 let visible = !live.contains_key(&key) && !tombstones.contains(&key);
474 let entity = key.entity_tag_prefix();
475 let previous = canonical.insert(key, row);
476 if visible {
477 self.entity_cardinality
478 .apply_insert(entity, previous.is_some());
479 } else if previous.is_none() {
480 self.apply_entity_overlay_delta(entity, true, false);
481 }
482 self.bump_generation();
483
484 Ok(previous)
485 }
486
487 pub(in crate::db) fn fold_recovered_journal_delete(
489 &mut self,
490 key: &RawDataStoreKey,
491 ) -> Result<Option<RawRow>, crate::error::InternalError> {
492 let DataStoreBackend::Journaled {
493 canonical,
494 live,
495 tombstones,
496 ..
497 } = &mut self.backend
498 else {
499 return Err(crate::error::InternalError::store_invariant());
500 };
501
502 let visible = !live.contains_key(key) && !tombstones.contains(key);
503 let previous = canonical.remove(key);
504 if visible {
505 self.entity_cardinality
506 .apply_remove(key.entity_tag_prefix(), previous.is_some());
507 } else if previous.is_some() {
508 self.apply_entity_overlay_delta(key.entity_tag_prefix(), false, true);
509 }
510 self.bump_generation();
511
512 Ok(previous)
513 }
514
515 pub(in crate::db) fn preflight_fold_recovered_journal(
517 &self,
518 ) -> Result<(), crate::error::InternalError> {
519 match self.backend {
520 DataStoreBackend::Journaled { .. } => Ok(()),
521 DataStoreBackend::Heap(_) => Err(crate::error::InternalError::store_invariant()),
522 }
523 }
524
525 pub(in crate::db) fn get(&self, key: &RawDataStoreKey) -> Option<RawRow> {
527 self.read(key).into_owned()
528 }
529
530 pub(in crate::db) fn get_canonical(&self, key: &RawDataStoreKey) -> Option<RawRow> {
536 match &self.backend {
537 DataStoreBackend::Heap(map) => map.get(key).cloned(),
538 DataStoreBackend::Journaled { canonical, .. } => canonical.get(key),
539 }
540 }
541
542 #[must_use]
545 pub(in crate::db) fn contains_canonical(&self, key: &RawDataStoreKey) -> bool {
546 match &self.backend {
547 DataStoreBackend::Heap(map) => map.contains_key(key),
548 DataStoreBackend::Journaled { canonical, .. } => canonical.contains_key(key),
549 }
550 }
551
552 pub(in crate::db) fn read<'a>(&'a self, key: &RawDataStoreKey) -> StoredRowRead<'a> {
554 #[cfg(test)]
555 record_data_store_get_call();
556
557 match &self.backend {
558 DataStoreBackend::Heap(map) => map
559 .get(key)
560 .map_or(StoredRowRead::Missing, StoredRowRead::Borrowed),
561 DataStoreBackend::Journaled {
562 canonical,
563 live,
564 tombstones,
565 ..
566 } => {
567 if tombstones.contains(key) {
568 StoredRowRead::Missing
569 } else if let Some(row) = live.get(key) {
570 StoredRowRead::Borrowed(row)
571 } else {
572 canonical
573 .get(key)
574 .map_or(StoredRowRead::Missing, StoredRowRead::Owned)
575 }
576 }
577 }
578 }
579
580 #[must_use]
582 pub(in crate::db) fn contains(&self, key: &RawDataStoreKey) -> bool {
583 match &self.backend {
584 DataStoreBackend::Heap(map) => map.contains_key(key),
585 DataStoreBackend::Journaled {
586 canonical,
587 live,
588 tombstones,
589 ..
590 } => {
591 !tombstones.contains(key) && (live.contains_key(key) || canonical.contains_key(key))
592 }
593 }
594 }
595
596 #[must_use]
601 pub(in crate::db) fn is_empty(&self) -> bool {
602 let mut empty = true;
603 let _: Result<(), Infallible> = self.visit_key_range(.., |_key| {
604 empty = false;
605 Ok(StoreVisit::Stop)
606 });
607 empty
608 }
609
610 #[cfg(test)]
612 #[must_use]
613 pub(in crate::db) fn len(&self) -> u64 {
614 match &self.backend {
615 DataStoreBackend::Heap(map) => u64::try_from(map.len()).unwrap_or(u64::MAX),
616 DataStoreBackend::Journaled { .. } => {
617 let mut count = 0_u64;
618 let _: Result<(), Infallible> = self.visit_entries(|_key, _row| {
619 count = count.saturating_add(1);
620 Ok(StoreVisit::Continue)
621 });
622 count
623 }
624 }
625 }
626
627 #[must_use]
629 pub(in crate::db) const fn generation(&self) -> u64 {
630 self.generation
631 }
632
633 #[must_use]
635 pub(in crate::db) fn exact_entity_count(&self, entity: EntityTag) -> Option<u64> {
636 self.entity_cardinality.exact_count(entity)
637 }
638
639 #[must_use]
641 pub(in crate::db) fn exact_entity_cardinality_delta(&self, entity: EntityTag) -> Option<i64> {
642 match &self.backend {
643 DataStoreBackend::Heap(_) => Some(0),
644 DataStoreBackend::Journaled {
645 entity_cardinality_delta,
646 ..
647 } => entity_cardinality_delta.exact_delta(entity),
648 }
649 }
650
651 pub(in crate::db) fn visit_entries<E>(
653 &self,
654 mut visitor: impl FnMut(&RawDataStoreKey, &RawRow) -> Result<StoreVisit, E>,
655 ) -> Result<(), E> {
656 match &self.backend {
657 DataStoreBackend::Heap(map) => {
658 for (key, row) in map {
659 if visitor(key, row)?.should_stop() {
660 break;
661 }
662 }
663 }
664 DataStoreBackend::Journaled { .. } => Self::visit_journaled_entries_in_bounds(
665 &self.backend,
666 (Bound::Unbounded, Bound::Unbounded),
667 &mut visitor,
668 )?,
669 }
670
671 Ok(())
672 }
673
674 pub(in crate::db) fn visit_canonical_entries_after(
679 &self,
680 checkpoint: Option<&RawDataStoreKey>,
681 mut visitor: impl FnMut(&RawDataStoreKey, &RawRow) -> Result<bool, crate::error::InternalError>,
682 ) -> Result<(), crate::error::InternalError> {
683 let lower = checkpoint.map_or(Bound::Unbounded, Bound::Excluded);
684 match &self.backend {
685 DataStoreBackend::Heap(map) => {
686 for (key, row) in map.range((lower, Bound::Unbounded)) {
687 if visitor(key, row)? {
688 break;
689 }
690 }
691 }
692 DataStoreBackend::Journaled { canonical, .. } => {
693 for entry in canonical.range((lower, Bound::Unbounded)) {
694 if visitor(entry.key(), &entry.value())? {
695 break;
696 }
697 }
698 }
699 }
700 Ok(())
701 }
702
703 pub(in crate::db) fn canonical_is_empty(&self) -> Result<bool, crate::error::InternalError> {
708 match &self.backend {
709 DataStoreBackend::Journaled { canonical, .. } => Ok(canonical.is_empty()),
710 DataStoreBackend::Heap(_) => Err(crate::error::InternalError::store_invariant()),
711 }
712 }
713
714 pub(in crate::db) fn visit_range<E>(
716 &self,
717 key_range: impl RangeBounds<RawDataStoreKey>,
718 mut visitor: impl FnMut(&RawDataStoreKey, &RawRow) -> Result<StoreVisit, E>,
719 ) -> Result<(), E> {
720 let bounds = (key_range.start_bound(), key_range.end_bound());
721 match &self.backend {
722 DataStoreBackend::Heap(map) => {
723 for (key, row) in map.range(bounds) {
724 if visitor(key, row)?.should_stop() {
725 break;
726 }
727 }
728 }
729 DataStoreBackend::Journaled { .. } => {
730 Self::visit_journaled_entries_in_bounds(&self.backend, bounds, &mut visitor)?;
731 }
732 }
733
734 Ok(())
735 }
736
737 pub(in crate::db) fn try_visit_range_with_row_preflight<E>(
744 &self,
745 key_range: impl RangeBounds<RawDataStoreKey>,
746 mut preflight: impl FnMut(&RawDataStoreKey) -> Result<StoreVisit, E>,
747 mut visitor: impl FnMut(&RawDataStoreKey, &RawRow) -> Result<StoreVisit, E>,
748 ) -> Result<Option<bool>, E> {
749 let bounds = (key_range.start_bound(), key_range.end_bound());
750 let mut stopped = false;
751 match &self.backend {
752 DataStoreBackend::Heap(map) => {
753 for (key, row) in map.range(bounds) {
754 if preflight(key)?.should_stop() {
755 stopped = true;
756 break;
757 }
758 if visitor(key, row)?.should_stop() {
759 stopped = true;
760 break;
761 }
762 }
763 }
764 DataStoreBackend::Journaled {
765 canonical,
766 live,
767 tombstones,
768 ..
769 } if canonical.is_empty() => {
770 for (key, row) in live.range(bounds) {
771 if tombstones.contains(key) {
772 continue;
773 }
774 if preflight(key)?.should_stop() {
775 stopped = true;
776 break;
777 }
778 if visitor(key, row)?.should_stop() {
779 stopped = true;
780 break;
781 }
782 }
783 }
784 DataStoreBackend::Journaled {
785 canonical,
786 live,
787 tombstones,
788 ..
789 } if live.is_empty() && tombstones.is_empty() => {
790 for entry in canonical.range(bounds) {
791 if preflight(entry.key())?.should_stop() {
792 stopped = true;
793 break;
794 }
795 if visitor(entry.key(), &entry.value())?.should_stop() {
796 stopped = true;
797 break;
798 }
799 }
800 }
801 DataStoreBackend::Journaled { .. } => return Ok(None),
802 }
803
804 Ok(Some(!stopped))
805 }
806
807 pub(in crate::db) fn visit_key_range<E>(
814 &self,
815 key_range: impl RangeBounds<RawDataStoreKey>,
816 visitor: impl FnMut(&RawDataStoreKey) -> Result<StoreVisit, E>,
817 ) -> Result<(), E> {
818 self.visit_keys_in_bounds(
819 (key_range.start_bound(), key_range.end_bound()),
820 false,
821 visitor,
822 )
823 }
824
825 pub(in crate::db) fn visit_key_range_rev<E>(
827 &self,
828 key_range: impl RangeBounds<RawDataStoreKey>,
829 visitor: impl FnMut(&RawDataStoreKey) -> Result<StoreVisit, E>,
830 ) -> Result<(), E> {
831 self.visit_keys_in_bounds(
832 (key_range.start_bound(), key_range.end_bound()),
833 true,
834 visitor,
835 )
836 }
837
838 #[cfg(test)]
840 pub(in crate::db) fn memory_bytes(&self) -> u64 {
841 let mut bytes = 0u64;
843 let _: Result<(), Infallible> = self.visit_entries(|key, row| {
844 bytes = bytes.saturating_add(key.as_bytes().len() as u64 + row.len() as u64);
845 Ok(StoreVisit::Continue)
846 });
847 bytes
848 }
849
850 const fn bump_generation(&mut self) {
851 self.generation = self.generation.saturating_add(1);
852 }
853
854 #[cfg(test)]
855 fn rebuild_entity_cardinality_from_entries(&mut self) {
856 let mut cardinality = EntityCardinality::empty();
857 let _: Result<(), Infallible> = self.visit_entries(|key, _row| {
858 cardinality.apply_present_entity(key.entity_tag_prefix());
859 Ok(StoreVisit::Continue)
860 });
861 self.entity_cardinality = cardinality;
862 }
863
864 fn apply_entity_overlay_delta(
865 &mut self,
866 entity: Option<EntityTag>,
867 previous_present: bool,
868 next_present: bool,
869 ) {
870 let DataStoreBackend::Journaled {
871 entity_cardinality_delta,
872 ..
873 } = &mut self.backend
874 else {
875 return;
876 };
877 entity_cardinality_delta.apply_presence_transition(entity, previous_present, next_present);
878 }
879
880 #[cfg(test)]
882 pub(in crate::db) fn current_get_call_count() -> u64 {
883 DATA_STORE_GET_CALL_COUNT.with(Cell::get)
884 }
885
886 fn visit_keys_in_bounds<E>(
887 &self,
888 bounds: (Bound<&RawDataStoreKey>, Bound<&RawDataStoreKey>),
889 reverse: bool,
890 mut visitor: impl FnMut(&RawDataStoreKey) -> Result<StoreVisit, E>,
891 ) -> Result<(), E> {
892 match &self.backend {
893 DataStoreBackend::Heap(map) => {
894 if reverse {
895 for (key, _row) in map.range(bounds).rev() {
896 if visitor(key)?.should_stop() {
897 break;
898 }
899 }
900 } else {
901 for (key, _row) in map.range(bounds) {
902 if visitor(key)?.should_stop() {
903 break;
904 }
905 }
906 }
907 }
908 DataStoreBackend::Journaled { .. } => {
909 Self::visit_journaled_keys_in_bounds(&self.backend, bounds, reverse, &mut visitor)?;
910 }
911 }
912
913 Ok(())
914 }
915
916 fn visit_journaled_keys_in_bounds<E>(
919 backend: &DataStoreBackend,
920 bounds: (Bound<&RawDataStoreKey>, Bound<&RawDataStoreKey>),
921 reverse: bool,
922 visitor: &mut dyn FnMut(&RawDataStoreKey) -> Result<StoreVisit, E>,
923 ) -> Result<(), E> {
924 let DataStoreBackend::Journaled {
925 canonical,
926 live,
927 tombstones,
928 ..
929 } = backend
930 else {
931 return Ok(());
932 };
933
934 if canonical.is_empty() {
935 if reverse {
936 for (key, _row) in live.range(bounds).rev() {
937 if visitor(key)?.should_stop() {
938 return Ok(());
939 }
940 }
941 } else {
942 for (key, _row) in live.range(bounds) {
943 if visitor(key)?.should_stop() {
944 return Ok(());
945 }
946 }
947 }
948 return Ok(());
949 }
950
951 if live.is_empty() && tombstones.is_empty() {
952 if reverse {
953 for entry in canonical.range(bounds).rev() {
954 if visitor(entry.key())?.should_stop() {
955 return Ok(());
956 }
957 }
958 } else {
959 for entry in canonical.range(bounds) {
960 if visitor(entry.key())?.should_stop() {
961 return Ok(());
962 }
963 }
964 }
965 return Ok(());
966 }
967
968 let direction = if reverse {
969 Direction::Desc
970 } else {
971 Direction::Asc
972 };
973 match direction {
974 Direction::Asc => {
975 for entry in ordered_overlay_entries(
976 canonical.range(bounds),
977 live.range(bounds),
978 direction,
979 |entry| entry.key(),
980 |entry| entry.0,
981 tombstones,
982 ) {
983 let visit = match entry {
984 OrderedOverlayEntry::Canonical(canonical_entry) => {
985 visitor(canonical_entry.key())?
986 }
987 OrderedOverlayEntry::Live((key, _row)) => visitor(key)?,
988 };
989 if visit.should_stop() {
990 return Ok(());
991 }
992 }
993 }
994 Direction::Desc => {
995 for entry in ordered_overlay_entries(
996 canonical.range(bounds).rev(),
997 live.range(bounds).rev(),
998 direction,
999 |entry| entry.key(),
1000 |entry| entry.0,
1001 tombstones,
1002 ) {
1003 let visit = match entry {
1004 OrderedOverlayEntry::Canonical(canonical_entry) => {
1005 visitor(canonical_entry.key())?
1006 }
1007 OrderedOverlayEntry::Live((key, _row)) => visitor(key)?,
1008 };
1009 if visit.should_stop() {
1010 return Ok(());
1011 }
1012 }
1013 }
1014 }
1015
1016 Ok(())
1017 }
1018
1019 fn visit_journaled_entries_in_bounds<E>(
1022 backend: &DataStoreBackend,
1023 bounds: (Bound<&RawDataStoreKey>, Bound<&RawDataStoreKey>),
1024 visitor: &mut dyn FnMut(&RawDataStoreKey, &RawRow) -> Result<StoreVisit, E>,
1025 ) -> Result<(), E> {
1026 let DataStoreBackend::Journaled {
1027 canonical,
1028 live,
1029 tombstones,
1030 ..
1031 } = backend
1032 else {
1033 return Ok(());
1034 };
1035
1036 if canonical.is_empty() {
1037 for (key, row) in live.range(bounds) {
1038 if visitor(key, row)?.should_stop() {
1039 return Ok(());
1040 }
1041 }
1042 return Ok(());
1043 }
1044
1045 if live.is_empty() && tombstones.is_empty() {
1046 for entry in canonical.range(bounds) {
1047 if visitor(entry.key(), &entry.value())?.should_stop() {
1048 return Ok(());
1049 }
1050 }
1051 return Ok(());
1052 }
1053
1054 for entry in ordered_overlay_entries(
1055 canonical.range(bounds),
1056 live.range(bounds),
1057 Direction::Asc,
1058 |entry| entry.key(),
1059 |entry| entry.0,
1060 tombstones,
1061 ) {
1062 let visit = match entry {
1063 OrderedOverlayEntry::Canonical(canonical_entry) => {
1064 visitor(canonical_entry.key(), &canonical_entry.value())?
1065 }
1066 OrderedOverlayEntry::Live((key, row)) => visitor(key, row)?,
1067 };
1068 if visit.should_stop() {
1069 return Ok(());
1070 }
1071 }
1072
1073 Ok(())
1074 }
1075}
1076
1077#[derive(Clone, Debug)]
1078struct EntityCardinality {
1079 counts: HeapBTreeMap<EntityTag, u64>,
1080 decodable: bool,
1081}
1082
1083#[derive(Clone, Debug)]
1084struct EntityCardinalityDelta {
1085 counts: HeapBTreeMap<EntityTag, i64>,
1086 decodable: bool,
1087}
1088
1089impl EntityCardinalityDelta {
1090 const fn empty() -> Self {
1091 Self {
1092 counts: HeapBTreeMap::new(),
1093 decodable: true,
1094 }
1095 }
1096
1097 fn exact_delta(&self, entity: EntityTag) -> Option<i64> {
1098 self.decodable
1099 .then(|| self.counts.get(&entity).copied().unwrap_or(0))
1100 }
1101
1102 fn apply_presence_transition(
1103 &mut self,
1104 entity: Option<EntityTag>,
1105 previous_present: bool,
1106 next_present: bool,
1107 ) {
1108 if !self.decodable || previous_present == next_present {
1109 return;
1110 }
1111 let Some(entity) = entity else {
1112 self.counts.clear();
1113 self.decodable = false;
1114 return;
1115 };
1116 let delta = if next_present { 1_i64 } else { -1_i64 };
1117 let count = self.counts.entry(entity).or_insert(0);
1118 let Some(next) = count.checked_add(delta) else {
1119 self.counts.clear();
1120 self.decodable = false;
1121 return;
1122 };
1123 *count = next;
1124 if *count == 0 {
1125 self.counts.remove(&entity);
1126 }
1127 }
1128}
1129
1130impl EntityCardinality {
1131 const fn empty() -> Self {
1132 Self {
1133 counts: HeapBTreeMap::new(),
1134 decodable: true,
1135 }
1136 }
1137
1138 const fn unavailable() -> Self {
1139 Self {
1140 counts: HeapBTreeMap::new(),
1141 decodable: false,
1142 }
1143 }
1144
1145 fn exact_count(&self, entity: EntityTag) -> Option<u64> {
1146 self.decodable
1147 .then(|| self.counts.get(&entity).copied().unwrap_or(0))
1148 }
1149
1150 fn apply_insert(&mut self, entity: Option<EntityTag>, previous_present: bool) {
1151 if previous_present {
1152 return;
1153 }
1154 self.apply_present_entity(entity);
1155 }
1156
1157 fn apply_remove(&mut self, entity: Option<EntityTag>, previous_present: bool) {
1158 if !previous_present {
1159 return;
1160 }
1161 self.apply_removed_entity(entity);
1162 }
1163
1164 fn apply_present_entity(&mut self, entity: Option<EntityTag>) {
1165 if !self.decodable {
1166 return;
1167 }
1168 let Some(entity) = entity else {
1169 self.invalidate();
1170 return;
1171 };
1172
1173 let count = self.counts.entry(entity).or_insert(0);
1174 *count = count.saturating_add(1);
1175 }
1176
1177 fn apply_removed_entity(&mut self, entity: Option<EntityTag>) {
1178 if !self.decodable {
1179 return;
1180 }
1181 let Some(entity) = entity else {
1182 self.invalidate();
1183 return;
1184 };
1185
1186 if let Some(count) = self.counts.get_mut(&entity) {
1187 *count = count.saturating_sub(1);
1188 if *count == 0 {
1189 self.counts.remove(&entity);
1190 }
1191 }
1192 }
1193
1194 fn invalidate(&mut self) {
1195 self.counts.clear();
1196 self.decodable = false;
1197 }
1198}
1199
1200#[cfg(test)]
1201mod tests;