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