Skip to main content

hypercall/snapshot/
sync.rs

1//! Synchronization state tracking for snapshot services.
2
3use std::sync::atomic::{AtomicU8, Ordering};
4
5/// State of service synchronization during startup.
6///
7/// State machine: `Initializing -> CatchingUp -> Ready`
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9#[repr(u8)]
10pub enum SyncState {
11    /// Snapshot restore in progress or just starting
12    Initializing = 0,
13    /// Snapshot restored, replaying messages to catch up
14    CatchingUp = 1,
15    /// Fully caught up and ready to serve
16    Ready = 2,
17}
18
19impl std::fmt::Display for SyncState {
20    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
21        match self {
22            SyncState::Initializing => write!(f, "Initializing"),
23            SyncState::CatchingUp => write!(f, "CatchingUp"),
24            SyncState::Ready => write!(f, "Ready"),
25        }
26    }
27}
28
29/// Thread-safe sync status tracker.
30///
31/// Used to gate API/engine readiness until state is fully restored
32/// and caught up to the live stream.
33pub struct SyncStatus {
34    state: AtomicU8,
35}
36
37impl SyncStatus {
38    /// Create a new sync status in Initializing state.
39    pub fn new() -> Self {
40        Self {
41            state: AtomicU8::new(SyncState::Initializing as u8),
42        }
43    }
44
45    /// Get the current sync state.
46    pub fn state(&self) -> SyncState {
47        match self.state.load(Ordering::SeqCst) {
48            0 => SyncState::Initializing,
49            1 => SyncState::CatchingUp,
50            2 => SyncState::Ready,
51            _ => SyncState::Initializing,
52        }
53    }
54
55    /// Check if ready to serve requests.
56    pub fn is_ready(&self) -> bool {
57        self.state() == SyncState::Ready
58    }
59
60    /// Transition to CatchingUp state.
61    pub fn set_catching_up(&self) {
62        self.state
63            .store(SyncState::CatchingUp as u8, Ordering::SeqCst);
64    }
65
66    /// Transition to Ready state.
67    pub fn set_ready(&self) {
68        self.state.store(SyncState::Ready as u8, Ordering::SeqCst);
69    }
70}
71
72impl Default for SyncStatus {
73    fn default() -> Self {
74        Self::new()
75    }
76}
77
78#[cfg(test)]
79mod tests {
80    use super::*;
81
82    #[test]
83    fn test_sync_status_state_machine() {
84        let status = SyncStatus::new();
85        assert_eq!(status.state(), SyncState::Initializing);
86        assert!(!status.is_ready());
87
88        status.set_catching_up();
89        assert_eq!(status.state(), SyncState::CatchingUp);
90        assert!(!status.is_ready());
91
92        status.set_ready();
93        assert_eq!(status.state(), SyncState::Ready);
94        assert!(status.is_ready());
95    }
96
97    #[test]
98    fn test_sync_state_display() {
99        assert_eq!(format!("{}", SyncState::Initializing), "Initializing");
100        assert_eq!(format!("{}", SyncState::CatchingUp), "CatchingUp");
101        assert_eq!(format!("{}", SyncState::Ready), "Ready");
102    }
103}