1use crate::db::index::IndexState;
7use candid::CandidType;
8use serde::Deserialize;
9
10#[cfg_attr(doc, doc = "StorageReport\n\nLive storage snapshot payload.")]
11#[derive(CandidType, Clone, Debug, Default, Deserialize)]
12pub struct StorageReport {
13 pub(crate) storage_data: Vec<DataStoreSnapshot>,
14 pub(crate) storage_index: Vec<IndexStoreSnapshot>,
15 pub(crate) schema_storage: Vec<SchemaStoreSnapshot>,
16 pub(crate) entity_storage: Vec<EntitySnapshot>,
17 pub(crate) corrupted_keys: u64,
18 pub(crate) corrupted_entries: u64,
19}
20
21#[cfg_attr(
22 doc,
23 doc = "IntegrityTotals\n\nAggregated integrity-scan counters across all stores."
24)]
25#[derive(CandidType, Clone, Debug, Default, Deserialize)]
26pub struct IntegrityTotals {
27 pub(crate) data_rows_scanned: u64,
28 pub(crate) index_entries_scanned: u64,
29 pub(crate) corrupted_data_keys: u64,
30 pub(crate) corrupted_data_rows: u64,
31 pub(crate) corrupted_index_keys: u64,
32 pub(crate) corrupted_index_entries: u64,
33 pub(crate) missing_index_entries: u64,
34 pub(crate) divergent_index_entries: u64,
35 pub(crate) orphan_index_references: u64,
36 pub(crate) misuse_findings: u64,
37}
38
39impl IntegrityTotals {
40 pub(super) const fn add_store_snapshot(&mut self, store: &IntegrityStoreSnapshot) {
41 self.data_rows_scanned = self
42 .data_rows_scanned
43 .saturating_add(store.data_rows_scanned);
44 self.index_entries_scanned = self
45 .index_entries_scanned
46 .saturating_add(store.index_entries_scanned);
47 self.corrupted_data_keys = self
48 .corrupted_data_keys
49 .saturating_add(store.corrupted_data_keys);
50 self.corrupted_data_rows = self
51 .corrupted_data_rows
52 .saturating_add(store.corrupted_data_rows);
53 self.corrupted_index_keys = self
54 .corrupted_index_keys
55 .saturating_add(store.corrupted_index_keys);
56 self.corrupted_index_entries = self
57 .corrupted_index_entries
58 .saturating_add(store.corrupted_index_entries);
59 self.missing_index_entries = self
60 .missing_index_entries
61 .saturating_add(store.missing_index_entries);
62 self.divergent_index_entries = self
63 .divergent_index_entries
64 .saturating_add(store.divergent_index_entries);
65 self.orphan_index_references = self
66 .orphan_index_references
67 .saturating_add(store.orphan_index_references);
68 self.misuse_findings = self.misuse_findings.saturating_add(store.misuse_findings);
69 }
70
71 #[must_use]
73 pub const fn data_rows_scanned(&self) -> u64 {
74 self.data_rows_scanned
75 }
76
77 #[must_use]
79 pub const fn index_entries_scanned(&self) -> u64 {
80 self.index_entries_scanned
81 }
82
83 #[must_use]
85 pub const fn corrupted_data_keys(&self) -> u64 {
86 self.corrupted_data_keys
87 }
88
89 #[must_use]
91 pub const fn corrupted_data_rows(&self) -> u64 {
92 self.corrupted_data_rows
93 }
94
95 #[must_use]
97 pub const fn corrupted_index_keys(&self) -> u64 {
98 self.corrupted_index_keys
99 }
100
101 #[must_use]
103 pub const fn corrupted_index_entries(&self) -> u64 {
104 self.corrupted_index_entries
105 }
106
107 #[must_use]
109 pub const fn missing_index_entries(&self) -> u64 {
110 self.missing_index_entries
111 }
112
113 #[must_use]
115 pub const fn divergent_index_entries(&self) -> u64 {
116 self.divergent_index_entries
117 }
118
119 #[must_use]
121 pub const fn orphan_index_references(&self) -> u64 {
122 self.orphan_index_references
123 }
124
125 #[must_use]
127 pub const fn misuse_findings(&self) -> u64 {
128 self.misuse_findings
129 }
130}
131
132#[cfg_attr(
133 doc,
134 doc = "IntegrityStoreSnapshot\n\nPer-store integrity findings and scan counters."
135)]
136#[derive(CandidType, Clone, Debug, Default, Deserialize)]
137pub struct IntegrityStoreSnapshot {
138 pub(crate) path: String,
139 pub(crate) data_rows_scanned: u64,
140 pub(crate) index_entries_scanned: u64,
141 pub(crate) corrupted_data_keys: u64,
142 pub(crate) corrupted_data_rows: u64,
143 pub(crate) corrupted_index_keys: u64,
144 pub(crate) corrupted_index_entries: u64,
145 pub(crate) missing_index_entries: u64,
146 pub(crate) divergent_index_entries: u64,
147 pub(crate) orphan_index_references: u64,
148 pub(crate) misuse_findings: u64,
149}
150
151impl IntegrityStoreSnapshot {
152 #[must_use]
154 pub(crate) fn new(path: String) -> Self {
155 Self {
156 path,
157 ..Self::default()
158 }
159 }
160
161 #[must_use]
163 pub const fn path(&self) -> &str {
164 self.path.as_str()
165 }
166
167 #[must_use]
169 pub const fn data_rows_scanned(&self) -> u64 {
170 self.data_rows_scanned
171 }
172
173 #[must_use]
175 pub const fn index_entries_scanned(&self) -> u64 {
176 self.index_entries_scanned
177 }
178
179 #[must_use]
181 pub const fn corrupted_data_keys(&self) -> u64 {
182 self.corrupted_data_keys
183 }
184
185 #[must_use]
187 pub const fn corrupted_data_rows(&self) -> u64 {
188 self.corrupted_data_rows
189 }
190
191 #[must_use]
193 pub const fn corrupted_index_keys(&self) -> u64 {
194 self.corrupted_index_keys
195 }
196
197 #[must_use]
199 pub const fn corrupted_index_entries(&self) -> u64 {
200 self.corrupted_index_entries
201 }
202
203 #[must_use]
205 pub const fn missing_index_entries(&self) -> u64 {
206 self.missing_index_entries
207 }
208
209 #[must_use]
211 pub const fn divergent_index_entries(&self) -> u64 {
212 self.divergent_index_entries
213 }
214
215 #[must_use]
217 pub const fn orphan_index_references(&self) -> u64 {
218 self.orphan_index_references
219 }
220
221 #[must_use]
223 pub const fn misuse_findings(&self) -> u64 {
224 self.misuse_findings
225 }
226}
227
228#[cfg_attr(
229 doc,
230 doc = "IntegrityReport\n\nFull integrity-scan output across all registered stores."
231)]
232#[derive(CandidType, Clone, Debug, Default, Deserialize)]
233pub struct IntegrityReport {
234 pub(crate) stores: Vec<IntegrityStoreSnapshot>,
235 pub(crate) totals: IntegrityTotals,
236}
237
238impl IntegrityReport {
239 #[must_use]
241 pub(crate) const fn new(stores: Vec<IntegrityStoreSnapshot>, totals: IntegrityTotals) -> Self {
242 Self { stores, totals }
243 }
244
245 #[must_use]
247 pub const fn stores(&self) -> &[IntegrityStoreSnapshot] {
248 self.stores.as_slice()
249 }
250
251 #[must_use]
253 pub const fn totals(&self) -> &IntegrityTotals {
254 &self.totals
255 }
256}
257
258impl StorageReport {
259 #[must_use]
261 pub(crate) const fn new(
262 storage_data: Vec<DataStoreSnapshot>,
263 storage_index: Vec<IndexStoreSnapshot>,
264 schema_storage: Vec<SchemaStoreSnapshot>,
265 entity_storage: Vec<EntitySnapshot>,
266 corrupted_keys: u64,
267 corrupted_entries: u64,
268 ) -> Self {
269 Self {
270 storage_data,
271 storage_index,
272 schema_storage,
273 entity_storage,
274 corrupted_keys,
275 corrupted_entries,
276 }
277 }
278
279 #[must_use]
281 pub const fn storage_data(&self) -> &[DataStoreSnapshot] {
282 self.storage_data.as_slice()
283 }
284
285 #[must_use]
287 pub const fn storage_index(&self) -> &[IndexStoreSnapshot] {
288 self.storage_index.as_slice()
289 }
290
291 #[must_use]
293 pub const fn schema_storage(&self) -> &[SchemaStoreSnapshot] {
294 self.schema_storage.as_slice()
295 }
296
297 #[must_use]
299 pub const fn entity_storage(&self) -> &[EntitySnapshot] {
300 self.entity_storage.as_slice()
301 }
302
303 #[must_use]
305 pub const fn corrupted_keys(&self) -> u64 {
306 self.corrupted_keys
307 }
308
309 #[must_use]
311 pub const fn corrupted_entries(&self) -> u64 {
312 self.corrupted_entries
313 }
314}
315
316#[cfg_attr(doc, doc = "SchemaStoreSnapshot\n\nSchema-store diagnostic row.")]
317#[derive(CandidType, Clone, Debug, Default, Deserialize)]
318pub struct SchemaStoreSnapshot {
319 pub(crate) path: String,
320 pub(crate) memory_id: Option<u8>,
321 pub(crate) stable_key: Option<String>,
322 pub(crate) schema_version: Option<u32>,
323 pub(crate) schema_fingerprint: Option<String>,
324 pub(crate) entity_count: u64,
325}
326
327#[derive(Clone, Debug, Eq, PartialEq)]
328pub(crate) struct StoreSnapshotAllocationIdentity {
329 memory_id: u8,
330 stable_key: String,
331}
332
333impl StoreSnapshotAllocationIdentity {
334 pub(crate) const fn new(memory_id: u8, stable_key: String) -> Self {
335 Self {
336 memory_id,
337 stable_key,
338 }
339 }
340
341 const fn memory_id(&self) -> u8 {
342 self.memory_id
343 }
344}
345
346#[derive(Clone, Debug, Default, Eq, PartialEq)]
347pub(crate) struct StoreSnapshotSchemaMetadata {
348 schema_version: Option<u32>,
349 schema_fingerprint: Option<String>,
350}
351
352impl StoreSnapshotSchemaMetadata {
353 pub(crate) const fn absent() -> Self {
354 Self {
355 schema_version: None,
356 schema_fingerprint: None,
357 }
358 }
359
360 pub(crate) const fn new(schema_version: u32, schema_fingerprint: String) -> Self {
361 Self {
362 schema_version: Some(schema_version),
363 schema_fingerprint: Some(schema_fingerprint),
364 }
365 }
366
367 const fn schema_version(&self) -> Option<u32> {
368 self.schema_version
369 }
370
371 fn schema_fingerprint(&self) -> Option<String> {
372 self.schema_fingerprint.clone()
373 }
374}
375
376#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
377pub(crate) struct IndexStoreSnapshotStats {
378 entries: u64,
379 user_entries: u64,
380 system_entries: u64,
381 memory_bytes: u64,
382 state: IndexState,
383}
384
385impl IndexStoreSnapshotStats {
386 pub(crate) const fn new(
387 entries: u64,
388 user_entries: u64,
389 system_entries: u64,
390 memory_bytes: u64,
391 state: IndexState,
392 ) -> Self {
393 Self {
394 entries,
395 user_entries,
396 system_entries,
397 memory_bytes,
398 state,
399 }
400 }
401}
402
403impl SchemaStoreSnapshot {
404 #[must_use]
406 pub(crate) fn new(
407 path: String,
408 allocation: Option<StoreSnapshotAllocationIdentity>,
409 schema_metadata: StoreSnapshotSchemaMetadata,
410 entity_count: u64,
411 ) -> Self {
412 let memory_id = allocation
413 .as_ref()
414 .map(StoreSnapshotAllocationIdentity::memory_id);
415 let stable_key = match allocation {
416 Some(allocation) => Some(allocation.stable_key),
417 None => None,
418 };
419 Self {
420 path,
421 memory_id,
422 stable_key,
423 schema_version: schema_metadata.schema_version(),
424 schema_fingerprint: schema_metadata.schema_fingerprint(),
425 entity_count,
426 }
427 }
428
429 #[must_use]
431 pub const fn path(&self) -> &str {
432 self.path.as_str()
433 }
434
435 #[must_use]
437 pub const fn memory_id(&self) -> Option<u8> {
438 self.memory_id
439 }
440
441 #[must_use]
443 pub const fn stable_key(&self) -> Option<&str> {
444 match &self.stable_key {
445 Some(value) => Some(value.as_str()),
446 None => None,
447 }
448 }
449
450 #[must_use]
452 pub const fn schema_version(&self) -> Option<u32> {
453 self.schema_version
454 }
455
456 #[must_use]
458 pub const fn schema_fingerprint(&self) -> Option<&str> {
459 match &self.schema_fingerprint {
460 Some(value) => Some(value.as_str()),
461 None => None,
462 }
463 }
464
465 #[must_use]
467 pub const fn entity_count(&self) -> u64 {
468 self.entity_count
469 }
470}
471
472#[cfg_attr(doc, doc = "DataStoreSnapshot\n\nData-store snapshot row.")]
473#[derive(CandidType, Clone, Debug, Default, Deserialize)]
474pub struct DataStoreSnapshot {
475 pub(crate) path: String,
476 pub(crate) memory_id: Option<u8>,
477 pub(crate) stable_key: Option<String>,
478 pub(crate) schema_version: Option<u32>,
479 pub(crate) schema_fingerprint: Option<String>,
480 pub(crate) entries: u64,
481 pub(crate) memory_bytes: u64,
482}
483
484impl DataStoreSnapshot {
485 #[must_use]
487 pub(crate) fn new(
488 path: String,
489 allocation: Option<StoreSnapshotAllocationIdentity>,
490 schema_metadata: StoreSnapshotSchemaMetadata,
491 entries: u64,
492 memory_bytes: u64,
493 ) -> Self {
494 let memory_id = allocation
495 .as_ref()
496 .map(StoreSnapshotAllocationIdentity::memory_id);
497 let stable_key = match allocation {
498 Some(allocation) => Some(allocation.stable_key),
499 None => None,
500 };
501 Self {
502 path,
503 memory_id,
504 stable_key,
505 schema_version: schema_metadata.schema_version(),
506 schema_fingerprint: schema_metadata.schema_fingerprint(),
507 entries,
508 memory_bytes,
509 }
510 }
511
512 #[must_use]
514 pub const fn path(&self) -> &str {
515 self.path.as_str()
516 }
517
518 #[must_use]
520 pub const fn memory_id(&self) -> Option<u8> {
521 self.memory_id
522 }
523
524 #[must_use]
526 pub const fn stable_key(&self) -> Option<&str> {
527 match &self.stable_key {
528 Some(value) => Some(value.as_str()),
529 None => None,
530 }
531 }
532
533 #[must_use]
535 pub const fn schema_version(&self) -> Option<u32> {
536 self.schema_version
537 }
538
539 #[must_use]
541 pub const fn schema_fingerprint(&self) -> Option<&str> {
542 match &self.schema_fingerprint {
543 Some(value) => Some(value.as_str()),
544 None => None,
545 }
546 }
547
548 #[must_use]
550 pub const fn entries(&self) -> u64 {
551 self.entries
552 }
553
554 #[must_use]
556 pub const fn memory_bytes(&self) -> u64 {
557 self.memory_bytes
558 }
559}
560
561#[cfg_attr(doc, doc = "IndexStoreSnapshot\n\nIndex-store snapshot row.")]
562#[derive(CandidType, Clone, Debug, Default, Deserialize)]
563pub struct IndexStoreSnapshot {
564 pub(crate) path: String,
565 pub(crate) memory_id: Option<u8>,
566 pub(crate) stable_key: Option<String>,
567 pub(crate) schema_version: Option<u32>,
568 pub(crate) schema_fingerprint: Option<String>,
569 pub(crate) entries: u64,
570 pub(crate) user_entries: u64,
571 pub(crate) system_entries: u64,
572 pub(crate) memory_bytes: u64,
573 pub(crate) state: IndexState,
574}
575
576impl IndexStoreSnapshot {
577 #[must_use]
579 pub(crate) fn new(
580 path: String,
581 allocation: Option<StoreSnapshotAllocationIdentity>,
582 schema_metadata: StoreSnapshotSchemaMetadata,
583 stats: IndexStoreSnapshotStats,
584 ) -> Self {
585 let memory_id = allocation
586 .as_ref()
587 .map(StoreSnapshotAllocationIdentity::memory_id);
588 let stable_key = match allocation {
589 Some(allocation) => Some(allocation.stable_key),
590 None => None,
591 };
592 Self {
593 path,
594 memory_id,
595 stable_key,
596 schema_version: schema_metadata.schema_version(),
597 schema_fingerprint: schema_metadata.schema_fingerprint(),
598 entries: stats.entries,
599 user_entries: stats.user_entries,
600 system_entries: stats.system_entries,
601 memory_bytes: stats.memory_bytes,
602 state: stats.state,
603 }
604 }
605
606 #[must_use]
608 pub const fn path(&self) -> &str {
609 self.path.as_str()
610 }
611
612 #[must_use]
614 pub const fn memory_id(&self) -> Option<u8> {
615 self.memory_id
616 }
617
618 #[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 #[must_use]
629 pub const fn schema_version(&self) -> Option<u32> {
630 self.schema_version
631 }
632
633 #[must_use]
635 pub const fn schema_fingerprint(&self) -> Option<&str> {
636 match &self.schema_fingerprint {
637 Some(value) => Some(value.as_str()),
638 None => None,
639 }
640 }
641
642 #[must_use]
644 pub const fn entries(&self) -> u64 {
645 self.entries
646 }
647
648 #[must_use]
650 pub const fn user_entries(&self) -> u64 {
651 self.user_entries
652 }
653
654 #[must_use]
656 pub const fn system_entries(&self) -> u64 {
657 self.system_entries
658 }
659
660 #[must_use]
662 pub const fn memory_bytes(&self) -> u64 {
663 self.memory_bytes
664 }
665
666 #[must_use]
669 pub const fn state(&self) -> IndexState {
670 self.state
671 }
672}
673
674#[cfg_attr(doc, doc = "EntitySnapshot\n\nPer-entity storage snapshot row.")]
675#[derive(CandidType, Clone, Debug, Default, Deserialize)]
676pub struct EntitySnapshot {
677 pub(crate) store: String,
678
679 pub(crate) path: String,
680
681 pub(crate) entries: u64,
682
683 pub(crate) memory_bytes: u64,
684}
685
686impl EntitySnapshot {
687 #[must_use]
689 pub(crate) const fn new(store: String, path: String, entries: u64, memory_bytes: u64) -> Self {
690 Self {
691 store,
692 path,
693 entries,
694 memory_bytes,
695 }
696 }
697
698 #[must_use]
700 pub const fn store(&self) -> &str {
701 self.store.as_str()
702 }
703
704 #[must_use]
706 pub const fn path(&self) -> &str {
707 self.path.as_str()
708 }
709
710 #[must_use]
712 pub const fn entries(&self) -> u64 {
713 self.entries
714 }
715
716 #[must_use]
718 pub const fn memory_bytes(&self) -> u64 {
719 self.memory_bytes
720 }
721}