use crate::db::{
data::DataStore,
index::{IndexState, IndexStore},
journal::JournalTailStore,
schema::SchemaStore,
};
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,
capabilities: StoreRuntimeStorageCapabilities,
}
#[derive(CandidType, Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
pub enum StoreRuntimeStorageMode {
#[default]
Stable,
Heap,
Journaled,
}
impl StoreRuntimeStorageMode {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Stable => "stable",
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]
StableCommitReplay,
StableBasePlusJournalReplay,
None,
}
impl StoreRecoveryCapability {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::StableCommitReplay => "stable-replay",
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 {
#[default]
DurableAcceptedHistory,
LiveRebuiltMetadata,
CanonicalStableHistoryPlusJournalTail,
}
impl StoreSchemaMetadataCapability {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::DurableAcceptedHistory => "durable-accepted-history",
Self::LiveRebuiltMetadata => "live-rebuilt-metadata",
Self::CanonicalStableHistoryPlusJournalTail => {
"canonical-stable-history-plus-journal-tail"
}
}
}
}
#[derive(CandidType, Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
pub enum StoreRelationSourceCapability {
#[default]
DurableSource,
LiveSource,
}
#[derive(CandidType, Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
pub enum StoreRelationTargetCapability {
#[default]
DurableTarget,
VolatileTarget,
}
#[derive(CandidType, Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
pub enum StoreLiveValidationCapability {
#[default]
Supported,
}
#[derive(CandidType, 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,
live_validation: StoreLiveValidationCapability,
}
impl StoreRuntimeStorageCapabilities {
#[must_use]
pub const fn stable() -> Self {
Self {
storage_mode: StoreRuntimeStorageMode::Stable,
allocation_identity: StoreAllocationIdentityCapability::Present,
durability: StoreDurability::Durable,
recovery: StoreRecoveryCapability::StableCommitReplay,
commit_participation: StoreCommitParticipation::Durable,
schema_metadata: StoreSchemaMetadataCapability::DurableAcceptedHistory,
relation_source: StoreRelationSourceCapability::DurableSource,
relation_target: StoreRelationTargetCapability::DurableTarget,
live_validation: StoreLiveValidationCapability::Supported,
}
}
#[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,
live_validation: StoreLiveValidationCapability::Supported,
}
}
#[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,
live_validation: StoreLiveValidationCapability::Supported,
}
}
#[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
}
#[must_use]
pub const fn live_validation(self) -> StoreLiveValidationCapability {
self.live_validation
}
}
#[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(
data: StoreAllocationIdentity,
index: StoreAllocationIdentity,
schema: StoreAllocationIdentity,
) -> Self {
Self {
data: Some(data),
index: Some(index),
schema: Some(schema),
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::Stable => {
self.data.is_some()
&& self.index.is_some()
&& self.schema.is_some()
&& self.journal.is_none()
}
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,
capabilities,
}
}
#[must_use]
pub const 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 {
Self {
data,
index,
schema,
journal: Some(journal),
allocations,
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 index_state(&self) -> IndexState {
self.with_index(IndexStore::state)
}
pub(in crate::db) fn mark_index_building(&self) {
self.with_index_mut(IndexStore::mark_building);
}
pub(in crate::db) fn mark_index_ready(&self) {
self.with_index_mut(IndexStore::mark_ready);
}
#[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 const fn storage_capabilities(&self) -> StoreRuntimeStorageCapabilities {
self.capabilities
}
}