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