use crate::db::{
commit::database_incarnation_id,
data::DataStore,
index::{IndexId, IndexKeyKind, IndexState, IndexStore, UserIndexPrefixCardinalityKey},
integrity::DatabaseIncarnationId,
journal::{FoldWatermark, JournalTailStore},
schema::{
SchemaStore,
cardinality_build::CardinalityBuildAuthority,
cardinality_generation::{
CardinalityAcceptedRootIdentity, CardinalityCountDigest, CardinalityGenerationState,
CardinalityStoreAllocationIdentity,
},
},
};
use crate::{error::InternalError, types::EntityTag};
use candid::CandidType;
use serde::Deserialize;
use std::{cell::RefCell, thread::LocalKey};
#[derive(Clone, Copy, Debug)]
pub struct StoreHandle {
data: &'static LocalKey<RefCell<DataStore>>,
index: &'static LocalKey<RefCell<IndexStore>>,
schema: &'static LocalKey<RefCell<SchemaStore>>,
journal: Option<&'static LocalKey<RefCell<JournalTailStore>>>,
allocations: StoreAllocationIdentities,
cardinality_allocation: Option<CardinalityStoreAllocationIdentity>,
capabilities: StoreRuntimeStorageCapabilities,
}
enum ReadyCardinalityCountTargets<'a> {
Digests(&'a [CardinalityCountDigest]),
UserIndexPrefixes(&'a [UserIndexPrefixCardinalityKey]),
}
enum ReadyCardinalitySource {
Current {
database_incarnation: DatabaseIncarnationId,
},
Admitted {
database_incarnation: DatabaseIncarnationId,
accepted_root: CardinalityAcceptedRootIdentity,
fold_watermark: FoldWatermark,
},
}
impl ReadyCardinalityCountTargets<'_> {
const fn len(&self) -> usize {
match self {
Self::Digests(digests) => digests.len(),
Self::UserIndexPrefixes(keys) => keys.len(),
}
}
}
#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
pub enum StoreRuntimeStorageMode {
#[default]
Heap,
Journaled,
}
impl StoreRuntimeStorageMode {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Heap => "heap",
Self::Journaled => "journaled",
}
}
}
#[derive(CandidType, Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
pub enum StoreAllocationIdentityCapability {
#[default]
Present,
Absent,
}
impl StoreAllocationIdentityCapability {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Present => "present",
Self::Absent => "absent",
}
}
}
#[derive(CandidType, Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
pub enum StoreDurability {
#[default]
Durable,
Volatile,
}
impl StoreDurability {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Durable => "durable",
Self::Volatile => "volatile",
}
}
}
#[derive(CandidType, Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
pub enum StoreRecoveryCapability {
#[default]
StableBasePlusJournalReplay,
None,
}
impl StoreRecoveryCapability {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::StableBasePlusJournalReplay => "stable-base-plus-journal-replay",
Self::None => "none",
}
}
}
#[derive(CandidType, Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
pub enum StoreCommitParticipation {
#[default]
Durable,
LiveOnly,
}
impl StoreCommitParticipation {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Durable => "durable",
Self::LiveOnly => "live-only",
}
}
}
#[derive(CandidType, Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
pub enum StoreSchemaMetadataCapability {
LiveRebuiltMetadata,
#[default]
CanonicalStableHistoryPlusJournalTail,
}
impl StoreSchemaMetadataCapability {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::LiveRebuiltMetadata => "live-rebuilt-metadata",
Self::CanonicalStableHistoryPlusJournalTail => {
"canonical-stable-history-plus-journal-tail"
}
}
}
}
#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
pub enum StoreRelationSourceCapability {
#[default]
DurableSource,
LiveSource,
}
#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
pub enum StoreRelationTargetCapability {
#[default]
DurableTarget,
VolatileTarget,
}
#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
pub struct StoreRuntimeStorageCapabilities {
storage_mode: StoreRuntimeStorageMode,
allocation_identity: StoreAllocationIdentityCapability,
durability: StoreDurability,
recovery: StoreRecoveryCapability,
commit_participation: StoreCommitParticipation,
schema_metadata: StoreSchemaMetadataCapability,
relation_source: StoreRelationSourceCapability,
relation_target: StoreRelationTargetCapability,
}
impl StoreRuntimeStorageCapabilities {
#[must_use]
pub const fn heap() -> Self {
Self {
storage_mode: StoreRuntimeStorageMode::Heap,
allocation_identity: StoreAllocationIdentityCapability::Absent,
durability: StoreDurability::Volatile,
recovery: StoreRecoveryCapability::None,
commit_participation: StoreCommitParticipation::LiveOnly,
schema_metadata: StoreSchemaMetadataCapability::LiveRebuiltMetadata,
relation_source: StoreRelationSourceCapability::LiveSource,
relation_target: StoreRelationTargetCapability::VolatileTarget,
}
}
#[must_use]
pub const fn journaled() -> Self {
Self {
storage_mode: StoreRuntimeStorageMode::Journaled,
allocation_identity: StoreAllocationIdentityCapability::Present,
durability: StoreDurability::Durable,
recovery: StoreRecoveryCapability::StableBasePlusJournalReplay,
commit_participation: StoreCommitParticipation::Durable,
schema_metadata: StoreSchemaMetadataCapability::CanonicalStableHistoryPlusJournalTail,
relation_source: StoreRelationSourceCapability::DurableSource,
relation_target: StoreRelationTargetCapability::DurableTarget,
}
}
#[must_use]
pub const fn storage_mode(self) -> StoreRuntimeStorageMode {
self.storage_mode
}
#[must_use]
pub const fn allocation_identity(self) -> StoreAllocationIdentityCapability {
self.allocation_identity
}
#[must_use]
pub const fn durability(self) -> StoreDurability {
self.durability
}
#[must_use]
pub const fn recovery(self) -> StoreRecoveryCapability {
self.recovery
}
#[must_use]
pub const fn commit_participation(self) -> StoreCommitParticipation {
self.commit_participation
}
#[must_use]
pub const fn schema_metadata(self) -> StoreSchemaMetadataCapability {
self.schema_metadata
}
#[must_use]
pub const fn relation_source(self) -> StoreRelationSourceCapability {
self.relation_source
}
#[must_use]
pub const fn relation_target(self) -> StoreRelationTargetCapability {
self.relation_target
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct StoreAllocationIdentity {
memory_id: u8,
stable_key: &'static str,
}
impl StoreAllocationIdentity {
#[must_use]
pub const fn new(memory_id: u8, stable_key: &'static str) -> Self {
Self {
memory_id,
stable_key,
}
}
#[must_use]
pub const fn memory_id(self) -> u8 {
self.memory_id
}
#[must_use]
pub const fn stable_key(self) -> &'static str {
self.stable_key
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct StoreAllocationIdentities {
data: Option<StoreAllocationIdentity>,
index: Option<StoreAllocationIdentity>,
schema: Option<StoreAllocationIdentity>,
journal: Option<StoreAllocationIdentity>,
}
impl StoreAllocationIdentities {
#[must_use]
pub const fn absent() -> Self {
Self {
data: None,
index: None,
schema: None,
journal: None,
}
}
#[must_use]
pub const fn new_journaled(
data: StoreAllocationIdentity,
index: StoreAllocationIdentity,
schema: StoreAllocationIdentity,
journal: StoreAllocationIdentity,
) -> Self {
Self {
data: Some(data),
index: Some(index),
schema: Some(schema),
journal: Some(journal),
}
}
#[must_use]
pub const fn data(self) -> Option<StoreAllocationIdentity> {
self.data
}
#[must_use]
pub const fn index(self) -> Option<StoreAllocationIdentity> {
self.index
}
#[must_use]
pub const fn schema(self) -> Option<StoreAllocationIdentity> {
self.schema
}
#[must_use]
pub const fn journal(self) -> Option<StoreAllocationIdentity> {
self.journal
}
#[must_use]
pub const fn allocation_identity_capability(self) -> Option<StoreAllocationIdentityCapability> {
match (self.data, self.index, self.schema) {
(Some(_), Some(_), Some(_)) => Some(StoreAllocationIdentityCapability::Present),
(None, None, None) if self.journal.is_none() => {
Some(StoreAllocationIdentityCapability::Absent)
}
_ => None,
}
}
#[must_use]
pub const fn matches_storage_capabilities(
self,
capabilities: StoreRuntimeStorageCapabilities,
) -> bool {
match capabilities.storage_mode() {
StoreRuntimeStorageMode::Heap => {
self.data.is_none()
&& self.index.is_none()
&& self.schema.is_none()
&& self.journal.is_none()
}
StoreRuntimeStorageMode::Journaled => {
self.data.is_some()
&& self.index.is_some()
&& self.schema.is_some()
&& self.journal.is_some()
}
}
}
}
impl StoreHandle {
#[must_use]
pub const fn new(
data: &'static LocalKey<RefCell<DataStore>>,
index: &'static LocalKey<RefCell<IndexStore>>,
schema: &'static LocalKey<RefCell<SchemaStore>>,
allocations: StoreAllocationIdentities,
capabilities: StoreRuntimeStorageCapabilities,
) -> Self {
Self {
data,
index,
schema,
journal: None,
allocations,
cardinality_allocation: None,
capabilities,
}
}
#[must_use]
pub fn new_journaled(
data: &'static LocalKey<RefCell<DataStore>>,
index: &'static LocalKey<RefCell<IndexStore>>,
schema: &'static LocalKey<RefCell<SchemaStore>>,
journal: &'static LocalKey<RefCell<JournalTailStore>>,
allocations: StoreAllocationIdentities,
capabilities: StoreRuntimeStorageCapabilities,
) -> Self {
let cardinality_allocation = CardinalityStoreAllocationIdentity::derive(allocations).ok();
Self {
data,
index,
schema,
journal: Some(journal),
allocations,
cardinality_allocation,
capabilities,
}
}
pub fn with_data<R>(&self, f: impl FnOnce(&DataStore) -> R) -> R {
#[cfg(feature = "diagnostics")]
{
crate::db::physical_access::measure_physical_access_operation(|| {
self.data.with_borrow(f)
})
}
#[cfg(not(feature = "diagnostics"))]
{
self.data.with_borrow(f)
}
}
pub fn with_data_mut<R>(&self, f: impl FnOnce(&mut DataStore) -> R) -> R {
self.data.with_borrow_mut(f)
}
pub fn with_index<R>(&self, f: impl FnOnce(&IndexStore) -> R) -> R {
#[cfg(feature = "diagnostics")]
{
crate::db::physical_access::measure_physical_access_operation(|| {
self.index.with_borrow(f)
})
}
#[cfg(not(feature = "diagnostics"))]
{
self.index.with_borrow(f)
}
}
pub fn with_index_mut<R>(&self, f: impl FnOnce(&mut IndexStore) -> R) -> R {
self.index.with_borrow_mut(f)
}
pub fn with_schema<R>(&self, f: impl FnOnce(&SchemaStore) -> R) -> R {
self.schema.with_borrow(f)
}
pub fn with_schema_mut<R>(&self, f: impl FnOnce(&mut SchemaStore) -> R) -> R {
self.schema.with_borrow_mut(f)
}
#[must_use]
pub(in crate::db) fn exact_entity_count(&self, entity: EntityTag) -> Option<u64> {
if self.journal.is_none() {
return self.with_data(|store| store.exact_entity_count(entity));
}
let delta = self.with_data(|store| store.exact_entity_cardinality_delta(entity))?;
let digest = CardinalityCountDigest::for_entity(entity);
let base = self
.ready_cardinality_counts(&[digest], |authority| authority.accepts_entity(entity))
.ok()
.flatten()?
.into_iter()
.next()?;
apply_visible_cardinality_delta(base, delta)
}
#[must_use]
pub(in crate::db) fn exact_user_index_prefix_count(
&self,
data_generation: u64,
key_kind: IndexKeyKind,
index_id: IndexId,
components: &[Vec<u8>],
) -> Option<u64> {
self.exact_user_index_prefix_counts(data_generation, key_kind, index_id, [components])?
.into_iter()
.next()
}
#[must_use]
pub(in crate::db) fn exact_user_index_prefix_counts<'a>(
&self,
data_generation: u64,
key_kind: IndexKeyKind,
index_id: IndexId,
component_prefixes: impl IntoIterator<Item = &'a [Vec<u8>]>,
) -> Option<Vec<u64>> {
let component_prefixes = component_prefixes.into_iter().collect::<Vec<_>>();
if key_kind != IndexKeyKind::User {
return None;
}
if self.journal.is_none() {
return self.with_index(|store| {
component_prefixes
.iter()
.map(|components| {
store.exact_prefix_cardinality(
data_generation,
key_kind,
index_id,
components,
)
})
.collect()
});
}
let deltas = self.with_index(|store| {
component_prefixes
.iter()
.map(|components| {
store.exact_prefix_cardinality_delta(key_kind, index_id, components)
})
.collect::<Option<Vec<_>>>()
})?;
let digests = component_prefixes
.iter()
.map(|components| {
CardinalityCountDigest::for_user_index_prefix(index_id, components).ok()
})
.collect::<Option<Vec<_>>>()?;
let bases = self
.ready_cardinality_counts(&digests, |authority| {
component_prefixes.iter().all(|components| {
authority.accepts_user_index_prefix(index_id, components.len())
})
})
.ok()
.flatten()?;
bases
.into_iter()
.zip(deltas)
.map(|(base, delta)| apply_visible_cardinality_delta(base, delta))
.collect()
}
#[must_use]
pub(in crate::db) fn exact_user_index_prefix_key_counts(
&self,
data_generation: u64,
keys: &[UserIndexPrefixCardinalityKey],
) -> Option<Vec<u64>> {
self.exact_user_index_prefix_key_counts_with_authority(data_generation, keys, None)
}
#[must_use]
pub(in crate::db) fn exact_user_index_prefix_key_counts_for_admitted_root(
&self,
database_incarnation: DatabaseIncarnationId,
accepted_root: CardinalityAcceptedRootIdentity,
data_generation: u64,
keys: &[UserIndexPrefixCardinalityKey],
) -> Option<Vec<u64>> {
self.exact_user_index_prefix_key_counts_with_authority(
data_generation,
keys,
Some((database_incarnation, accepted_root)),
)
}
fn exact_user_index_prefix_key_counts_with_authority(
&self,
data_generation: u64,
keys: &[UserIndexPrefixCardinalityKey],
admitted: Option<(DatabaseIncarnationId, CardinalityAcceptedRootIdentity)>,
) -> Option<Vec<u64>> {
if keys.is_empty() {
return None;
}
if self.journal.is_none() {
return self.with_index(|store| {
keys.iter()
.map(|key| {
store.exact_prefix_cardinality(
data_generation,
IndexKeyKind::User,
key.index_id(),
key.prefix_components(),
)
})
.collect()
});
}
let (delta_watermark, deltas) = self.with_index(|store| {
let watermark = store.exact_prefix_cardinality_delta_watermark()?;
keys.iter()
.map(|key| {
store.exact_prefix_cardinality_delta(
IndexKeyKind::User,
key.index_id(),
key.prefix_components(),
)
})
.collect::<Option<Vec<_>>>()
.map(|deltas| (watermark, deltas))
})?;
let accepts = |authority: &CardinalityBuildAuthority| {
keys.iter().all(|key| {
authority.accepts_user_index_prefix(key.index_id(), key.prefix_components().len())
})
};
let bases = match admitted {
Some((database_incarnation, accepted_root)) => self
.ready_cardinality_counts_for_source(
ReadyCardinalitySource::Admitted {
database_incarnation,
accepted_root,
fold_watermark: delta_watermark,
},
ReadyCardinalityCountTargets::UserIndexPrefixes(keys),
accepts,
),
None => self.ready_cardinality_counts_for_targets(
ReadyCardinalityCountTargets::UserIndexPrefixes(keys),
accepts,
),
}
.ok()
.flatten()?;
bases
.into_iter()
.zip(deltas)
.map(|(base, delta)| apply_visible_cardinality_delta(base, delta))
.collect()
}
#[must_use]
pub(in crate::db) fn user_index_prefix_family_has_ready_generation<'a, I>(
&self,
database_incarnation: DatabaseIncarnationId,
accepted_root: CardinalityAcceptedRootIdentity,
data_generation: u64,
key_kind: IndexKeyKind,
index_id: IndexId,
component_prefixes: I,
) -> bool
where
I: Clone + IntoIterator<Item = &'a [Vec<u8>]>,
{
if key_kind != IndexKeyKind::User || component_prefixes.clone().into_iter().next().is_none()
{
return false;
}
if self.journal.is_none() {
return self.with_index(|store| {
component_prefixes.clone().into_iter().all(|components| {
store
.exact_prefix_cardinality(data_generation, key_kind, index_id, components)
.is_some()
})
});
}
let delta_watermark = self.with_index(|store| {
let watermark = store.exact_prefix_cardinality_delta_watermark()?;
component_prefixes
.clone()
.into_iter()
.all(|components| {
store
.exact_prefix_cardinality_delta(key_kind, index_id, components)
.is_some()
})
.then_some(watermark)
});
delta_watermark.is_some_and(|watermark| {
self.ready_cardinality_counts_for_source(
ReadyCardinalitySource::Admitted {
database_incarnation,
accepted_root,
fold_watermark: watermark,
},
ReadyCardinalityCountTargets::Digests(&[]),
|authority| {
component_prefixes.into_iter().all(|components| {
authority.accepts_user_index_prefix(index_id, components.len())
})
},
)
.is_ok_and(|counts| counts.is_some())
})
}
#[must_use]
pub(in crate::db) fn exact_user_index_prefix_count_sum<'a>(
&self,
data_generation: u64,
key_kind: IndexKeyKind,
index_id: IndexId,
component_prefixes: impl IntoIterator<Item = &'a [Vec<u8>]>,
stop_after: Option<u64>,
) -> Option<u64> {
let component_prefixes = component_prefixes.into_iter().collect::<Vec<_>>();
if self.journal.is_none() {
return self.with_index(|store| {
store.exact_prefix_cardinality_sum(
data_generation,
key_kind,
index_id,
component_prefixes.iter().copied(),
stop_after,
)
});
}
let counts = self.exact_user_index_prefix_counts(
data_generation,
key_kind,
index_id,
component_prefixes.iter().copied(),
)?;
let mut total = 0_u64;
for count in counts {
total = total.checked_add(count)?;
if stop_after.is_some_and(|required| total >= required) {
break;
}
}
Some(total)
}
#[must_use]
pub(in crate::db) fn exact_user_index_child_prefixes_for_parent_set<'a>(
&self,
data_generation: u64,
index_id: IndexId,
parent_prefixes: impl IntoIterator<Item = &'a [Vec<u8>]>,
total_cap: usize,
) -> Option<Vec<Vec<Vec<u8>>>> {
let mut parent_prefixes = parent_prefixes
.into_iter()
.map(<[Vec<u8>]>::to_vec)
.collect::<Vec<_>>();
if parent_prefixes.iter().any(Vec::is_empty) {
return None;
}
parent_prefixes.sort_unstable();
parent_prefixes.dedup();
let child_prefixes = self.with_index(|store| {
store.exact_child_prefixes_for_parent_set(
data_generation,
IndexKeyKind::User,
index_id,
parent_prefixes.iter().map(Vec::as_slice),
total_cap,
)
})?;
if self.journal.is_none() {
return Some(child_prefixes);
}
let parent_count = parent_prefixes.len();
let counts = self.exact_user_index_prefix_counts(
data_generation,
IndexKeyKind::User,
index_id,
parent_prefixes
.iter()
.chain(&child_prefixes)
.map(Vec::as_slice),
)?;
let (parent_counts, child_counts) = counts.split_at(parent_count);
let parent_total = checked_cardinality_sum(parent_counts)?;
let child_total = checked_cardinality_sum(child_counts)?;
(parent_total == child_total).then_some(child_prefixes)
}
fn ready_cardinality_counts(
&self,
digests: &[CardinalityCountDigest],
accepts: impl FnOnce(&CardinalityBuildAuthority) -> bool,
) -> Result<Option<Vec<u64>>, InternalError> {
self.ready_cardinality_counts_for_targets(
ReadyCardinalityCountTargets::Digests(digests),
accepts,
)
}
fn ready_cardinality_counts_for_targets(
&self,
targets: ReadyCardinalityCountTargets<'_>,
accepts: impl FnOnce(&CardinalityBuildAuthority) -> bool,
) -> Result<Option<Vec<u64>>, InternalError> {
let incarnation = database_incarnation_id()?;
self.ready_cardinality_counts_for_source(
ReadyCardinalitySource::Current {
database_incarnation: incarnation,
},
targets,
accepts,
)
}
fn ready_cardinality_counts_for_source(
&self,
source: ReadyCardinalitySource,
targets: ReadyCardinalityCountTargets<'_>,
accepts: impl FnOnce(&CardinalityBuildAuthority) -> bool,
) -> Result<Option<Vec<u64>>, InternalError> {
let Some(journal) = self.journal else {
return Ok(None);
};
let Some(allocation) = self.cardinality_allocation else {
return Ok(None);
};
let (incarnation, accepted_root, watermark) = match source {
ReadyCardinalitySource::Current {
database_incarnation,
} => (
database_incarnation,
None,
journal.with_borrow(JournalTailStore::fold_watermark)?,
),
ReadyCardinalitySource::Admitted {
database_incarnation,
accepted_root,
fold_watermark,
} => (database_incarnation, Some(accepted_root), fold_watermark),
};
self.with_schema(|schema| {
let (header, cursor) = schema.cardinality_generation_control()?;
let Some(header) = header else {
return Ok(None);
};
if header.state() != CardinalityGenerationState::Ready || cursor.is_some() {
return Ok(None);
}
let authority = match accepted_root {
Some(root) => CardinalityBuildAuthority::derive_for_admitted_consumer_root(
schema,
incarnation,
allocation,
root,
watermark,
)?,
None => CardinalityBuildAuthority::derive_for_current_consumer(
schema,
incarnation,
allocation,
watermark,
)?,
};
let Some(authority) = authority else {
return Ok(None);
};
if !accepts(&authority) {
return Ok(None);
}
if header.validate_source(authority.source()).is_err() {
return Ok(None);
}
if targets.len() != 0 && schema.cardinality_count_slot_is_empty(header.slot())? {
return Ok(Some(vec![0; targets.len()]));
}
let counts = match targets {
ReadyCardinalityCountTargets::Digests(digests) => digests
.iter()
.map(|digest| {
schema
.cardinality_count(header.slot(), header.generation(), *digest)
.map(|count| count.unwrap_or(0))
})
.collect::<Result<Vec<_>, _>>()?,
ReadyCardinalityCountTargets::UserIndexPrefixes(keys) => keys
.iter()
.map(|key| {
let digest = CardinalityCountDigest::for_user_index_prefix(
key.index_id(),
key.prefix_components(),
)?;
schema
.cardinality_count(header.slot(), header.generation(), digest)
.map(|count| count.unwrap_or(0))
})
.collect::<Result<Vec<_>, _>>()?,
};
Ok(Some(counts))
})
}
#[must_use]
pub(in crate::db) fn index_state(&self) -> IndexState {
self.with_index(IndexStore::state)
}
pub(in crate::db) fn access_state_revision(&self) -> Result<u64, crate::error::InternalError> {
self.journal.map_or_else(
|| Ok(self.with_index(IndexStore::access_state_revision)),
|journal| journal.with_borrow(JournalTailStore::access_state_revision),
)
}
pub(in crate::db) fn mark_index_building(&self) -> Result<(), crate::error::InternalError> {
self.set_index_state(IndexState::Building)
}
pub(in crate::db) fn mark_index_ready(&self) -> Result<(), crate::error::InternalError> {
self.set_index_state(IndexState::Ready)
}
fn set_index_state(&self, state: IndexState) -> Result<(), crate::error::InternalError> {
if self.index_state() == state {
return Ok(());
}
let revision = self.journal.map_or_else(
|| {
self.with_index(IndexStore::access_state_revision)
.checked_add(1)
.ok_or_else(crate::error::InternalError::store_invariant)
},
|journal| journal.with_borrow_mut(JournalTailStore::advance_access_state_revision),
)?;
self.with_index_mut(|index| index.set_access_state(state, revision));
Ok(())
}
#[must_use]
pub const fn data_store(&self) -> &'static LocalKey<RefCell<DataStore>> {
self.data
}
#[must_use]
pub const fn index_store(&self) -> &'static LocalKey<RefCell<IndexStore>> {
self.index
}
#[must_use]
pub const fn schema_store(&self) -> &'static LocalKey<RefCell<SchemaStore>> {
self.schema
}
#[must_use]
pub const fn journal_tail_store(&self) -> Option<&'static LocalKey<RefCell<JournalTailStore>>> {
self.journal
}
#[must_use]
pub const fn data_allocation(&self) -> Option<StoreAllocationIdentity> {
self.allocations.data()
}
#[must_use]
pub const fn index_allocation(&self) -> Option<StoreAllocationIdentity> {
self.allocations.index()
}
#[must_use]
pub const fn schema_allocation(&self) -> Option<StoreAllocationIdentity> {
self.allocations.schema()
}
#[must_use]
pub const fn journal_allocation(&self) -> Option<StoreAllocationIdentity> {
self.allocations.journal()
}
#[must_use]
pub(in crate::db) const fn allocation_identities(&self) -> StoreAllocationIdentities {
self.allocations
}
#[must_use]
pub const fn storage_capabilities(&self) -> StoreRuntimeStorageCapabilities {
self.capabilities
}
}
fn apply_visible_cardinality_delta(base: u64, delta: i64) -> Option<u64> {
if delta >= 0 {
base.checked_add(u64::try_from(delta).ok()?)
} else {
base.checked_sub(delta.unsigned_abs())
}
}
fn checked_cardinality_sum(counts: &[u64]) -> Option<u64> {
counts
.iter()
.try_fold(0_u64, |total, count| total.checked_add(*count))
}