peat_mesh/topology/
autonomous.rs1use super::partition::{PartitionEvent, PartitionHandler};
39use std::sync::{Arc, RwLock};
40use std::time::Instant;
41use tracing::{info, warn};
42
43#[derive(Debug, Clone, PartialEq, Eq, Default)]
45pub enum AutonomousState {
46 #[default]
48 Normal,
49
50 Autonomous {
52 entered_at: Instant,
54
55 detection_attempts: u32,
57 },
58}
59
60#[derive(Debug)]
67pub struct AutonomousOperationHandler {
68 state: Arc<RwLock<AutonomousState>>,
69}
70
71impl AutonomousOperationHandler {
72 pub fn new() -> Self {
74 Self {
75 state: Arc::new(RwLock::new(AutonomousState::default())),
76 }
77 }
78
79 pub fn is_autonomous(&self) -> bool {
81 matches!(
82 *self.state.read().unwrap_or_else(|e| e.into_inner()),
83 AutonomousState::Autonomous { .. }
84 )
85 }
86
87 pub fn get_state(&self) -> AutonomousState {
89 self.state.read().unwrap_or_else(|e| e.into_inner()).clone()
90 }
91
92 fn enter_autonomous_mode(&self, detection_attempts: u32) {
94 let mut state = self.state.write().unwrap_or_else(|e| e.into_inner());
95
96 if matches!(*state, AutonomousState::Autonomous { .. }) {
97 warn!("Already in autonomous mode, ignoring duplicate partition detection");
98 return;
99 }
100
101 *state = AutonomousState::Autonomous {
102 entered_at: Instant::now(),
103 detection_attempts,
104 };
105
106 info!(
107 "Entered autonomous operation mode after {} detection attempts",
108 detection_attempts
109 );
110 }
111
112 fn exit_autonomous_mode(&self, autonomous_duration: std::time::Duration) {
114 let mut state = self.state.write().unwrap_or_else(|e| e.into_inner());
115
116 if matches!(*state, AutonomousState::Normal) {
117 warn!("Already in normal mode, ignoring duplicate partition healing");
118 return;
119 }
120
121 *state = AutonomousState::Normal;
122
123 info!(
124 "Exited autonomous operation mode after {:?} duration",
125 autonomous_duration
126 );
127 }
128}
129
130impl Default for AutonomousOperationHandler {
131 fn default() -> Self {
132 Self::new()
133 }
134}
135
136impl PartitionHandler for AutonomousOperationHandler {
137 fn on_partition_detected(&self, event: &PartitionEvent) {
138 if let PartitionEvent::PartitionDetected {
139 attempts,
140 detection_duration,
141 } = event
142 {
143 info!(
144 "Network partition detected after {} attempts over {:?}",
145 attempts, detection_duration
146 );
147
148 self.enter_autonomous_mode(*attempts);
149 }
150 }
151
152 fn on_partition_healed(&self, event: &PartitionEvent) {
153 if let PartitionEvent::PartitionHealed {
154 visible_beacons,
155 partition_duration,
156 } = event
157 {
158 info!(
159 "Network partition healed, {} higher-level beacons visible, partition lasted {:?}",
160 visible_beacons, partition_duration
161 );
162
163 self.exit_autonomous_mode(*partition_duration);
164 }
165 }
166}
167
168#[cfg(test)]
169mod tests {
170 use super::*;
171 use std::time::Duration;
172
173 #[test]
174 fn test_autonomous_handler_creation() {
175 let handler = AutonomousOperationHandler::new();
176 assert!(!handler.is_autonomous());
177 assert_eq!(handler.get_state(), AutonomousState::Normal);
178 }
179
180 #[test]
181 fn test_enter_autonomous_mode() {
182 let handler = AutonomousOperationHandler::new();
183
184 let event = PartitionEvent::PartitionDetected {
185 attempts: 3,
186 detection_duration: Duration::from_secs(6),
187 };
188
189 handler.on_partition_detected(&event);
190
191 assert!(handler.is_autonomous());
192
193 match handler.get_state() {
194 AutonomousState::Autonomous {
195 detection_attempts, ..
196 } => {
197 assert_eq!(detection_attempts, 3);
198 }
199 _ => panic!("Expected Autonomous state"),
200 }
201 }
202
203 #[test]
204 fn test_exit_autonomous_mode() {
205 let handler = AutonomousOperationHandler::new();
206
207 let partition_event = PartitionEvent::PartitionDetected {
209 attempts: 3,
210 detection_duration: Duration::from_secs(6),
211 };
212 handler.on_partition_detected(&partition_event);
213 assert!(handler.is_autonomous());
214
215 let healed_event = PartitionEvent::PartitionHealed {
217 visible_beacons: 2,
218 partition_duration: Duration::from_secs(120),
219 };
220 handler.on_partition_healed(&healed_event);
221
222 assert!(!handler.is_autonomous());
223 assert_eq!(handler.get_state(), AutonomousState::Normal);
224 }
225
226 #[test]
227 fn test_duplicate_partition_detection() {
228 let handler = AutonomousOperationHandler::new();
229
230 let event = PartitionEvent::PartitionDetected {
231 attempts: 3,
232 detection_duration: Duration::from_secs(6),
233 };
234
235 handler.on_partition_detected(&event);
237 assert!(handler.is_autonomous());
238
239 let state1 = handler.get_state();
240
241 handler.on_partition_detected(&event);
243
244 let state2 = handler.get_state();
245 assert_eq!(state1, state2);
246 }
247
248 #[test]
249 fn test_duplicate_partition_healing() {
250 let handler = AutonomousOperationHandler::new();
251
252 let healed_event = PartitionEvent::PartitionHealed {
253 visible_beacons: 2,
254 partition_duration: Duration::from_secs(120),
255 };
256
257 handler.on_partition_healed(&healed_event);
259 assert!(!handler.is_autonomous());
260 assert_eq!(handler.get_state(), AutonomousState::Normal);
261 }
262
263 #[test]
264 fn test_autonomous_state_transition() {
265 let handler = AutonomousOperationHandler::new();
266
267 let partition_event = PartitionEvent::PartitionDetected {
269 attempts: 5,
270 detection_duration: Duration::from_secs(10),
271 };
272 handler.on_partition_detected(&partition_event);
273
274 assert!(handler.is_autonomous());
275
276 let healed_event = PartitionEvent::PartitionHealed {
278 visible_beacons: 1,
279 partition_duration: Duration::from_secs(300),
280 };
281 handler.on_partition_healed(&healed_event);
282
283 assert!(!handler.is_autonomous());
284 }
285}