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