1use crate::{
2 ledger::{AllocationLedger, AllocationRecord, GenerationRecord},
3 physical::CommitStoreDiagnostic,
4 slot::AllocationSlotDescriptor,
5};
6use serde::{Deserialize, Serialize};
7
8#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
13pub struct DiagnosticExport {
14 pub ledger_schema_version: u32,
16 pub physical_format_id: u32,
18 pub current_generation: u64,
20 pub ledger_anchor: AllocationSlotDescriptor,
22 pub records: Vec<DiagnosticRecord>,
24 pub generations: Vec<DiagnosticGeneration>,
26 pub commit_recovery: Option<CommitStoreDiagnostic>,
28}
29
30impl DiagnosticExport {
31 #[must_use]
33 pub fn from_ledger(ledger: &AllocationLedger, ledger_anchor: AllocationSlotDescriptor) -> Self {
34 Self::from_ledger_with_commit_recovery(ledger, ledger_anchor, None)
35 }
36
37 #[must_use]
39 pub fn from_ledger_with_commit_recovery(
40 ledger: &AllocationLedger,
41 ledger_anchor: AllocationSlotDescriptor,
42 commit_recovery: Option<CommitStoreDiagnostic>,
43 ) -> Self {
44 Self {
45 ledger_schema_version: ledger.ledger_schema_version,
46 physical_format_id: ledger.physical_format_id,
47 current_generation: ledger.current_generation,
48 ledger_anchor,
49 records: ledger
50 .allocation_history()
51 .records()
52 .iter()
53 .cloned()
54 .map(|allocation| DiagnosticRecord { allocation })
55 .collect(),
56 generations: ledger
57 .allocation_history()
58 .generations()
59 .iter()
60 .cloned()
61 .map(|generation| DiagnosticGeneration { generation })
62 .collect(),
63 commit_recovery,
64 }
65 }
66}
67
68#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
73pub struct DiagnosticRecord {
74 pub allocation: AllocationRecord,
76}
77
78#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
83pub struct DiagnosticGeneration {
84 pub generation: GenerationRecord,
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91 use crate::{
92 declaration::AllocationDeclaration,
93 ledger::{AllocationHistory, AllocationRecord, AllocationState},
94 physical::{CommitRecoveryError, CommitSlotDiagnostic, CommitStoreDiagnostic},
95 schema::SchemaMetadata,
96 };
97
98 #[test]
99 fn diagnostic_export_copies_ledger_records() {
100 let declaration = AllocationDeclaration::new(
101 "app.users.v1",
102 AllocationSlotDescriptor::memory_manager(100).expect("usable slot"),
103 None,
104 SchemaMetadata::default(),
105 )
106 .expect("declaration");
107 let ledger = AllocationLedger {
108 ledger_schema_version: 1,
109 physical_format_id: 1,
110 current_generation: 3,
111 allocation_history: AllocationHistory::from_parts(
112 vec![AllocationRecord::from_declaration(
113 3,
114 declaration,
115 AllocationState::Active,
116 )],
117 vec![GenerationRecord {
118 generation: 3,
119 parent_generation: Some(2),
120 runtime_fingerprint: Some("wasm:abc123".to_string()),
121 declaration_count: 1,
122 committed_at: None,
123 }],
124 ),
125 };
126
127 let export = DiagnosticExport::from_ledger(
128 &ledger,
129 AllocationSlotDescriptor::memory_manager(0).expect("usable slot"),
130 );
131
132 assert_eq!(export.current_generation, 3);
133 assert_eq!(export.records.len(), 1);
134 assert_eq!(export.generations.len(), 1);
135 assert_eq!(
136 export.ledger_anchor,
137 AllocationSlotDescriptor::memory_manager(0).expect("usable slot")
138 );
139 assert_eq!(export.commit_recovery, None);
140 }
141
142 #[test]
143 fn diagnostic_export_can_include_commit_recovery_state() {
144 let ledger = AllocationLedger {
145 ledger_schema_version: 1,
146 physical_format_id: 1,
147 current_generation: 3,
148 allocation_history: AllocationHistory::default(),
149 };
150 let commit_recovery = CommitStoreDiagnostic {
151 slot0: CommitSlotDiagnostic {
152 present: true,
153 generation: Some(3),
154 valid: true,
155 },
156 slot1: CommitSlotDiagnostic {
157 present: false,
158 generation: None,
159 valid: false,
160 },
161 authoritative_generation: Some(3),
162 recovery_error: None,
163 };
164
165 let export = DiagnosticExport::from_ledger_with_commit_recovery(
166 &ledger,
167 AllocationSlotDescriptor::memory_manager(0).expect("usable slot"),
168 Some(commit_recovery),
169 );
170
171 assert_eq!(export.commit_recovery, Some(commit_recovery));
172 }
173
174 #[test]
175 fn diagnostic_export_can_report_recovery_failure() {
176 let ledger = AllocationLedger {
177 ledger_schema_version: 1,
178 physical_format_id: 1,
179 current_generation: 0,
180 allocation_history: AllocationHistory::default(),
181 };
182 let commit_recovery = CommitStoreDiagnostic {
183 slot0: CommitSlotDiagnostic {
184 present: false,
185 generation: None,
186 valid: false,
187 },
188 slot1: CommitSlotDiagnostic {
189 present: false,
190 generation: None,
191 valid: false,
192 },
193 authoritative_generation: None,
194 recovery_error: Some(CommitRecoveryError::NoValidGeneration),
195 };
196
197 let export = DiagnosticExport::from_ledger_with_commit_recovery(
198 &ledger,
199 AllocationSlotDescriptor::memory_manager(0).expect("usable slot"),
200 Some(commit_recovery),
201 );
202
203 assert_eq!(
204 export
205 .commit_recovery
206 .expect("commit recovery")
207 .recovery_error,
208 Some(CommitRecoveryError::NoValidGeneration)
209 );
210 }
211}