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