1use 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
27#[cfg_attr(
28 doc,
29 doc = "IntegrityTotals\n\nAggregated integrity-scan counters across all stores."
30)]
31#[derive(CandidType, Clone, Debug, Default, Deserialize)]
32pub struct IntegrityTotals {
33 pub(crate) data_rows_scanned: u64,
34 pub(crate) index_entries_scanned: u64,
35 pub(crate) corrupted_data_keys: u64,
36 pub(crate) corrupted_data_rows: u64,
37 pub(crate) corrupted_index_keys: u64,
38 pub(crate) corrupted_index_entries: u64,
39 pub(crate) missing_index_entries: u64,
40 pub(crate) divergent_index_entries: u64,
41 pub(crate) orphan_index_references: u64,
42 pub(crate) misuse_findings: u64,
43}
44
45impl IntegrityTotals {
46 pub(super) const fn add_store_snapshot(&mut self, store: &IntegrityStoreSnapshot) {
47 self.data_rows_scanned = self
48 .data_rows_scanned
49 .saturating_add(store.data_rows_scanned);
50 self.index_entries_scanned = self
51 .index_entries_scanned
52 .saturating_add(store.index_entries_scanned);
53 self.corrupted_data_keys = self
54 .corrupted_data_keys
55 .saturating_add(store.corrupted_data_keys);
56 self.corrupted_data_rows = self
57 .corrupted_data_rows
58 .saturating_add(store.corrupted_data_rows);
59 self.corrupted_index_keys = self
60 .corrupted_index_keys
61 .saturating_add(store.corrupted_index_keys);
62 self.corrupted_index_entries = self
63 .corrupted_index_entries
64 .saturating_add(store.corrupted_index_entries);
65 self.missing_index_entries = self
66 .missing_index_entries
67 .saturating_add(store.missing_index_entries);
68 self.divergent_index_entries = self
69 .divergent_index_entries
70 .saturating_add(store.divergent_index_entries);
71 self.orphan_index_references = self
72 .orphan_index_references
73 .saturating_add(store.orphan_index_references);
74 self.misuse_findings = self.misuse_findings.saturating_add(store.misuse_findings);
75 }
76
77 #[must_use]
79 pub const fn data_rows_scanned(&self) -> u64 {
80 self.data_rows_scanned
81 }
82
83 #[must_use]
85 pub const fn index_entries_scanned(&self) -> u64 {
86 self.index_entries_scanned
87 }
88
89 #[must_use]
91 pub const fn corrupted_data_keys(&self) -> u64 {
92 self.corrupted_data_keys
93 }
94
95 #[must_use]
97 pub const fn corrupted_data_rows(&self) -> u64 {
98 self.corrupted_data_rows
99 }
100
101 #[must_use]
103 pub const fn corrupted_index_keys(&self) -> u64 {
104 self.corrupted_index_keys
105 }
106
107 #[must_use]
109 pub const fn corrupted_index_entries(&self) -> u64 {
110 self.corrupted_index_entries
111 }
112
113 #[must_use]
115 pub const fn missing_index_entries(&self) -> u64 {
116 self.missing_index_entries
117 }
118
119 #[must_use]
121 pub const fn divergent_index_entries(&self) -> u64 {
122 self.divergent_index_entries
123 }
124
125 #[must_use]
127 pub const fn orphan_index_references(&self) -> u64 {
128 self.orphan_index_references
129 }
130
131 #[must_use]
133 pub const fn misuse_findings(&self) -> u64 {
134 self.misuse_findings
135 }
136}
137
138#[cfg_attr(
139 doc,
140 doc = "IntegrityStoreSnapshot\n\nPer-store integrity findings and scan counters."
141)]
142#[derive(CandidType, Clone, Debug, Default, Deserialize)]
143pub struct IntegrityStoreSnapshot {
144 pub(crate) path: String,
145 pub(crate) data_rows_scanned: u64,
146 pub(crate) index_entries_scanned: u64,
147 pub(crate) corrupted_data_keys: u64,
148 pub(crate) corrupted_data_rows: u64,
149 pub(crate) corrupted_index_keys: u64,
150 pub(crate) corrupted_index_entries: u64,
151 pub(crate) missing_index_entries: u64,
152 pub(crate) divergent_index_entries: u64,
153 pub(crate) orphan_index_references: u64,
154 pub(crate) misuse_findings: u64,
155}
156
157impl IntegrityStoreSnapshot {
158 #[must_use]
160 pub(crate) fn new(path: String) -> Self {
161 Self {
162 path,
163 ..Self::default()
164 }
165 }
166
167 #[must_use]
169 pub const fn path(&self) -> &str {
170 self.path.as_str()
171 }
172
173 #[must_use]
175 pub const fn data_rows_scanned(&self) -> u64 {
176 self.data_rows_scanned
177 }
178
179 #[must_use]
181 pub const fn index_entries_scanned(&self) -> u64 {
182 self.index_entries_scanned
183 }
184
185 #[must_use]
187 pub const fn corrupted_data_keys(&self) -> u64 {
188 self.corrupted_data_keys
189 }
190
191 #[must_use]
193 pub const fn corrupted_data_rows(&self) -> u64 {
194 self.corrupted_data_rows
195 }
196
197 #[must_use]
199 pub const fn corrupted_index_keys(&self) -> u64 {
200 self.corrupted_index_keys
201 }
202
203 #[must_use]
205 pub const fn corrupted_index_entries(&self) -> u64 {
206 self.corrupted_index_entries
207 }
208
209 #[must_use]
211 pub const fn missing_index_entries(&self) -> u64 {
212 self.missing_index_entries
213 }
214
215 #[must_use]
217 pub const fn divergent_index_entries(&self) -> u64 {
218 self.divergent_index_entries
219 }
220
221 #[must_use]
223 pub const fn orphan_index_references(&self) -> u64 {
224 self.orphan_index_references
225 }
226
227 #[must_use]
229 pub const fn misuse_findings(&self) -> u64 {
230 self.misuse_findings
231 }
232}
233
234#[cfg_attr(
235 doc,
236 doc = "IntegrityReport\n\nFull integrity-scan output across all registered stores."
237)]
238#[derive(CandidType, Clone, Debug, Default, Deserialize)]
239pub struct IntegrityReport {
240 pub(crate) stores: Vec<IntegrityStoreSnapshot>,
241 pub(crate) totals: IntegrityTotals,
242}
243
244impl IntegrityReport {
245 #[must_use]
247 pub(crate) const fn new(stores: Vec<IntegrityStoreSnapshot>, totals: IntegrityTotals) -> Self {
248 Self { stores, totals }
249 }
250
251 #[must_use]
253 pub const fn stores(&self) -> &[IntegrityStoreSnapshot] {
254 self.stores.as_slice()
255 }
256
257 #[must_use]
259 pub const fn totals(&self) -> &IntegrityTotals {
260 &self.totals
261 }
262}
263
264impl StorageReport {
265 #[must_use]
267 pub(crate) const fn new(
268 storage_data: Vec<DataStoreSnapshot>,
269 storage_index: Vec<IndexStoreSnapshot>,
270 schema_storage: Vec<SchemaStoreSnapshot>,
271 entity_storage: Vec<EntitySnapshot>,
272 corrupted_keys: u64,
273 corrupted_entries: u64,
274 ) -> Self {
275 Self {
276 storage_data,
277 storage_index,
278 schema_storage,
279 entity_storage,
280 corrupted_keys,
281 corrupted_entries,
282 }
283 }
284
285 #[must_use]
287 pub const fn storage_data(&self) -> &[DataStoreSnapshot] {
288 self.storage_data.as_slice()
289 }
290
291 #[must_use]
293 pub const fn storage_index(&self) -> &[IndexStoreSnapshot] {
294 self.storage_index.as_slice()
295 }
296
297 #[must_use]
299 pub const fn schema_storage(&self) -> &[SchemaStoreSnapshot] {
300 self.schema_storage.as_slice()
301 }
302
303 #[must_use]
305 pub const fn entity_storage(&self) -> &[EntitySnapshot] {
306 self.entity_storage.as_slice()
307 }
308
309 #[must_use]
311 pub const fn corrupted_keys(&self) -> u64 {
312 self.corrupted_keys
313 }
314
315 #[must_use]
317 pub const fn corrupted_entries(&self) -> u64 {
318 self.corrupted_entries
319 }
320}
321
322#[cfg_attr(doc, doc = "SchemaStoreSnapshot\n\nSchema-store diagnostic row.")]
323#[derive(CandidType, Clone, Debug, Default, Deserialize)]
324pub struct SchemaStoreSnapshot {
325 pub(crate) path: String,
326 pub(crate) storage: StoreSnapshotStorageMode,
327 pub(crate) allocation: StoreAllocationIdentityCapability,
328 pub(crate) durability: StoreDurability,
329 pub(crate) commit: StoreCommitParticipation,
330 pub(crate) recovery: StoreRecoveryCapability,
331 pub(crate) schema_metadata: StoreSchemaMetadataCapability,
332 pub(crate) memory_id: Option<u8>,
333 pub(crate) stable_key: Option<String>,
334 pub(crate) schema_version: Option<u32>,
335 pub(crate) schema_fingerprint: Option<String>,
336 pub(crate) entity_count: u64,
337}
338
339#[derive(CandidType, Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
344pub enum StoreSnapshotStorageMode {
345 #[default]
346 Stable,
347 Heap,
348}
349
350impl StoreSnapshotStorageMode {
351 #[must_use]
353 pub const fn as_str(self) -> &'static str {
354 match self {
355 Self::Stable => "stable",
356 Self::Heap => "heap",
357 }
358 }
359}
360
361#[derive(Clone, Debug, Eq, PartialEq)]
362pub(crate) struct StoreSnapshotAllocationIdentity {
363 memory_id: u8,
364 stable_key: String,
365}
366
367impl StoreSnapshotAllocationIdentity {
368 pub(crate) const fn new(memory_id: u8, stable_key: String) -> Self {
369 Self {
370 memory_id,
371 stable_key,
372 }
373 }
374
375 const fn memory_id(&self) -> u8 {
376 self.memory_id
377 }
378}
379
380#[derive(Clone, Debug, Default, Eq, PartialEq)]
381pub(crate) struct StoreSnapshotSchemaMetadata {
382 schema_version: Option<u32>,
383 schema_fingerprint: Option<String>,
384}
385
386impl StoreSnapshotSchemaMetadata {
387 pub(crate) const fn absent() -> Self {
388 Self {
389 schema_version: None,
390 schema_fingerprint: None,
391 }
392 }
393
394 pub(crate) const fn new(schema_version: u32, schema_fingerprint: String) -> Self {
395 Self {
396 schema_version: Some(schema_version),
397 schema_fingerprint: Some(schema_fingerprint),
398 }
399 }
400
401 const fn schema_version(&self) -> Option<u32> {
402 self.schema_version
403 }
404
405 fn schema_fingerprint(&self) -> Option<String> {
406 self.schema_fingerprint.clone()
407 }
408}
409
410#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
411pub(crate) struct IndexStoreSnapshotStats {
412 entries: u64,
413 user_entries: u64,
414 system_entries: u64,
415 memory_bytes: u64,
416 state: IndexState,
417}
418
419impl IndexStoreSnapshotStats {
420 pub(crate) const fn new(
421 entries: u64,
422 user_entries: u64,
423 system_entries: u64,
424 memory_bytes: u64,
425 state: IndexState,
426 ) -> Self {
427 Self {
428 entries,
429 user_entries,
430 system_entries,
431 memory_bytes,
432 state,
433 }
434 }
435}
436
437impl SchemaStoreSnapshot {
438 #[must_use]
440 pub(crate) fn new(
441 path: String,
442 storage: StoreSnapshotStorageMode,
443 capabilities: StoreRuntimeStorageCapabilities,
444 allocation: Option<StoreSnapshotAllocationIdentity>,
445 schema_metadata: StoreSnapshotSchemaMetadata,
446 entity_count: u64,
447 ) -> Self {
448 let memory_id = allocation
449 .as_ref()
450 .map(StoreSnapshotAllocationIdentity::memory_id);
451 let stable_key = match allocation {
452 Some(allocation) => Some(allocation.stable_key),
453 None => None,
454 };
455 Self {
456 path,
457 storage,
458 allocation: capabilities.allocation_identity(),
459 durability: capabilities.durability(),
460 commit: capabilities.commit_participation(),
461 recovery: capabilities.recovery(),
462 schema_metadata: capabilities.schema_metadata(),
463 memory_id,
464 stable_key,
465 schema_version: schema_metadata.schema_version(),
466 schema_fingerprint: schema_metadata.schema_fingerprint(),
467 entity_count,
468 }
469 }
470
471 #[must_use]
473 pub const fn path(&self) -> &str {
474 self.path.as_str()
475 }
476
477 #[must_use]
479 pub const fn storage(&self) -> StoreSnapshotStorageMode {
480 self.storage
481 }
482
483 #[must_use]
485 pub const fn allocation(&self) -> StoreAllocationIdentityCapability {
486 self.allocation
487 }
488
489 #[must_use]
491 pub const fn durability(&self) -> StoreDurability {
492 self.durability
493 }
494
495 #[must_use]
497 pub const fn commit(&self) -> StoreCommitParticipation {
498 self.commit
499 }
500
501 #[must_use]
503 pub const fn recovery(&self) -> StoreRecoveryCapability {
504 self.recovery
505 }
506
507 #[must_use]
509 pub const fn schema_metadata(&self) -> StoreSchemaMetadataCapability {
510 self.schema_metadata
511 }
512
513 #[must_use]
515 pub const fn memory_id(&self) -> Option<u8> {
516 self.memory_id
517 }
518
519 #[must_use]
521 pub const fn stable_key(&self) -> Option<&str> {
522 match &self.stable_key {
523 Some(value) => Some(value.as_str()),
524 None => None,
525 }
526 }
527
528 #[must_use]
530 pub const fn schema_version(&self) -> Option<u32> {
531 self.schema_version
532 }
533
534 #[must_use]
536 pub const fn schema_fingerprint(&self) -> Option<&str> {
537 match &self.schema_fingerprint {
538 Some(value) => Some(value.as_str()),
539 None => None,
540 }
541 }
542
543 #[must_use]
545 pub const fn entity_count(&self) -> u64 {
546 self.entity_count
547 }
548}
549
550#[cfg_attr(doc, doc = "DataStoreSnapshot\n\nData-store snapshot row.")]
551#[derive(CandidType, Clone, Debug, Default, Deserialize)]
552pub struct DataStoreSnapshot {
553 pub(crate) path: String,
554 pub(crate) storage: StoreSnapshotStorageMode,
555 pub(crate) allocation: StoreAllocationIdentityCapability,
556 pub(crate) durability: StoreDurability,
557 pub(crate) commit: StoreCommitParticipation,
558 pub(crate) recovery: StoreRecoveryCapability,
559 pub(crate) schema_metadata: StoreSchemaMetadataCapability,
560 pub(crate) memory_id: Option<u8>,
561 pub(crate) stable_key: Option<String>,
562 pub(crate) schema_version: Option<u32>,
563 pub(crate) schema_fingerprint: Option<String>,
564 pub(crate) entries: u64,
565 pub(crate) memory_bytes: u64,
566}
567
568impl DataStoreSnapshot {
569 #[must_use]
571 pub(crate) fn new(
572 path: String,
573 storage: StoreSnapshotStorageMode,
574 capabilities: StoreRuntimeStorageCapabilities,
575 allocation: Option<StoreSnapshotAllocationIdentity>,
576 schema_metadata: StoreSnapshotSchemaMetadata,
577 entries: u64,
578 memory_bytes: u64,
579 ) -> Self {
580 let memory_id = allocation
581 .as_ref()
582 .map(StoreSnapshotAllocationIdentity::memory_id);
583 let stable_key = match allocation {
584 Some(allocation) => Some(allocation.stable_key),
585 None => None,
586 };
587 Self {
588 path,
589 storage,
590 allocation: capabilities.allocation_identity(),
591 durability: capabilities.durability(),
592 commit: capabilities.commit_participation(),
593 recovery: capabilities.recovery(),
594 schema_metadata: capabilities.schema_metadata(),
595 memory_id,
596 stable_key,
597 schema_version: schema_metadata.schema_version(),
598 schema_fingerprint: schema_metadata.schema_fingerprint(),
599 entries,
600 memory_bytes,
601 }
602 }
603
604 #[must_use]
606 pub const fn path(&self) -> &str {
607 self.path.as_str()
608 }
609
610 #[must_use]
612 pub const fn storage(&self) -> StoreSnapshotStorageMode {
613 self.storage
614 }
615
616 #[must_use]
618 pub const fn allocation(&self) -> StoreAllocationIdentityCapability {
619 self.allocation
620 }
621
622 #[must_use]
624 pub const fn durability(&self) -> StoreDurability {
625 self.durability
626 }
627
628 #[must_use]
630 pub const fn commit(&self) -> StoreCommitParticipation {
631 self.commit
632 }
633
634 #[must_use]
636 pub const fn recovery(&self) -> StoreRecoveryCapability {
637 self.recovery
638 }
639
640 #[must_use]
642 pub const fn schema_metadata(&self) -> StoreSchemaMetadataCapability {
643 self.schema_metadata
644 }
645
646 #[must_use]
648 pub const fn memory_id(&self) -> Option<u8> {
649 self.memory_id
650 }
651
652 #[must_use]
654 pub const fn stable_key(&self) -> Option<&str> {
655 match &self.stable_key {
656 Some(value) => Some(value.as_str()),
657 None => None,
658 }
659 }
660
661 #[must_use]
663 pub const fn schema_version(&self) -> Option<u32> {
664 self.schema_version
665 }
666
667 #[must_use]
669 pub const fn schema_fingerprint(&self) -> Option<&str> {
670 match &self.schema_fingerprint {
671 Some(value) => Some(value.as_str()),
672 None => None,
673 }
674 }
675
676 #[must_use]
678 pub const fn entries(&self) -> u64 {
679 self.entries
680 }
681
682 #[must_use]
684 pub const fn memory_bytes(&self) -> u64 {
685 self.memory_bytes
686 }
687}
688
689#[cfg_attr(doc, doc = "IndexStoreSnapshot\n\nIndex-store snapshot row.")]
690#[derive(CandidType, Clone, Debug, Default, Deserialize)]
691pub struct IndexStoreSnapshot {
692 pub(crate) path: String,
693 pub(crate) storage: StoreSnapshotStorageMode,
694 pub(crate) allocation: StoreAllocationIdentityCapability,
695 pub(crate) durability: StoreDurability,
696 pub(crate) commit: StoreCommitParticipation,
697 pub(crate) recovery: StoreRecoveryCapability,
698 pub(crate) schema_metadata: StoreSchemaMetadataCapability,
699 pub(crate) memory_id: Option<u8>,
700 pub(crate) stable_key: Option<String>,
701 pub(crate) schema_version: Option<u32>,
702 pub(crate) schema_fingerprint: Option<String>,
703 pub(crate) entries: u64,
704 pub(crate) user_entries: u64,
705 pub(crate) system_entries: u64,
706 pub(crate) memory_bytes: u64,
707 pub(crate) state: IndexState,
708}
709
710impl IndexStoreSnapshot {
711 #[must_use]
713 pub(crate) fn new(
714 path: String,
715 storage: StoreSnapshotStorageMode,
716 capabilities: StoreRuntimeStorageCapabilities,
717 allocation: Option<StoreSnapshotAllocationIdentity>,
718 schema_metadata: StoreSnapshotSchemaMetadata,
719 stats: IndexStoreSnapshotStats,
720 ) -> Self {
721 let memory_id = allocation
722 .as_ref()
723 .map(StoreSnapshotAllocationIdentity::memory_id);
724 let stable_key = match allocation {
725 Some(allocation) => Some(allocation.stable_key),
726 None => None,
727 };
728 Self {
729 path,
730 storage,
731 allocation: capabilities.allocation_identity(),
732 durability: capabilities.durability(),
733 commit: capabilities.commit_participation(),
734 recovery: capabilities.recovery(),
735 schema_metadata: capabilities.schema_metadata(),
736 memory_id,
737 stable_key,
738 schema_version: schema_metadata.schema_version(),
739 schema_fingerprint: schema_metadata.schema_fingerprint(),
740 entries: stats.entries,
741 user_entries: stats.user_entries,
742 system_entries: stats.system_entries,
743 memory_bytes: stats.memory_bytes,
744 state: stats.state,
745 }
746 }
747
748 #[must_use]
750 pub const fn path(&self) -> &str {
751 self.path.as_str()
752 }
753
754 #[must_use]
756 pub const fn storage(&self) -> StoreSnapshotStorageMode {
757 self.storage
758 }
759
760 #[must_use]
762 pub const fn allocation(&self) -> StoreAllocationIdentityCapability {
763 self.allocation
764 }
765
766 #[must_use]
768 pub const fn durability(&self) -> StoreDurability {
769 self.durability
770 }
771
772 #[must_use]
774 pub const fn commit(&self) -> StoreCommitParticipation {
775 self.commit
776 }
777
778 #[must_use]
780 pub const fn recovery(&self) -> StoreRecoveryCapability {
781 self.recovery
782 }
783
784 #[must_use]
786 pub const fn schema_metadata(&self) -> StoreSchemaMetadataCapability {
787 self.schema_metadata
788 }
789
790 #[must_use]
792 pub const fn memory_id(&self) -> Option<u8> {
793 self.memory_id
794 }
795
796 #[must_use]
798 pub const fn stable_key(&self) -> Option<&str> {
799 match &self.stable_key {
800 Some(value) => Some(value.as_str()),
801 None => None,
802 }
803 }
804
805 #[must_use]
807 pub const fn schema_version(&self) -> Option<u32> {
808 self.schema_version
809 }
810
811 #[must_use]
813 pub const fn schema_fingerprint(&self) -> Option<&str> {
814 match &self.schema_fingerprint {
815 Some(value) => Some(value.as_str()),
816 None => None,
817 }
818 }
819
820 #[must_use]
822 pub const fn entries(&self) -> u64 {
823 self.entries
824 }
825
826 #[must_use]
828 pub const fn user_entries(&self) -> u64 {
829 self.user_entries
830 }
831
832 #[must_use]
834 pub const fn system_entries(&self) -> u64 {
835 self.system_entries
836 }
837
838 #[must_use]
840 pub const fn memory_bytes(&self) -> u64 {
841 self.memory_bytes
842 }
843
844 #[must_use]
847 pub const fn state(&self) -> IndexState {
848 self.state
849 }
850}
851
852#[cfg_attr(doc, doc = "EntitySnapshot\n\nPer-entity storage snapshot row.")]
853#[derive(CandidType, Clone, Debug, Default, Deserialize)]
854pub struct EntitySnapshot {
855 pub(crate) store: String,
856
857 pub(crate) path: String,
858
859 pub(crate) entries: u64,
860
861 pub(crate) memory_bytes: u64,
862}
863
864impl EntitySnapshot {
865 #[must_use]
867 pub(crate) const fn new(store: String, path: String, entries: u64, memory_bytes: u64) -> Self {
868 Self {
869 store,
870 path,
871 entries,
872 memory_bytes,
873 }
874 }
875
876 #[must_use]
878 pub const fn store(&self) -> &str {
879 self.store.as_str()
880 }
881
882 #[must_use]
884 pub const fn path(&self) -> &str {
885 self.path.as_str()
886 }
887
888 #[must_use]
890 pub const fn entries(&self) -> u64 {
891 self.entries
892 }
893
894 #[must_use]
896 pub const fn memory_bytes(&self) -> u64 {
897 self.memory_bytes
898 }
899}