1use crate::db::{
2 data::DataStore,
3 index::{IndexState, IndexStore},
4 journal::JournalTailStore,
5 schema::SchemaStore,
6};
7use candid::CandidType;
8use serde::Deserialize;
9use std::{cell::RefCell, thread::LocalKey};
10
11#[derive(Clone, Copy, Debug)]
21pub struct StoreHandle {
22 data: &'static LocalKey<RefCell<DataStore>>,
23 index: &'static LocalKey<RefCell<IndexStore>>,
24 schema: &'static LocalKey<RefCell<SchemaStore>>,
25 journal: Option<&'static LocalKey<RefCell<JournalTailStore>>>,
26 allocations: StoreAllocationIdentities,
27 capabilities: StoreRuntimeStorageCapabilities,
28}
29
30#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
34pub enum StoreRuntimeStorageMode {
35 #[default]
37 Stable,
38 Heap,
40 Journaled,
42}
43
44impl StoreRuntimeStorageMode {
45 #[must_use]
47 pub const fn as_str(self) -> &'static str {
48 match self {
49 Self::Stable => "stable",
50 Self::Heap => "heap",
51 Self::Journaled => "journaled",
52 }
53 }
54}
55
56#[derive(CandidType, Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
58pub enum StoreAllocationIdentityCapability {
59 #[default]
61 Present,
62 Absent,
64}
65
66impl StoreAllocationIdentityCapability {
67 #[must_use]
69 pub const fn as_str(self) -> &'static str {
70 match self {
71 Self::Present => "present",
72 Self::Absent => "absent",
73 }
74 }
75}
76
77#[derive(CandidType, Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
79pub enum StoreDurability {
80 #[default]
82 Durable,
83 Volatile,
85}
86
87impl StoreDurability {
88 #[must_use]
90 pub const fn as_str(self) -> &'static str {
91 match self {
92 Self::Durable => "durable",
93 Self::Volatile => "volatile",
94 }
95 }
96}
97
98#[derive(CandidType, Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
100pub enum StoreRecoveryCapability {
101 #[default]
103 StableCommitReplay,
104 StableBasePlusJournalReplay,
107 None,
109}
110
111impl StoreRecoveryCapability {
112 #[must_use]
114 pub const fn as_str(self) -> &'static str {
115 match self {
116 Self::StableCommitReplay => "stable-replay",
117 Self::StableBasePlusJournalReplay => "stable-base-plus-journal-replay",
118 Self::None => "none",
119 }
120 }
121}
122
123#[derive(CandidType, Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
125pub enum StoreCommitParticipation {
126 #[default]
128 Durable,
129 LiveOnly,
131}
132
133impl StoreCommitParticipation {
134 #[must_use]
136 pub const fn as_str(self) -> &'static str {
137 match self {
138 Self::Durable => "durable",
139 Self::LiveOnly => "live-only",
140 }
141 }
142}
143
144#[derive(CandidType, Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
146pub enum StoreSchemaMetadataCapability {
147 #[default]
149 DurableAcceptedHistory,
150 LiveRebuiltMetadata,
152 CanonicalStableHistoryPlusJournalTail,
154}
155
156impl StoreSchemaMetadataCapability {
157 #[must_use]
159 pub const fn as_str(self) -> &'static str {
160 match self {
161 Self::DurableAcceptedHistory => "durable-accepted-history",
162 Self::LiveRebuiltMetadata => "live-rebuilt-metadata",
163 Self::CanonicalStableHistoryPlusJournalTail => {
164 "canonical-stable-history-plus-journal-tail"
165 }
166 }
167 }
168}
169
170#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
172pub enum StoreRelationSourceCapability {
173 #[default]
175 DurableSource,
176 LiveSource,
178}
179
180#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
182pub enum StoreRelationTargetCapability {
183 #[default]
185 DurableTarget,
186 VolatileTarget,
188}
189
190#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
192pub enum StoreLiveValidationCapability {
193 #[default]
195 Supported,
196}
197
198#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
202pub struct StoreRuntimeStorageCapabilities {
203 storage_mode: StoreRuntimeStorageMode,
204 allocation_identity: StoreAllocationIdentityCapability,
205 durability: StoreDurability,
206 recovery: StoreRecoveryCapability,
207 commit_participation: StoreCommitParticipation,
208 schema_metadata: StoreSchemaMetadataCapability,
209 relation_source: StoreRelationSourceCapability,
210 relation_target: StoreRelationTargetCapability,
211 live_validation: StoreLiveValidationCapability,
212}
213
214impl StoreRuntimeStorageCapabilities {
215 #[must_use]
217 pub const fn stable() -> Self {
218 Self {
219 storage_mode: StoreRuntimeStorageMode::Stable,
220 allocation_identity: StoreAllocationIdentityCapability::Present,
221 durability: StoreDurability::Durable,
222 recovery: StoreRecoveryCapability::StableCommitReplay,
223 commit_participation: StoreCommitParticipation::Durable,
224 schema_metadata: StoreSchemaMetadataCapability::DurableAcceptedHistory,
225 relation_source: StoreRelationSourceCapability::DurableSource,
226 relation_target: StoreRelationTargetCapability::DurableTarget,
227 live_validation: StoreLiveValidationCapability::Supported,
228 }
229 }
230
231 #[must_use]
233 pub const fn heap() -> Self {
234 Self {
235 storage_mode: StoreRuntimeStorageMode::Heap,
236 allocation_identity: StoreAllocationIdentityCapability::Absent,
237 durability: StoreDurability::Volatile,
238 recovery: StoreRecoveryCapability::None,
239 commit_participation: StoreCommitParticipation::LiveOnly,
240 schema_metadata: StoreSchemaMetadataCapability::LiveRebuiltMetadata,
241 relation_source: StoreRelationSourceCapability::LiveSource,
242 relation_target: StoreRelationTargetCapability::VolatileTarget,
243 live_validation: StoreLiveValidationCapability::Supported,
244 }
245 }
246
247 #[must_use]
249 pub const fn journaled() -> Self {
250 Self {
251 storage_mode: StoreRuntimeStorageMode::Journaled,
252 allocation_identity: StoreAllocationIdentityCapability::Present,
253 durability: StoreDurability::Durable,
254 recovery: StoreRecoveryCapability::StableBasePlusJournalReplay,
255 commit_participation: StoreCommitParticipation::Durable,
256 schema_metadata: StoreSchemaMetadataCapability::CanonicalStableHistoryPlusJournalTail,
257 relation_source: StoreRelationSourceCapability::DurableSource,
258 relation_target: StoreRelationTargetCapability::DurableTarget,
259 live_validation: StoreLiveValidationCapability::Supported,
260 }
261 }
262
263 #[must_use]
265 pub const fn storage_mode(self) -> StoreRuntimeStorageMode {
266 self.storage_mode
267 }
268
269 #[must_use]
271 pub const fn allocation_identity(self) -> StoreAllocationIdentityCapability {
272 self.allocation_identity
273 }
274
275 #[must_use]
277 pub const fn durability(self) -> StoreDurability {
278 self.durability
279 }
280
281 #[must_use]
283 pub const fn recovery(self) -> StoreRecoveryCapability {
284 self.recovery
285 }
286
287 #[must_use]
289 pub const fn commit_participation(self) -> StoreCommitParticipation {
290 self.commit_participation
291 }
292
293 #[must_use]
295 pub const fn schema_metadata(self) -> StoreSchemaMetadataCapability {
296 self.schema_metadata
297 }
298
299 #[must_use]
301 pub const fn relation_source(self) -> StoreRelationSourceCapability {
302 self.relation_source
303 }
304
305 #[must_use]
307 pub const fn relation_target(self) -> StoreRelationTargetCapability {
308 self.relation_target
309 }
310
311 #[must_use]
313 pub const fn live_validation(self) -> StoreLiveValidationCapability {
314 self.live_validation
315 }
316}
317
318#[derive(Clone, Copy, Debug, Eq, PartialEq)]
325pub struct StoreAllocationIdentity {
326 memory_id: u8,
327 stable_key: &'static str,
328}
329
330impl StoreAllocationIdentity {
331 #[must_use]
333 pub const fn new(memory_id: u8, stable_key: &'static str) -> Self {
334 Self {
335 memory_id,
336 stable_key,
337 }
338 }
339
340 #[must_use]
342 pub const fn memory_id(self) -> u8 {
343 self.memory_id
344 }
345
346 #[must_use]
348 pub const fn stable_key(self) -> &'static str {
349 self.stable_key
350 }
351}
352
353#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
361pub struct StoreAllocationIdentities {
362 data: Option<StoreAllocationIdentity>,
363 index: Option<StoreAllocationIdentity>,
364 schema: Option<StoreAllocationIdentity>,
365 journal: Option<StoreAllocationIdentity>,
366}
367
368impl StoreAllocationIdentities {
369 #[must_use]
371 pub const fn absent() -> Self {
372 Self {
373 data: None,
374 index: None,
375 schema: None,
376 journal: None,
377 }
378 }
379
380 #[must_use]
382 pub const fn new(
383 data: StoreAllocationIdentity,
384 index: StoreAllocationIdentity,
385 schema: StoreAllocationIdentity,
386 ) -> Self {
387 Self {
388 data: Some(data),
389 index: Some(index),
390 schema: Some(schema),
391 journal: None,
392 }
393 }
394
395 #[must_use]
397 pub const fn new_journaled(
398 data: StoreAllocationIdentity,
399 index: StoreAllocationIdentity,
400 schema: StoreAllocationIdentity,
401 journal: StoreAllocationIdentity,
402 ) -> Self {
403 Self {
404 data: Some(data),
405 index: Some(index),
406 schema: Some(schema),
407 journal: Some(journal),
408 }
409 }
410
411 #[must_use]
413 pub const fn data(self) -> Option<StoreAllocationIdentity> {
414 self.data
415 }
416
417 #[must_use]
419 pub const fn index(self) -> Option<StoreAllocationIdentity> {
420 self.index
421 }
422
423 #[must_use]
425 pub const fn schema(self) -> Option<StoreAllocationIdentity> {
426 self.schema
427 }
428
429 #[must_use]
431 pub const fn journal(self) -> Option<StoreAllocationIdentity> {
432 self.journal
433 }
434
435 #[must_use]
438 pub const fn allocation_identity_capability(self) -> Option<StoreAllocationIdentityCapability> {
439 match (self.data, self.index, self.schema) {
440 (Some(_), Some(_), Some(_)) => Some(StoreAllocationIdentityCapability::Present),
441 (None, None, None) if self.journal.is_none() => {
442 Some(StoreAllocationIdentityCapability::Absent)
443 }
444 _ => None,
445 }
446 }
447
448 #[must_use]
451 pub const fn matches_storage_capabilities(
452 self,
453 capabilities: StoreRuntimeStorageCapabilities,
454 ) -> bool {
455 match capabilities.storage_mode() {
456 StoreRuntimeStorageMode::Stable => {
457 self.data.is_some()
458 && self.index.is_some()
459 && self.schema.is_some()
460 && self.journal.is_none()
461 }
462 StoreRuntimeStorageMode::Heap => {
463 self.data.is_none()
464 && self.index.is_none()
465 && self.schema.is_none()
466 && self.journal.is_none()
467 }
468 StoreRuntimeStorageMode::Journaled => {
469 self.data.is_some()
470 && self.index.is_some()
471 && self.schema.is_some()
472 && self.journal.is_some()
473 }
474 }
475 }
476}
477
478impl StoreHandle {
479 #[must_use]
481 pub const fn new(
482 data: &'static LocalKey<RefCell<DataStore>>,
483 index: &'static LocalKey<RefCell<IndexStore>>,
484 schema: &'static LocalKey<RefCell<SchemaStore>>,
485 allocations: StoreAllocationIdentities,
486 capabilities: StoreRuntimeStorageCapabilities,
487 ) -> Self {
488 Self {
489 data,
490 index,
491 schema,
492 journal: None,
493 allocations,
494 capabilities,
495 }
496 }
497
498 #[must_use]
500 pub const fn new_journaled(
501 data: &'static LocalKey<RefCell<DataStore>>,
502 index: &'static LocalKey<RefCell<IndexStore>>,
503 schema: &'static LocalKey<RefCell<SchemaStore>>,
504 journal: &'static LocalKey<RefCell<JournalTailStore>>,
505 allocations: StoreAllocationIdentities,
506 capabilities: StoreRuntimeStorageCapabilities,
507 ) -> Self {
508 Self {
509 data,
510 index,
511 schema,
512 journal: Some(journal),
513 allocations,
514 capabilities,
515 }
516 }
517
518 pub fn with_data<R>(&self, f: impl FnOnce(&DataStore) -> R) -> R {
520 #[cfg(feature = "diagnostics")]
521 {
522 crate::db::physical_access::measure_physical_access_operation(|| {
523 self.data.with_borrow(f)
524 })
525 }
526
527 #[cfg(not(feature = "diagnostics"))]
528 {
529 self.data.with_borrow(f)
530 }
531 }
532
533 pub fn with_data_mut<R>(&self, f: impl FnOnce(&mut DataStore) -> R) -> R {
535 self.data.with_borrow_mut(f)
536 }
537
538 pub fn with_index<R>(&self, f: impl FnOnce(&IndexStore) -> R) -> R {
540 #[cfg(feature = "diagnostics")]
541 {
542 crate::db::physical_access::measure_physical_access_operation(|| {
543 self.index.with_borrow(f)
544 })
545 }
546
547 #[cfg(not(feature = "diagnostics"))]
548 {
549 self.index.with_borrow(f)
550 }
551 }
552
553 pub fn with_index_mut<R>(&self, f: impl FnOnce(&mut IndexStore) -> R) -> R {
555 self.index.with_borrow_mut(f)
556 }
557
558 pub fn with_schema<R>(&self, f: impl FnOnce(&SchemaStore) -> R) -> R {
560 self.schema.with_borrow(f)
561 }
562
563 pub fn with_schema_mut<R>(&self, f: impl FnOnce(&mut SchemaStore) -> R) -> R {
565 self.schema.with_borrow_mut(f)
566 }
567
568 #[must_use]
570 pub(in crate::db) fn index_state(&self) -> IndexState {
571 self.with_index(IndexStore::state)
572 }
573
574 pub(in crate::db) fn mark_index_building(&self) {
576 self.with_index_mut(IndexStore::mark_building);
577 }
578
579 pub(in crate::db) fn mark_index_ready(&self) {
581 self.with_index_mut(IndexStore::mark_ready);
582 }
583
584 #[must_use]
586 pub const fn data_store(&self) -> &'static LocalKey<RefCell<DataStore>> {
587 self.data
588 }
589
590 #[must_use]
592 pub const fn index_store(&self) -> &'static LocalKey<RefCell<IndexStore>> {
593 self.index
594 }
595
596 #[must_use]
598 pub const fn schema_store(&self) -> &'static LocalKey<RefCell<SchemaStore>> {
599 self.schema
600 }
601
602 #[must_use]
604 pub const fn journal_tail_store(&self) -> Option<&'static LocalKey<RefCell<JournalTailStore>>> {
605 self.journal
606 }
607
608 #[must_use]
611 pub const fn data_allocation(&self) -> Option<StoreAllocationIdentity> {
612 self.allocations.data()
613 }
614
615 #[must_use]
618 pub const fn index_allocation(&self) -> Option<StoreAllocationIdentity> {
619 self.allocations.index()
620 }
621
622 #[must_use]
625 pub const fn schema_allocation(&self) -> Option<StoreAllocationIdentity> {
626 self.allocations.schema()
627 }
628
629 #[must_use]
632 pub const fn journal_allocation(&self) -> Option<StoreAllocationIdentity> {
633 self.allocations.journal()
634 }
635
636 #[must_use]
638 pub const fn storage_capabilities(&self) -> StoreRuntimeStorageCapabilities {
639 self.capabilities
640 }
641}