1use crate::db::{
7 data::DataStore,
8 index::{IndexState, IndexStore},
9 journal::JournalTailStore,
10 schema::SchemaStore,
11};
12use candid::CandidType;
13use serde::Deserialize;
14use std::{cell::RefCell, thread::LocalKey};
15
16#[derive(Clone, Copy, Debug)]
26pub struct StoreHandle {
27 data: &'static LocalKey<RefCell<DataStore>>,
28 index: &'static LocalKey<RefCell<IndexStore>>,
29 schema: &'static LocalKey<RefCell<SchemaStore>>,
30 journal: Option<&'static LocalKey<RefCell<JournalTailStore>>>,
31 allocations: StoreAllocationIdentities,
32 capabilities: StoreRuntimeStorageCapabilities,
33}
34
35#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
39pub enum StoreRuntimeStorageMode {
40 #[default]
42 Heap,
43 Journaled,
45}
46
47impl StoreRuntimeStorageMode {
48 #[must_use]
50 pub const fn as_str(self) -> &'static str {
51 match self {
52 Self::Heap => "heap",
53 Self::Journaled => "journaled",
54 }
55 }
56}
57
58#[derive(CandidType, Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
60pub enum StoreAllocationIdentityCapability {
61 #[default]
63 Present,
64 Absent,
66}
67
68impl StoreAllocationIdentityCapability {
69 #[must_use]
71 pub const fn as_str(self) -> &'static str {
72 match self {
73 Self::Present => "present",
74 Self::Absent => "absent",
75 }
76 }
77}
78
79#[derive(CandidType, Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
81pub enum StoreDurability {
82 #[default]
84 Durable,
85 Volatile,
87}
88
89impl StoreDurability {
90 #[must_use]
92 pub const fn as_str(self) -> &'static str {
93 match self {
94 Self::Durable => "durable",
95 Self::Volatile => "volatile",
96 }
97 }
98}
99
100#[derive(CandidType, Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
102pub enum StoreRecoveryCapability {
103 #[default]
106 StableBasePlusJournalReplay,
107 None,
109}
110
111impl StoreRecoveryCapability {
112 #[must_use]
114 pub const fn as_str(self) -> &'static str {
115 match self {
116 Self::StableBasePlusJournalReplay => "stable-base-plus-journal-replay",
117 Self::None => "none",
118 }
119 }
120}
121
122#[derive(CandidType, Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
124pub enum StoreCommitParticipation {
125 #[default]
127 Durable,
128 LiveOnly,
130}
131
132impl StoreCommitParticipation {
133 #[must_use]
135 pub const fn as_str(self) -> &'static str {
136 match self {
137 Self::Durable => "durable",
138 Self::LiveOnly => "live-only",
139 }
140 }
141}
142
143#[derive(CandidType, Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
145pub enum StoreSchemaMetadataCapability {
146 LiveRebuiltMetadata,
149 #[default]
151 CanonicalStableHistoryPlusJournalTail,
152}
153
154impl StoreSchemaMetadataCapability {
155 #[must_use]
157 pub const fn as_str(self) -> &'static str {
158 match self {
159 Self::LiveRebuiltMetadata => "live-rebuilt-metadata",
160 Self::CanonicalStableHistoryPlusJournalTail => {
161 "canonical-stable-history-plus-journal-tail"
162 }
163 }
164 }
165}
166
167#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
169pub enum StoreRelationSourceCapability {
170 #[default]
172 DurableSource,
173 LiveSource,
175}
176
177#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
179pub enum StoreRelationTargetCapability {
180 #[default]
182 DurableTarget,
183 VolatileTarget,
185}
186
187#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
191pub struct StoreRuntimeStorageCapabilities {
192 storage_mode: StoreRuntimeStorageMode,
193 allocation_identity: StoreAllocationIdentityCapability,
194 durability: StoreDurability,
195 recovery: StoreRecoveryCapability,
196 commit_participation: StoreCommitParticipation,
197 schema_metadata: StoreSchemaMetadataCapability,
198 relation_source: StoreRelationSourceCapability,
199 relation_target: StoreRelationTargetCapability,
200}
201
202impl StoreRuntimeStorageCapabilities {
203 #[must_use]
205 pub const fn heap() -> Self {
206 Self {
207 storage_mode: StoreRuntimeStorageMode::Heap,
208 allocation_identity: StoreAllocationIdentityCapability::Absent,
209 durability: StoreDurability::Volatile,
210 recovery: StoreRecoveryCapability::None,
211 commit_participation: StoreCommitParticipation::LiveOnly,
212 schema_metadata: StoreSchemaMetadataCapability::LiveRebuiltMetadata,
213 relation_source: StoreRelationSourceCapability::LiveSource,
214 relation_target: StoreRelationTargetCapability::VolatileTarget,
215 }
216 }
217
218 #[must_use]
220 pub const fn journaled() -> Self {
221 Self {
222 storage_mode: StoreRuntimeStorageMode::Journaled,
223 allocation_identity: StoreAllocationIdentityCapability::Present,
224 durability: StoreDurability::Durable,
225 recovery: StoreRecoveryCapability::StableBasePlusJournalReplay,
226 commit_participation: StoreCommitParticipation::Durable,
227 schema_metadata: StoreSchemaMetadataCapability::CanonicalStableHistoryPlusJournalTail,
228 relation_source: StoreRelationSourceCapability::DurableSource,
229 relation_target: StoreRelationTargetCapability::DurableTarget,
230 }
231 }
232
233 #[must_use]
235 pub const fn storage_mode(self) -> StoreRuntimeStorageMode {
236 self.storage_mode
237 }
238
239 #[must_use]
241 pub const fn allocation_identity(self) -> StoreAllocationIdentityCapability {
242 self.allocation_identity
243 }
244
245 #[must_use]
247 pub const fn durability(self) -> StoreDurability {
248 self.durability
249 }
250
251 #[must_use]
253 pub const fn recovery(self) -> StoreRecoveryCapability {
254 self.recovery
255 }
256
257 #[must_use]
259 pub const fn commit_participation(self) -> StoreCommitParticipation {
260 self.commit_participation
261 }
262
263 #[must_use]
265 pub const fn schema_metadata(self) -> StoreSchemaMetadataCapability {
266 self.schema_metadata
267 }
268
269 #[must_use]
271 pub const fn relation_source(self) -> StoreRelationSourceCapability {
272 self.relation_source
273 }
274
275 #[must_use]
277 pub const fn relation_target(self) -> StoreRelationTargetCapability {
278 self.relation_target
279 }
280}
281
282#[derive(Clone, Copy, Debug, Eq, PartialEq)]
289pub struct StoreAllocationIdentity {
290 memory_id: u8,
291 stable_key: &'static str,
292}
293
294impl StoreAllocationIdentity {
295 #[must_use]
297 pub const fn new(memory_id: u8, stable_key: &'static str) -> Self {
298 Self {
299 memory_id,
300 stable_key,
301 }
302 }
303
304 #[must_use]
306 pub const fn memory_id(self) -> u8 {
307 self.memory_id
308 }
309
310 #[must_use]
312 pub const fn stable_key(self) -> &'static str {
313 self.stable_key
314 }
315}
316
317#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
325pub struct StoreAllocationIdentities {
326 data: Option<StoreAllocationIdentity>,
327 index: Option<StoreAllocationIdentity>,
328 schema: Option<StoreAllocationIdentity>,
329 journal: Option<StoreAllocationIdentity>,
330}
331
332impl StoreAllocationIdentities {
333 #[must_use]
335 pub const fn absent() -> Self {
336 Self {
337 data: None,
338 index: None,
339 schema: None,
340 journal: None,
341 }
342 }
343
344 #[must_use]
346 pub const fn new_journaled(
347 data: StoreAllocationIdentity,
348 index: StoreAllocationIdentity,
349 schema: StoreAllocationIdentity,
350 journal: StoreAllocationIdentity,
351 ) -> Self {
352 Self {
353 data: Some(data),
354 index: Some(index),
355 schema: Some(schema),
356 journal: Some(journal),
357 }
358 }
359
360 #[must_use]
362 pub const fn data(self) -> Option<StoreAllocationIdentity> {
363 self.data
364 }
365
366 #[must_use]
368 pub const fn index(self) -> Option<StoreAllocationIdentity> {
369 self.index
370 }
371
372 #[must_use]
374 pub const fn schema(self) -> Option<StoreAllocationIdentity> {
375 self.schema
376 }
377
378 #[must_use]
380 pub const fn journal(self) -> Option<StoreAllocationIdentity> {
381 self.journal
382 }
383
384 #[must_use]
387 pub const fn allocation_identity_capability(self) -> Option<StoreAllocationIdentityCapability> {
388 match (self.data, self.index, self.schema) {
389 (Some(_), Some(_), Some(_)) => Some(StoreAllocationIdentityCapability::Present),
390 (None, None, None) if self.journal.is_none() => {
391 Some(StoreAllocationIdentityCapability::Absent)
392 }
393 _ => None,
394 }
395 }
396
397 #[must_use]
400 pub const fn matches_storage_capabilities(
401 self,
402 capabilities: StoreRuntimeStorageCapabilities,
403 ) -> bool {
404 match capabilities.storage_mode() {
405 StoreRuntimeStorageMode::Heap => {
406 self.data.is_none()
407 && self.index.is_none()
408 && self.schema.is_none()
409 && self.journal.is_none()
410 }
411 StoreRuntimeStorageMode::Journaled => {
412 self.data.is_some()
413 && self.index.is_some()
414 && self.schema.is_some()
415 && self.journal.is_some()
416 }
417 }
418 }
419}
420
421impl StoreHandle {
422 #[must_use]
424 pub const fn new(
425 data: &'static LocalKey<RefCell<DataStore>>,
426 index: &'static LocalKey<RefCell<IndexStore>>,
427 schema: &'static LocalKey<RefCell<SchemaStore>>,
428 allocations: StoreAllocationIdentities,
429 capabilities: StoreRuntimeStorageCapabilities,
430 ) -> Self {
431 Self {
432 data,
433 index,
434 schema,
435 journal: None,
436 allocations,
437 capabilities,
438 }
439 }
440
441 #[must_use]
443 pub const fn new_journaled(
444 data: &'static LocalKey<RefCell<DataStore>>,
445 index: &'static LocalKey<RefCell<IndexStore>>,
446 schema: &'static LocalKey<RefCell<SchemaStore>>,
447 journal: &'static LocalKey<RefCell<JournalTailStore>>,
448 allocations: StoreAllocationIdentities,
449 capabilities: StoreRuntimeStorageCapabilities,
450 ) -> Self {
451 Self {
452 data,
453 index,
454 schema,
455 journal: Some(journal),
456 allocations,
457 capabilities,
458 }
459 }
460
461 pub fn with_data<R>(&self, f: impl FnOnce(&DataStore) -> R) -> R {
463 #[cfg(feature = "diagnostics")]
464 {
465 crate::db::physical_access::measure_physical_access_operation(|| {
466 self.data.with_borrow(f)
467 })
468 }
469
470 #[cfg(not(feature = "diagnostics"))]
471 {
472 self.data.with_borrow(f)
473 }
474 }
475
476 pub fn with_data_mut<R>(&self, f: impl FnOnce(&mut DataStore) -> R) -> R {
478 self.data.with_borrow_mut(f)
479 }
480
481 pub fn with_index<R>(&self, f: impl FnOnce(&IndexStore) -> R) -> R {
483 #[cfg(feature = "diagnostics")]
484 {
485 crate::db::physical_access::measure_physical_access_operation(|| {
486 self.index.with_borrow(f)
487 })
488 }
489
490 #[cfg(not(feature = "diagnostics"))]
491 {
492 self.index.with_borrow(f)
493 }
494 }
495
496 pub fn with_index_mut<R>(&self, f: impl FnOnce(&mut IndexStore) -> R) -> R {
498 self.index.with_borrow_mut(f)
499 }
500
501 pub fn with_schema<R>(&self, f: impl FnOnce(&SchemaStore) -> R) -> R {
503 self.schema.with_borrow(f)
504 }
505
506 pub fn with_schema_mut<R>(&self, f: impl FnOnce(&mut SchemaStore) -> R) -> R {
508 self.schema.with_borrow_mut(f)
509 }
510
511 #[must_use]
513 pub(in crate::db) fn index_state(&self) -> IndexState {
514 self.with_index(IndexStore::state)
515 }
516
517 pub(in crate::db) fn mark_index_building(&self) {
519 self.with_index_mut(IndexStore::mark_building);
520 }
521
522 pub(in crate::db) fn mark_index_ready(&self) {
524 self.with_index_mut(IndexStore::mark_ready);
525 }
526
527 #[must_use]
529 pub const fn data_store(&self) -> &'static LocalKey<RefCell<DataStore>> {
530 self.data
531 }
532
533 #[must_use]
535 pub const fn index_store(&self) -> &'static LocalKey<RefCell<IndexStore>> {
536 self.index
537 }
538
539 #[must_use]
541 pub const fn schema_store(&self) -> &'static LocalKey<RefCell<SchemaStore>> {
542 self.schema
543 }
544
545 #[must_use]
547 pub const fn journal_tail_store(&self) -> Option<&'static LocalKey<RefCell<JournalTailStore>>> {
548 self.journal
549 }
550
551 #[must_use]
554 pub const fn data_allocation(&self) -> Option<StoreAllocationIdentity> {
555 self.allocations.data()
556 }
557
558 #[must_use]
561 pub const fn index_allocation(&self) -> Option<StoreAllocationIdentity> {
562 self.allocations.index()
563 }
564
565 #[must_use]
568 pub const fn schema_allocation(&self) -> Option<StoreAllocationIdentity> {
569 self.allocations.schema()
570 }
571
572 #[must_use]
575 pub const fn journal_allocation(&self) -> Option<StoreAllocationIdentity> {
576 self.allocations.journal()
577 }
578
579 #[must_use]
581 pub(in crate::db) const fn allocation_identities(&self) -> StoreAllocationIdentities {
582 self.allocations
583 }
584
585 #[must_use]
587 pub const fn storage_capabilities(&self) -> StoreRuntimeStorageCapabilities {
588 self.capabilities
589 }
590}