1use noxu_sync::RwLock;
7use std::fmt;
8use std::time::{Duration, Instant};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
17pub enum NodeState {
18 Detached,
22
23 Unknown,
28
29 Master,
35
36 Replica,
41
42 Shutdown,
44}
45
46impl NodeState {
47 pub fn is_writable(&self) -> bool {
49 matches!(self, NodeState::Master)
50 }
51
52 pub fn is_readable(&self) -> bool {
54 matches!(self, NodeState::Master | NodeState::Replica)
55 }
56
57 pub fn is_active(&self) -> bool {
59 matches!(
60 self,
61 NodeState::Master | NodeState::Replica | NodeState::Unknown
62 )
63 }
64
65 pub fn is_master(&self) -> bool {
67 matches!(self, NodeState::Master)
68 }
69
70 pub fn is_replica(&self) -> bool {
72 matches!(self, NodeState::Replica)
73 }
74
75 pub fn is_detached(&self) -> bool {
77 matches!(self, NodeState::Detached)
78 }
79
80 pub fn is_unknown(&self) -> bool {
82 matches!(self, NodeState::Unknown)
83 }
84
85 fn valid_transitions(&self) -> &'static [NodeState] {
87 match self {
88 NodeState::Detached => &[NodeState::Unknown, NodeState::Shutdown],
89 NodeState::Unknown => {
90 &[NodeState::Master, NodeState::Replica, NodeState::Shutdown]
91 }
92 NodeState::Master => {
93 &[NodeState::Unknown, NodeState::Replica, NodeState::Shutdown]
94 }
95 NodeState::Replica => {
96 &[NodeState::Unknown, NodeState::Master, NodeState::Shutdown]
97 }
98 NodeState::Shutdown => &[],
99 }
100 }
101
102 pub fn can_transition_to(&self, new_state: NodeState) -> bool {
104 self.valid_transitions().contains(&new_state)
105 }
106}
107
108impl fmt::Display for NodeState {
109 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110 match self {
111 NodeState::Detached => write!(f, "DETACHED"),
112 NodeState::Unknown => write!(f, "UNKNOWN"),
113 NodeState::Master => write!(f, "MASTER"),
114 NodeState::Replica => write!(f, "REPLICA"),
115 NodeState::Shutdown => write!(f, "SHUTDOWN"),
116 }
117 }
118}
119
120pub struct NodeStateMachine {
128 state: RwLock<NodeState>,
130 state_change_time: RwLock<Instant>,
132 transition_count: RwLock<u64>,
134}
135
136impl NodeStateMachine {
137 pub fn new() -> Self {
139 Self {
140 state: RwLock::new(NodeState::Detached),
141 state_change_time: RwLock::new(Instant::now()),
142 transition_count: RwLock::new(0),
143 }
144 }
145
146 pub fn get_state(&self) -> NodeState {
148 *self.state.read()
149 }
150
151 pub fn get_state_change_time(&self) -> Instant {
153 *self.state_change_time.read()
154 }
155
156 pub fn get_transition_count(&self) -> u64 {
158 *self.transition_count.read()
159 }
160
161 pub fn transition_to(
170 &self,
171 new_state: NodeState,
172 ) -> crate::error::Result<NodeState> {
173 let mut state = self.state.write();
174 let old_state = *state;
175
176 if !old_state.can_transition_to(new_state) {
177 return Err(crate::error::RepError::InvalidStateTransition(
178 format!("{} -> {}", old_state, new_state),
179 ));
180 }
181
182 *state = new_state;
183 *self.state_change_time.write() = Instant::now();
184 *self.transition_count.write() += 1;
185
186 log::info!("Node state change from {} to {}", old_state, new_state);
187
188 Ok(old_state)
189 }
190
191 pub fn can_transition_to(&self, new_state: NodeState) -> bool {
193 self.state.read().can_transition_to(new_state)
194 }
195
196 pub fn time_in_state(&self) -> Duration {
198 self.state_change_time.read().elapsed()
199 }
200}
201
202impl Default for NodeStateMachine {
203 fn default() -> Self {
204 Self::new()
205 }
206}
207
208#[cfg(test)]
209mod tests {
210 use super::*;
211
212 #[test]
215 fn test_node_state_is_writable() {
216 assert!(NodeState::Master.is_writable());
217 assert!(!NodeState::Replica.is_writable());
218 assert!(!NodeState::Unknown.is_writable());
219 assert!(!NodeState::Detached.is_writable());
220 assert!(!NodeState::Shutdown.is_writable());
221 }
222
223 #[test]
224 fn test_node_state_is_readable() {
225 assert!(NodeState::Master.is_readable());
226 assert!(NodeState::Replica.is_readable());
227 assert!(!NodeState::Unknown.is_readable());
228 assert!(!NodeState::Detached.is_readable());
229 assert!(!NodeState::Shutdown.is_readable());
230 }
231
232 #[test]
233 fn test_node_state_is_active() {
234 assert!(NodeState::Master.is_active());
235 assert!(NodeState::Replica.is_active());
236 assert!(NodeState::Unknown.is_active());
237 assert!(!NodeState::Detached.is_active());
238 assert!(!NodeState::Shutdown.is_active());
239 }
240
241 #[test]
242 fn test_node_state_convenience_methods() {
243 assert!(NodeState::Master.is_master());
244 assert!(!NodeState::Replica.is_master());
245 assert!(NodeState::Replica.is_replica());
246 assert!(!NodeState::Master.is_replica());
247 assert!(NodeState::Detached.is_detached());
248 assert!(NodeState::Unknown.is_unknown());
249 }
250
251 #[test]
252 fn test_node_state_display() {
253 assert_eq!(format!("{}", NodeState::Detached), "DETACHED");
254 assert_eq!(format!("{}", NodeState::Unknown), "UNKNOWN");
255 assert_eq!(format!("{}", NodeState::Master), "MASTER");
256 assert_eq!(format!("{}", NodeState::Replica), "REPLICA");
257 assert_eq!(format!("{}", NodeState::Shutdown), "SHUTDOWN");
258 }
259
260 #[test]
263 fn test_valid_transitions_from_detached() {
264 assert!(NodeState::Detached.can_transition_to(NodeState::Unknown));
265 assert!(NodeState::Detached.can_transition_to(NodeState::Shutdown));
266 assert!(!NodeState::Detached.can_transition_to(NodeState::Master));
267 assert!(!NodeState::Detached.can_transition_to(NodeState::Replica));
268 assert!(!NodeState::Detached.can_transition_to(NodeState::Detached));
269 }
270
271 #[test]
272 fn test_valid_transitions_from_unknown() {
273 assert!(NodeState::Unknown.can_transition_to(NodeState::Master));
274 assert!(NodeState::Unknown.can_transition_to(NodeState::Replica));
275 assert!(NodeState::Unknown.can_transition_to(NodeState::Shutdown));
276 assert!(!NodeState::Unknown.can_transition_to(NodeState::Detached));
277 assert!(!NodeState::Unknown.can_transition_to(NodeState::Unknown));
278 }
279
280 #[test]
281 fn test_valid_transitions_from_master() {
282 assert!(NodeState::Master.can_transition_to(NodeState::Unknown));
283 assert!(NodeState::Master.can_transition_to(NodeState::Replica));
284 assert!(NodeState::Master.can_transition_to(NodeState::Shutdown));
285 assert!(!NodeState::Master.can_transition_to(NodeState::Detached));
286 assert!(!NodeState::Master.can_transition_to(NodeState::Master));
287 }
288
289 #[test]
290 fn test_valid_transitions_from_replica() {
291 assert!(NodeState::Replica.can_transition_to(NodeState::Unknown));
292 assert!(NodeState::Replica.can_transition_to(NodeState::Master));
293 assert!(NodeState::Replica.can_transition_to(NodeState::Shutdown));
294 assert!(!NodeState::Replica.can_transition_to(NodeState::Detached));
295 assert!(!NodeState::Replica.can_transition_to(NodeState::Replica));
296 }
297
298 #[test]
299 fn test_valid_transitions_from_shutdown() {
300 assert!(!NodeState::Shutdown.can_transition_to(NodeState::Detached));
301 assert!(!NodeState::Shutdown.can_transition_to(NodeState::Unknown));
302 assert!(!NodeState::Shutdown.can_transition_to(NodeState::Master));
303 assert!(!NodeState::Shutdown.can_transition_to(NodeState::Replica));
304 assert!(!NodeState::Shutdown.can_transition_to(NodeState::Shutdown));
305 }
306
307 #[test]
310 fn test_initial_state() {
311 let sm = NodeStateMachine::new();
312 assert_eq!(sm.get_state(), NodeState::Detached);
313 assert_eq!(sm.get_transition_count(), 0);
314 }
315
316 #[test]
317 fn test_default_impl() {
318 let sm = NodeStateMachine::default();
319 assert_eq!(sm.get_state(), NodeState::Detached);
320 }
321
322 #[test]
323 fn test_valid_transition_detached_to_unknown() {
324 let sm = NodeStateMachine::new();
325 let old = sm.transition_to(NodeState::Unknown).unwrap();
326 assert_eq!(old, NodeState::Detached);
327 assert_eq!(sm.get_state(), NodeState::Unknown);
328 assert_eq!(sm.get_transition_count(), 1);
329 }
330
331 #[test]
332 fn test_valid_transition_unknown_to_master() {
333 let sm = NodeStateMachine::new();
334 sm.transition_to(NodeState::Unknown).unwrap();
335 let old = sm.transition_to(NodeState::Master).unwrap();
336 assert_eq!(old, NodeState::Unknown);
337 assert_eq!(sm.get_state(), NodeState::Master);
338 assert_eq!(sm.get_transition_count(), 2);
339 }
340
341 #[test]
342 fn test_valid_transition_unknown_to_replica() {
343 let sm = NodeStateMachine::new();
344 sm.transition_to(NodeState::Unknown).unwrap();
345 let old = sm.transition_to(NodeState::Replica).unwrap();
346 assert_eq!(old, NodeState::Unknown);
347 assert_eq!(sm.get_state(), NodeState::Replica);
348 }
349
350 #[test]
351 fn test_valid_transition_master_to_unknown() {
352 let sm = NodeStateMachine::new();
353 sm.transition_to(NodeState::Unknown).unwrap();
354 sm.transition_to(NodeState::Master).unwrap();
355 let old = sm.transition_to(NodeState::Unknown).unwrap();
356 assert_eq!(old, NodeState::Master);
357 assert_eq!(sm.get_state(), NodeState::Unknown);
358 }
359
360 #[test]
361 fn test_valid_transition_master_to_replica() {
362 let sm = NodeStateMachine::new();
363 sm.transition_to(NodeState::Unknown).unwrap();
364 sm.transition_to(NodeState::Master).unwrap();
365 let old = sm.transition_to(NodeState::Replica).unwrap();
366 assert_eq!(old, NodeState::Master);
367 assert_eq!(sm.get_state(), NodeState::Replica);
368 }
369
370 #[test]
371 fn test_valid_transition_replica_to_unknown() {
372 let sm = NodeStateMachine::new();
373 sm.transition_to(NodeState::Unknown).unwrap();
374 sm.transition_to(NodeState::Replica).unwrap();
375 let old = sm.transition_to(NodeState::Unknown).unwrap();
376 assert_eq!(old, NodeState::Replica);
377 }
378
379 #[test]
380 fn test_valid_transition_replica_to_master() {
381 let sm = NodeStateMachine::new();
382 sm.transition_to(NodeState::Unknown).unwrap();
383 sm.transition_to(NodeState::Replica).unwrap();
384 let old = sm.transition_to(NodeState::Master).unwrap();
385 assert_eq!(old, NodeState::Replica);
386 assert_eq!(sm.get_state(), NodeState::Master);
387 }
388
389 #[test]
390 fn test_valid_transition_to_shutdown_from_all() {
391 let sm = NodeStateMachine::new();
393 sm.transition_to(NodeState::Shutdown).unwrap();
394 assert_eq!(sm.get_state(), NodeState::Shutdown);
395
396 let sm = NodeStateMachine::new();
398 sm.transition_to(NodeState::Unknown).unwrap();
399 sm.transition_to(NodeState::Shutdown).unwrap();
400 assert_eq!(sm.get_state(), NodeState::Shutdown);
401
402 let sm = NodeStateMachine::new();
404 sm.transition_to(NodeState::Unknown).unwrap();
405 sm.transition_to(NodeState::Master).unwrap();
406 sm.transition_to(NodeState::Shutdown).unwrap();
407 assert_eq!(sm.get_state(), NodeState::Shutdown);
408
409 let sm = NodeStateMachine::new();
411 sm.transition_to(NodeState::Unknown).unwrap();
412 sm.transition_to(NodeState::Replica).unwrap();
413 sm.transition_to(NodeState::Shutdown).unwrap();
414 assert_eq!(sm.get_state(), NodeState::Shutdown);
415 }
416
417 #[test]
418 fn test_invalid_transition_detached_to_master() {
419 let sm = NodeStateMachine::new();
420 let result = sm.transition_to(NodeState::Master);
421 assert!(result.is_err());
422 assert_eq!(sm.get_state(), NodeState::Detached);
423 assert_eq!(sm.get_transition_count(), 0);
424 }
425
426 #[test]
427 fn test_invalid_transition_detached_to_replica() {
428 let sm = NodeStateMachine::new();
429 let result = sm.transition_to(NodeState::Replica);
430 assert!(result.is_err());
431 assert_eq!(sm.get_state(), NodeState::Detached);
432 }
433
434 #[test]
435 fn test_invalid_transition_from_shutdown() {
436 let sm = NodeStateMachine::new();
437 sm.transition_to(NodeState::Shutdown).unwrap();
438
439 assert!(sm.transition_to(NodeState::Detached).is_err());
440 assert!(sm.transition_to(NodeState::Unknown).is_err());
441 assert!(sm.transition_to(NodeState::Master).is_err());
442 assert!(sm.transition_to(NodeState::Replica).is_err());
443 assert!(sm.transition_to(NodeState::Shutdown).is_err());
444 assert_eq!(sm.get_state(), NodeState::Shutdown);
445 assert_eq!(sm.get_transition_count(), 1);
447 }
448
449 #[test]
450 fn test_invalid_self_transition() {
451 let sm = NodeStateMachine::new();
452 assert!(sm.transition_to(NodeState::Detached).is_err());
454
455 sm.transition_to(NodeState::Unknown).unwrap();
456 assert!(sm.transition_to(NodeState::Unknown).is_err());
458 }
459
460 #[test]
461 fn test_transition_counting() {
462 let sm = NodeStateMachine::new();
463 assert_eq!(sm.get_transition_count(), 0);
464
465 sm.transition_to(NodeState::Unknown).unwrap();
466 assert_eq!(sm.get_transition_count(), 1);
467
468 sm.transition_to(NodeState::Master).unwrap();
469 assert_eq!(sm.get_transition_count(), 2);
470
471 sm.transition_to(NodeState::Unknown).unwrap();
472 assert_eq!(sm.get_transition_count(), 3);
473
474 sm.transition_to(NodeState::Replica).unwrap();
475 assert_eq!(sm.get_transition_count(), 4);
476
477 let _ = sm.transition_to(NodeState::Detached);
479 assert_eq!(sm.get_transition_count(), 4);
480 }
481
482 #[test]
483 fn test_time_in_state() {
484 let sm = NodeStateMachine::new();
485 let d = sm.time_in_state();
487 assert!(d < Duration::from_secs(1));
488 }
489
490 #[test]
491 fn test_state_change_time_updates() {
492 let sm = NodeStateMachine::new();
493 let t1 = sm.get_state_change_time();
494 sm.transition_to(NodeState::Unknown).unwrap();
495 let t2 = sm.get_state_change_time();
496 assert!(t2 >= t1);
497 }
498
499 #[test]
500 fn test_can_transition_to_on_machine() {
501 let sm = NodeStateMachine::new();
502 assert!(sm.can_transition_to(NodeState::Unknown));
503 assert!(sm.can_transition_to(NodeState::Shutdown));
504 assert!(!sm.can_transition_to(NodeState::Master));
505
506 sm.transition_to(NodeState::Unknown).unwrap();
507 assert!(sm.can_transition_to(NodeState::Master));
508 assert!(sm.can_transition_to(NodeState::Replica));
509 assert!(!sm.can_transition_to(NodeState::Detached));
510 }
511
512 #[test]
513 fn test_full_lifecycle() {
514 let sm = NodeStateMachine::new();
515 assert_eq!(sm.get_state(), NodeState::Detached);
516
517 sm.transition_to(NodeState::Unknown).unwrap();
519 assert_eq!(sm.get_state(), NodeState::Unknown);
520
521 sm.transition_to(NodeState::Master).unwrap();
523 assert_eq!(sm.get_state(), NodeState::Master);
524 assert!(sm.get_state().is_writable());
525 assert!(sm.get_state().is_readable());
526
527 sm.transition_to(NodeState::Replica).unwrap();
529 assert_eq!(sm.get_state(), NodeState::Replica);
530 assert!(!sm.get_state().is_writable());
531 assert!(sm.get_state().is_readable());
532
533 sm.transition_to(NodeState::Unknown).unwrap();
535 assert_eq!(sm.get_state(), NodeState::Unknown);
536
537 sm.transition_to(NodeState::Master).unwrap();
539 assert_eq!(sm.get_state(), NodeState::Master);
540
541 sm.transition_to(NodeState::Shutdown).unwrap();
543 assert_eq!(sm.get_state(), NodeState::Shutdown);
544 assert!(!sm.get_state().is_writable());
545 assert!(!sm.get_state().is_readable());
546 assert!(!sm.get_state().is_active());
547
548 assert_eq!(sm.get_transition_count(), 6);
549 }
550
551 #[test]
552 fn test_send_sync() {
553 fn assert_send_sync<T: Send + Sync>() {}
554 assert_send_sync::<NodeStateMachine>();
555 assert_send_sync::<NodeState>();
556 }
557}