Skip to main content

icydb_core/db/diagnostics/
model.rs

1//! Module: diagnostics::model
2//! Responsibility: storage diagnostics DTO contracts and simple accessors.
3//! Does not own: store traversal or query execution policy.
4//! Boundary: report assembly modules construct these DTOs; public callers read them.
5
6use crate::db::{
7    diagnostics::MemoryAllocations,
8    index::IndexState,
9    registry::{
10        StoreAllocationIdentityCapability, StoreCommitParticipation, StoreDurability,
11        StoreRecoveryCapability, StoreRuntimeStorageCapabilities, StoreSchemaMetadataCapability,
12    },
13};
14use candid::CandidType;
15use serde::Deserialize;
16
17#[cfg_attr(doc, doc = "StorageReport\n\nLive storage snapshot payload.")]
18#[derive(CandidType, Clone, Debug, Default, Deserialize)]
19pub struct StorageReport {
20    pub(crate) memory_allocations: Option<MemoryAllocations>,
21    pub(crate) storage_data: Vec<DataStoreSnapshot>,
22    pub(crate) storage_index: Vec<IndexStoreSnapshot>,
23    pub(crate) schema_storage: Vec<SchemaStoreSnapshot>,
24    pub(crate) entity_storage: Vec<EntitySnapshot>,
25    pub(crate) corrupted_keys: u64,
26    pub(crate) corrupted_entries: u64,
27}
28
29impl StorageReport {
30    /// Construct one storage report payload.
31    #[must_use]
32    pub(super) const fn new(
33        storage_data: Vec<DataStoreSnapshot>,
34        storage_index: Vec<IndexStoreSnapshot>,
35        schema_storage: Vec<SchemaStoreSnapshot>,
36        entity_storage: Vec<EntitySnapshot>,
37        corrupted_keys: u64,
38        corrupted_entries: u64,
39        memory_allocations: Option<MemoryAllocations>,
40    ) -> Self {
41        Self {
42            memory_allocations,
43            storage_data,
44            storage_index,
45            schema_storage,
46            entity_storage,
47            corrupted_keys,
48            corrupted_entries,
49        }
50    }
51
52    /// Borrow canister-wide physical allocation accounting.
53    ///
54    /// Absent when no default memory runtime exists. This includes other
55    /// libraries' allocations and the ledger, not just this database's stores.
56    #[must_use]
57    pub const fn memory_allocations(&self) -> Option<&MemoryAllocations> {
58        self.memory_allocations.as_ref()
59    }
60
61    /// Borrow data-store snapshots.
62    #[must_use]
63    pub const fn storage_data(&self) -> &[DataStoreSnapshot] {
64        self.storage_data.as_slice()
65    }
66
67    /// Borrow index-store snapshots.
68    #[must_use]
69    pub const fn storage_index(&self) -> &[IndexStoreSnapshot] {
70        self.storage_index.as_slice()
71    }
72
73    /// Borrow schema-store snapshots.
74    #[must_use]
75    pub const fn schema_storage(&self) -> &[SchemaStoreSnapshot] {
76        self.schema_storage.as_slice()
77    }
78
79    /// Borrow entity-level storage snapshots.
80    #[must_use]
81    pub const fn entity_storage(&self) -> &[EntitySnapshot] {
82        self.entity_storage.as_slice()
83    }
84
85    /// Return count of corrupted decoded data keys.
86    #[must_use]
87    pub const fn corrupted_keys(&self) -> u64 {
88        self.corrupted_keys
89    }
90
91    /// Return count of corrupted index entries.
92    #[must_use]
93    pub const fn corrupted_entries(&self) -> u64 {
94        self.corrupted_entries
95    }
96}
97
98#[cfg_attr(doc, doc = "SchemaStoreSnapshot\n\nSchema-store diagnostic row.")]
99#[derive(CandidType, Clone, Debug, Default, Deserialize)]
100pub struct SchemaStoreSnapshot {
101    pub(crate) path: String,
102    pub(crate) storage: StoreSnapshotStorageMode,
103    pub(crate) allocation: StoreAllocationIdentityCapability,
104    pub(crate) durability: StoreDurability,
105    pub(crate) commit: StoreCommitParticipation,
106    pub(crate) recovery: StoreRecoveryCapability,
107    pub(crate) schema_metadata: StoreSchemaMetadataCapability,
108    pub(crate) memory_id: Option<u8>,
109    pub(crate) stable_key: Option<String>,
110    pub(crate) schema_version: Option<u32>,
111    pub(crate) schema_fingerprint_method_version: Option<u8>,
112    pub(crate) schema_fingerprint: Option<String>,
113    pub(crate) entity_count: u64,
114}
115
116/// Diagnostic storage mode reported for one store-role snapshot.
117///
118/// This is observability metadata only. It does not participate in allocation
119/// identity, stable-key generation, or durable row/index/schema storage ABI.
120#[derive(CandidType, Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
121pub enum StoreSnapshotStorageMode {
122    Heap,
123    #[default]
124    Journaled,
125}
126
127impl StoreSnapshotStorageMode {
128    /// Return the user-facing storage mode label.
129    #[must_use]
130    pub const fn as_str(self) -> &'static str {
131        match self {
132            Self::Heap => "heap",
133            Self::Journaled => "journaled",
134        }
135    }
136}
137
138#[derive(Clone, Debug, Eq, PartialEq)]
139pub(super) struct StoreSnapshotAllocationIdentity {
140    memory_id: u8,
141    stable_key: String,
142}
143
144impl StoreSnapshotAllocationIdentity {
145    pub(super) const fn new(memory_id: u8, stable_key: String) -> Self {
146        Self {
147            memory_id,
148            stable_key,
149        }
150    }
151
152    const fn memory_id(&self) -> u8 {
153        self.memory_id
154    }
155}
156
157#[derive(Clone, Debug, Default, Eq, PartialEq)]
158pub(super) struct StoreSnapshotSchemaMetadata {
159    version: Option<u32>,
160    fingerprint_method_version: Option<u8>,
161    fingerprint: Option<String>,
162}
163
164impl StoreSnapshotSchemaMetadata {
165    pub(super) const fn absent() -> Self {
166        Self {
167            version: None,
168            fingerprint_method_version: None,
169            fingerprint: None,
170        }
171    }
172
173    pub(super) const fn new(
174        schema_version: u32,
175        schema_fingerprint_method_version: u8,
176        schema_fingerprint: String,
177    ) -> Self {
178        Self {
179            version: Some(schema_version),
180            fingerprint_method_version: Some(schema_fingerprint_method_version),
181            fingerprint: Some(schema_fingerprint),
182        }
183    }
184
185    const fn schema_version(&self) -> Option<u32> {
186        self.version
187    }
188
189    const fn schema_fingerprint_method_version(&self) -> Option<u8> {
190        self.fingerprint_method_version
191    }
192
193    fn schema_fingerprint(&self) -> Option<String> {
194        self.fingerprint.clone()
195    }
196}
197
198#[derive(Clone, Debug, Eq, PartialEq)]
199struct StoreRoleSnapshotFields {
200    path: String,
201    storage: StoreSnapshotStorageMode,
202    allocation: StoreAllocationIdentityCapability,
203    durability: StoreDurability,
204    commit: StoreCommitParticipation,
205    recovery: StoreRecoveryCapability,
206    schema_metadata: StoreSchemaMetadataCapability,
207    memory_id: Option<u8>,
208    stable_key: Option<String>,
209    schema_version: Option<u32>,
210    schema_fingerprint_method_version: Option<u8>,
211    schema_fingerprint: Option<String>,
212}
213
214impl StoreRoleSnapshotFields {
215    fn new(
216        path: String,
217        storage: StoreSnapshotStorageMode,
218        capabilities: StoreRuntimeStorageCapabilities,
219        allocation: Option<StoreSnapshotAllocationIdentity>,
220        schema_metadata: StoreSnapshotSchemaMetadata,
221    ) -> Self {
222        let (memory_id, stable_key) = match allocation {
223            Some(allocation) => (Some(allocation.memory_id()), Some(allocation.stable_key)),
224            None => (None, None),
225        };
226        Self {
227            path,
228            storage,
229            allocation: capabilities.allocation_identity(),
230            durability: capabilities.durability(),
231            commit: capabilities.commit_participation(),
232            recovery: capabilities.recovery(),
233            schema_metadata: capabilities.schema_metadata(),
234            memory_id,
235            stable_key,
236            schema_version: schema_metadata.schema_version(),
237            schema_fingerprint_method_version: schema_metadata.schema_fingerprint_method_version(),
238            schema_fingerprint: schema_metadata.schema_fingerprint(),
239        }
240    }
241}
242
243#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
244pub(super) struct IndexStoreSnapshotStats {
245    entries: u64,
246    user_entries: u64,
247    system_entries: u64,
248    memory_bytes: u64,
249    state: IndexState,
250}
251
252impl IndexStoreSnapshotStats {
253    pub(super) const fn new(
254        entries: u64,
255        user_entries: u64,
256        system_entries: u64,
257        memory_bytes: u64,
258        state: IndexState,
259    ) -> Self {
260        Self {
261            entries,
262            user_entries,
263            system_entries,
264            memory_bytes,
265            state,
266        }
267    }
268}
269
270impl SchemaStoreSnapshot {
271    /// Construct one schema-store diagnostic row.
272    #[must_use]
273    pub(super) fn new(
274        path: String,
275        storage: StoreSnapshotStorageMode,
276        capabilities: StoreRuntimeStorageCapabilities,
277        allocation: Option<StoreSnapshotAllocationIdentity>,
278        schema_metadata: StoreSnapshotSchemaMetadata,
279        entity_count: u64,
280    ) -> Self {
281        let fields =
282            StoreRoleSnapshotFields::new(path, storage, capabilities, allocation, schema_metadata);
283        Self {
284            path: fields.path,
285            storage: fields.storage,
286            allocation: fields.allocation,
287            durability: fields.durability,
288            commit: fields.commit,
289            recovery: fields.recovery,
290            schema_metadata: fields.schema_metadata,
291            memory_id: fields.memory_id,
292            stable_key: fields.stable_key,
293            schema_version: fields.schema_version,
294            schema_fingerprint_method_version: fields.schema_fingerprint_method_version,
295            schema_fingerprint: fields.schema_fingerprint,
296            entity_count,
297        }
298    }
299
300    /// Borrow store path.
301    #[must_use]
302    pub const fn path(&self) -> &str {
303        self.path.as_str()
304    }
305
306    /// Return diagnostic storage mode.
307    #[must_use]
308    pub const fn storage(&self) -> StoreSnapshotStorageMode {
309        self.storage
310    }
311
312    /// Return allocation-identity capability metadata.
313    #[must_use]
314    pub const fn allocation(&self) -> StoreAllocationIdentityCapability {
315        self.allocation
316    }
317
318    /// Return durability capability metadata.
319    #[must_use]
320    pub const fn durability(&self) -> StoreDurability {
321        self.durability
322    }
323
324    /// Return commit participation capability metadata.
325    #[must_use]
326    pub const fn commit(&self) -> StoreCommitParticipation {
327        self.commit
328    }
329
330    /// Return recovery capability metadata.
331    #[must_use]
332    pub const fn recovery(&self) -> StoreRecoveryCapability {
333        self.recovery
334    }
335
336    /// Return schema-metadata persistence capability metadata.
337    #[must_use]
338    pub const fn schema_metadata(&self) -> StoreSchemaMetadataCapability {
339        self.schema_metadata
340    }
341
342    /// Return stable-memory manager ID, when generated wiring supplied it.
343    #[must_use]
344    pub const fn memory_id(&self) -> Option<u8> {
345        self.memory_id
346    }
347
348    /// Return durable stable-memory key, when generated wiring supplied it.
349    #[must_use]
350    pub const fn stable_key(&self) -> Option<&str> {
351        match &self.stable_key {
352            Some(value) => Some(value.as_str()),
353            None => None,
354        }
355    }
356
357    /// Return accepted schema/catalog version, when known.
358    #[must_use]
359    pub const fn schema_version(&self) -> Option<u32> {
360        self.schema_version
361    }
362
363    /// Return accepted schema/catalog fingerprint method version, when known.
364    #[must_use]
365    pub const fn schema_fingerprint_method_version(&self) -> Option<u8> {
366        self.schema_fingerprint_method_version
367    }
368
369    /// Return accepted schema/catalog fingerprint, when known.
370    #[must_use]
371    pub const fn schema_fingerprint(&self) -> Option<&str> {
372        match &self.schema_fingerprint {
373            Some(value) => Some(value.as_str()),
374            None => None,
375        }
376    }
377
378    /// Return number of entity schemas represented in this schema catalog.
379    #[must_use]
380    pub const fn entity_count(&self) -> u64 {
381        self.entity_count
382    }
383}
384
385#[cfg_attr(doc, doc = "DataStoreSnapshot\n\nData-store snapshot row.")]
386#[derive(CandidType, Clone, Debug, Default, Deserialize)]
387pub struct DataStoreSnapshot {
388    pub(crate) path: String,
389    pub(crate) storage: StoreSnapshotStorageMode,
390    pub(crate) allocation: StoreAllocationIdentityCapability,
391    pub(crate) durability: StoreDurability,
392    pub(crate) commit: StoreCommitParticipation,
393    pub(crate) recovery: StoreRecoveryCapability,
394    pub(crate) schema_metadata: StoreSchemaMetadataCapability,
395    pub(crate) memory_id: Option<u8>,
396    pub(crate) stable_key: Option<String>,
397    pub(crate) schema_version: Option<u32>,
398    pub(crate) schema_fingerprint_method_version: Option<u8>,
399    pub(crate) schema_fingerprint: Option<String>,
400    pub(crate) entries: u64,
401    pub(crate) memory_bytes: u64,
402}
403
404impl DataStoreSnapshot {
405    /// Construct one data-store snapshot row.
406    #[must_use]
407    pub(super) fn new(
408        path: String,
409        storage: StoreSnapshotStorageMode,
410        capabilities: StoreRuntimeStorageCapabilities,
411        allocation: Option<StoreSnapshotAllocationIdentity>,
412        schema_metadata: StoreSnapshotSchemaMetadata,
413        entries: u64,
414        memory_bytes: u64,
415    ) -> Self {
416        let fields =
417            StoreRoleSnapshotFields::new(path, storage, capabilities, allocation, schema_metadata);
418        Self {
419            path: fields.path,
420            storage: fields.storage,
421            allocation: fields.allocation,
422            durability: fields.durability,
423            commit: fields.commit,
424            recovery: fields.recovery,
425            schema_metadata: fields.schema_metadata,
426            memory_id: fields.memory_id,
427            stable_key: fields.stable_key,
428            schema_version: fields.schema_version,
429            schema_fingerprint_method_version: fields.schema_fingerprint_method_version,
430            schema_fingerprint: fields.schema_fingerprint,
431            entries,
432            memory_bytes,
433        }
434    }
435
436    /// Borrow store path.
437    #[must_use]
438    pub const fn path(&self) -> &str {
439        self.path.as_str()
440    }
441
442    /// Return diagnostic storage mode.
443    #[must_use]
444    pub const fn storage(&self) -> StoreSnapshotStorageMode {
445        self.storage
446    }
447
448    /// Return allocation-identity capability metadata.
449    #[must_use]
450    pub const fn allocation(&self) -> StoreAllocationIdentityCapability {
451        self.allocation
452    }
453
454    /// Return durability capability metadata.
455    #[must_use]
456    pub const fn durability(&self) -> StoreDurability {
457        self.durability
458    }
459
460    /// Return commit participation capability metadata.
461    #[must_use]
462    pub const fn commit(&self) -> StoreCommitParticipation {
463        self.commit
464    }
465
466    /// Return recovery capability metadata.
467    #[must_use]
468    pub const fn recovery(&self) -> StoreRecoveryCapability {
469        self.recovery
470    }
471
472    /// Return schema-metadata persistence capability metadata.
473    #[must_use]
474    pub const fn schema_metadata(&self) -> StoreSchemaMetadataCapability {
475        self.schema_metadata
476    }
477
478    /// Return stable-memory manager ID, when generated wiring supplied it.
479    #[must_use]
480    pub const fn memory_id(&self) -> Option<u8> {
481        self.memory_id
482    }
483
484    /// Return durable stable-memory key, when generated wiring supplied it.
485    #[must_use]
486    pub const fn stable_key(&self) -> Option<&str> {
487        match &self.stable_key {
488            Some(value) => Some(value.as_str()),
489            None => None,
490        }
491    }
492
493    /// Return accepted schema/catalog version, when known.
494    #[must_use]
495    pub const fn schema_version(&self) -> Option<u32> {
496        self.schema_version
497    }
498
499    /// Return accepted schema/catalog fingerprint method version, when known.
500    #[must_use]
501    pub const fn schema_fingerprint_method_version(&self) -> Option<u8> {
502        self.schema_fingerprint_method_version
503    }
504
505    /// Return accepted schema/catalog fingerprint, when known.
506    #[must_use]
507    pub const fn schema_fingerprint(&self) -> Option<&str> {
508        match &self.schema_fingerprint {
509            Some(value) => Some(value.as_str()),
510            None => None,
511        }
512    }
513
514    /// Return row count.
515    #[must_use]
516    pub const fn entries(&self) -> u64 {
517        self.entries
518    }
519
520    /// Return memory usage in bytes.
521    #[must_use]
522    pub const fn memory_bytes(&self) -> u64 {
523        self.memory_bytes
524    }
525}
526
527#[cfg_attr(doc, doc = "IndexStoreSnapshot\n\nIndex-store snapshot row.")]
528#[derive(CandidType, Clone, Debug, Default, Deserialize)]
529pub struct IndexStoreSnapshot {
530    pub(crate) path: String,
531    pub(crate) storage: StoreSnapshotStorageMode,
532    pub(crate) allocation: StoreAllocationIdentityCapability,
533    pub(crate) durability: StoreDurability,
534    pub(crate) commit: StoreCommitParticipation,
535    pub(crate) recovery: StoreRecoveryCapability,
536    pub(crate) schema_metadata: StoreSchemaMetadataCapability,
537    pub(crate) memory_id: Option<u8>,
538    pub(crate) stable_key: Option<String>,
539    pub(crate) schema_version: Option<u32>,
540    pub(crate) schema_fingerprint_method_version: Option<u8>,
541    pub(crate) schema_fingerprint: Option<String>,
542    pub(crate) entries: u64,
543    pub(crate) user_entries: u64,
544    pub(crate) system_entries: u64,
545    pub(crate) memory_bytes: u64,
546    pub(crate) state: IndexState,
547}
548
549impl IndexStoreSnapshot {
550    /// Construct one index-store snapshot row.
551    #[must_use]
552    pub(super) fn new(
553        path: String,
554        storage: StoreSnapshotStorageMode,
555        capabilities: StoreRuntimeStorageCapabilities,
556        allocation: Option<StoreSnapshotAllocationIdentity>,
557        schema_metadata: StoreSnapshotSchemaMetadata,
558        stats: IndexStoreSnapshotStats,
559    ) -> Self {
560        let fields =
561            StoreRoleSnapshotFields::new(path, storage, capabilities, allocation, schema_metadata);
562        Self {
563            path: fields.path,
564            storage: fields.storage,
565            allocation: fields.allocation,
566            durability: fields.durability,
567            commit: fields.commit,
568            recovery: fields.recovery,
569            schema_metadata: fields.schema_metadata,
570            memory_id: fields.memory_id,
571            stable_key: fields.stable_key,
572            schema_version: fields.schema_version,
573            schema_fingerprint_method_version: fields.schema_fingerprint_method_version,
574            schema_fingerprint: fields.schema_fingerprint,
575            entries: stats.entries,
576            user_entries: stats.user_entries,
577            system_entries: stats.system_entries,
578            memory_bytes: stats.memory_bytes,
579            state: stats.state,
580        }
581    }
582
583    /// Borrow store path.
584    #[must_use]
585    pub const fn path(&self) -> &str {
586        self.path.as_str()
587    }
588
589    /// Return diagnostic storage mode.
590    #[must_use]
591    pub const fn storage(&self) -> StoreSnapshotStorageMode {
592        self.storage
593    }
594
595    /// Return allocation-identity capability metadata.
596    #[must_use]
597    pub const fn allocation(&self) -> StoreAllocationIdentityCapability {
598        self.allocation
599    }
600
601    /// Return durability capability metadata.
602    #[must_use]
603    pub const fn durability(&self) -> StoreDurability {
604        self.durability
605    }
606
607    /// Return commit participation capability metadata.
608    #[must_use]
609    pub const fn commit(&self) -> StoreCommitParticipation {
610        self.commit
611    }
612
613    /// Return recovery capability metadata.
614    #[must_use]
615    pub const fn recovery(&self) -> StoreRecoveryCapability {
616        self.recovery
617    }
618
619    /// Return schema-metadata persistence capability metadata.
620    #[must_use]
621    pub const fn schema_metadata(&self) -> StoreSchemaMetadataCapability {
622        self.schema_metadata
623    }
624
625    /// Return stable-memory manager ID, when generated wiring supplied it.
626    #[must_use]
627    pub const fn memory_id(&self) -> Option<u8> {
628        self.memory_id
629    }
630
631    /// Return durable stable-memory key, when generated wiring supplied it.
632    #[must_use]
633    pub const fn stable_key(&self) -> Option<&str> {
634        match &self.stable_key {
635            Some(value) => Some(value.as_str()),
636            None => None,
637        }
638    }
639
640    /// Return accepted schema/catalog version, when known.
641    #[must_use]
642    pub const fn schema_version(&self) -> Option<u32> {
643        self.schema_version
644    }
645
646    /// Return accepted schema/catalog fingerprint method version, when known.
647    #[must_use]
648    pub const fn schema_fingerprint_method_version(&self) -> Option<u8> {
649        self.schema_fingerprint_method_version
650    }
651
652    /// Return accepted schema/catalog fingerprint, when known.
653    #[must_use]
654    pub const fn schema_fingerprint(&self) -> Option<&str> {
655        match &self.schema_fingerprint {
656            Some(value) => Some(value.as_str()),
657            None => None,
658        }
659    }
660
661    /// Return total entry count.
662    #[must_use]
663    pub const fn entries(&self) -> u64 {
664        self.entries
665    }
666
667    /// Return user-namespace entry count.
668    #[must_use]
669    pub const fn user_entries(&self) -> u64 {
670        self.user_entries
671    }
672
673    /// Return system-namespace entry count.
674    #[must_use]
675    pub const fn system_entries(&self) -> u64 {
676        self.system_entries
677    }
678
679    /// Return memory usage in bytes.
680    #[must_use]
681    pub const fn memory_bytes(&self) -> u64 {
682        self.memory_bytes
683    }
684
685    /// Return the current explicit runtime lifecycle state for this index
686    /// store snapshot.
687    #[must_use]
688    pub const fn state(&self) -> IndexState {
689        self.state
690    }
691}
692
693#[cfg_attr(doc, doc = "EntitySnapshot\n\nPer-entity storage snapshot row.")]
694#[derive(CandidType, Clone, Debug, Default, Deserialize)]
695pub struct EntitySnapshot {
696    pub(crate) store: String,
697
698    pub(crate) path: String,
699
700    pub(crate) entries: u64,
701
702    pub(crate) memory_bytes: u64,
703}
704
705impl EntitySnapshot {
706    /// Construct one entity-storage snapshot row.
707    #[must_use]
708    pub(super) const fn new(store: String, path: String, entries: u64, memory_bytes: u64) -> Self {
709        Self {
710            store,
711            path,
712            entries,
713            memory_bytes,
714        }
715    }
716
717    /// Borrow store path.
718    #[must_use]
719    pub const fn store(&self) -> &str {
720        self.store.as_str()
721    }
722
723    /// Borrow entity path.
724    #[must_use]
725    pub const fn path(&self) -> &str {
726        self.path.as_str()
727    }
728
729    /// Return row count.
730    #[must_use]
731    pub const fn entries(&self) -> u64 {
732        self.entries
733    }
734
735    /// Return memory usage in bytes.
736    #[must_use]
737    pub const fn memory_bytes(&self) -> u64 {
738        self.memory_bytes
739    }
740}