Skip to main content

optirs_tpu/pod_coordination/synchronization/clocks/
mod.rs

1// Clock Synchronization Module
2//
3// This module provides comprehensive clock synchronization capabilities for TPU pod coordination.
4// The module is organized into focused sub-modules that handle different aspects of time synchronization:
5//
6// - [`core`] - Main synchronization manager and coordination logic
7// - [`protocols`] - Synchronization protocols (NTP, PTP, GPS, etc.)
8// - [`sources`] - Time source management and selection algorithms
9// - [`gps`] - GPS signal processing and error correction
10// - [`network`] - Network synchronization, messaging, and load balancing
11// - [`quality`] - Quality monitoring and assessment
12// - [`drift`] - Drift compensation and prediction
13// - [`health`] - Health monitoring and recovery
14// - [`statistics`] - Performance tracking and reporting
15//
16// # Architecture
17//
18// The clock synchronization system follows a modular architecture where each component
19// has a specific responsibility but can work together to provide robust time synchronization:
20//
21// ```text
22// ┌─────────────────────────────────────────────────────────────────┐
23// │                    ClockSynchronizationManager                  │
24// │                         (core module)                          │
25// └─────────────────────────┬───────────────────────────────────────┘
26//                           │
27// ┌─────────────────────────┼───────────────────────────────────────┐
28// │         TimeSourceManager        │        ProtocolManager        │
29// │         (sources module)         │       (protocols module)      │
30// └─────────────────────────┬───────┴───────┬───────────────────────┘
31//                           │               │
32// ┌─────────────────────────┼───────────────┼───────────────────────┐
33// │      GPS Processing     │   Network     │    Quality & Health   │
34// │      (gps module)       │  (network)    │  (quality & health)   │
35// └─────────────────────────┼───────────────┼───────────────────────┘
36//                           │               │
37// ┌─────────────────────────┼───────────────┼───────────────────────┐
38// │   Drift Compensation    │  Statistics   │      Reporting        │
39// │     (drift module)      │ (statistics)  │    (statistics)       │
40// └─────────────────────────┴───────────────┴───────────────────────┘
41// ```
42//
43// # Usage
44//
45// Basic usage of the clock synchronization system:
46//
47// ```rust
48// use crate::pod_coordination::synchronization::clocks::{
49//     ClockSynchronizationManager, ClockSynchronizationConfig
50// };
51//
52// # fn example() -> Result<(), Box<dyn std::error::Error>> {
53// // Create synchronization manager with default configuration
54// let mut sync_manager = ClockSynchronizationManager::new(
55//     ClockSynchronizationConfig::default()
56// )?;
57//
58// // Start synchronization
59// sync_manager.start_synchronization()?;
60//
61// // Perform synchronization
62// sync_manager.synchronize()?;
63//
64// // Get synchronization status
65// let status = sync_manager.get_synchronization_status();
66// println!("Sync status: {:?}", status);
67//
68// // Stop synchronization
69// sync_manager.stop_synchronization()?;
70// # Ok(())
71// # }
72// ```
73//
74// # Performance Considerations
75//
76// The clock synchronization system is designed for high-performance operation with:
77// - Minimal latency overhead
78// - Efficient memory usage
79// - Scalable to large TPU clusters
80// - Real-time operation capabilities
81// - Adaptive algorithms for varying network conditions
82
83// Core synchronization components
84pub mod core;
85pub mod protocols;
86pub mod sources;
87
88// Specialized synchronization modules
89pub mod gps;
90pub mod network;
91
92// Monitoring and analysis modules
93pub mod drift;
94pub mod health;
95pub mod quality;
96pub mod statistics;
97
98// Re-export main types from core module
99pub use core::{
100    ClockOffset, ClockSynchronizationConfig, ClockSynchronizationManager,
101    ClockSynchronizationState, ClockSynchronizationStatus, ClockSynchronizer, SynchronizationEvent,
102    SynchronizationResult,
103};
104
105// Re-export protocol types
106pub use protocols::{
107    BerkeleyConfig, ClockSyncProtocol, CristianConfig, CustomProtocolConfig, NtpConfig, NtpPeer,
108    NtpSynchronizer, NtpTimestamps, ProtocolError, ProtocolManager, PtpConfig, PtpSynchronizer,
109    SntpConfig, SntpSynchronizer,
110};
111
112// Re-export source management types
113pub use sources::{
114    AtomicClockType, ClockSource, RadioTimeStation, SourceSelectionAlgorithm,
115    SourceSelectionCriteria, SourceValidation, SystemClockConfig, TimeSource, TimeSourceConfig,
116    TimeSourceManager,
117};
118
119// Re-export GPS synchronization types
120pub use gps::{
121    AntennaConfig, GpsConfig, GpsError, GpsErrorCorrection, GpsReceiverType, GpsSignalProcessing,
122    GpsSynchronizationManager, GpsTime, IonosphericCorrection, SatelliteClockCorrection,
123    TroposphericCorrection,
124};
125
126// Re-export network synchronization types
127pub use network::{
128    LoadBalancingAlgorithm, MessagePassingConfig, MessagePriority, NetworkFaultTolerance,
129    NetworkLoadBalancing, NetworkSyncConfig, NetworkSyncError, NetworkSynchronizationManager,
130    NetworkTopology, SyncMessageType,
131};
132
133// Re-export quality monitoring types
134pub use quality::{
135    ClockAccuracyRequirements, ClockQualityMonitor, QualityAssessment, QualityGrade, QualityMetric,
136    QualityMonitoringConfig, QualityRequirements, QualitySnapshot, QualityThresholds,
137    SourceQualityMonitoring,
138};
139
140// Re-export drift compensation types
141pub use drift::{
142    DriftCompensationAlgorithm, DriftCompensationConfig, DriftCompensationError,
143    DriftCompensationStatus, DriftCompensator, DriftMeasurement, DriftMeasurementConfig,
144    DriftModel, DriftPredictionConfig, DriftPredictionEngine,
145};
146
147// Re-export health monitoring types
148pub use health::{
149    AlertConfiguration, AlertSeverity, HealthAlert, HealthCheck, HealthCheckType,
150    HealthMonitorConfig, HealthMonitorError, HealthStatus, HealthThresholds, RecoveryConfiguration,
151    SourceFailoverConfig, SourceHealthMonitor,
152};
153
154// Re-export statistics and reporting types
155pub use statistics::{
156    ClockStatistics, PerformanceHistory, PerformanceMeasurement, PerformanceReport,
157    PerformanceTracking, QualityReporting, ReliabilityStatistics, ReportGeneration,
158    StatisticsCollector, StatisticsError, TrendDirection,
159};
160
161// Convenience type aliases
162pub type Result<T> = std::result::Result<T, ClockSynchronizationError>;
163pub type Duration = std::time::Duration;
164pub type Instant = std::time::Instant;
165
166/// Main error type for clock synchronization operations
167#[derive(Debug)]
168pub enum ClockSynchronizationError {
169    /// Core synchronization error
170    CoreError(core::ClockSynchronizationError),
171    /// Protocol error
172    ProtocolError(protocols::ProtocolError),
173    /// Source management error
174    SourceError(sources::SourceManagementError),
175    /// GPS synchronization error
176    GpsError(gps::GpsError),
177    /// Network synchronization error
178    NetworkError(network::NetworkSyncError),
179    /// Quality monitoring error
180    QualityError(quality::QualityMonitorError),
181    /// Drift compensation error
182    DriftError(drift::DriftCompensationError),
183    /// Health monitoring error
184    HealthError(health::HealthMonitorError),
185    /// Statistics error
186    StatisticsError(statistics::StatisticsError),
187    /// Configuration error
188    ConfigurationError(String),
189    /// System error
190    SystemError(String),
191}
192
193impl std::fmt::Display for ClockSynchronizationError {
194    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
195        match self {
196            ClockSynchronizationError::CoreError(e) => {
197                write!(f, "Core synchronization error: {}", e)
198            }
199            // No prefix here: `ProtocolError`'s own `Display` already starts
200            // with "Protocol error", and prefixing again rendered
201            // "Protocol error: Protocol error: ...". The inner type stays
202            // self-describing because it is also returned standalone from
203            // `NtpTimestamps::round_trip_delay`/`offset`.
204            ClockSynchronizationError::ProtocolError(e) => write!(f, "{}", e),
205            ClockSynchronizationError::SourceError(e) => {
206                write!(f, "Source management error: {}", e)
207            }
208            ClockSynchronizationError::GpsError(e) => write!(f, "GPS synchronization error: {}", e),
209            ClockSynchronizationError::NetworkError(e) => {
210                write!(f, "Network synchronization error: {}", e)
211            }
212            ClockSynchronizationError::QualityError(e) => {
213                write!(f, "Quality monitoring error: {}", e)
214            }
215            ClockSynchronizationError::DriftError(e) => {
216                write!(f, "Drift compensation error: {}", e)
217            }
218            ClockSynchronizationError::HealthError(e) => {
219                write!(f, "Health monitoring error: {}", e)
220            }
221            ClockSynchronizationError::StatisticsError(e) => write!(f, "Statistics error: {}", e),
222            ClockSynchronizationError::ConfigurationError(msg) => {
223                write!(f, "Configuration error: {}", msg)
224            }
225            ClockSynchronizationError::SystemError(msg) => write!(f, "System error: {}", msg),
226        }
227    }
228}
229
230impl std::error::Error for ClockSynchronizationError {}
231
232// Error conversions for seamless error handling
233impl From<core::ClockSynchronizationError> for ClockSynchronizationError {
234    fn from(err: core::ClockSynchronizationError) -> Self {
235        ClockSynchronizationError::CoreError(err)
236    }
237}
238
239impl From<protocols::ProtocolError> for ClockSynchronizationError {
240    fn from(err: protocols::ProtocolError) -> Self {
241        ClockSynchronizationError::ProtocolError(err)
242    }
243}
244
245impl From<sources::SourceManagementError> for ClockSynchronizationError {
246    fn from(err: sources::SourceManagementError) -> Self {
247        ClockSynchronizationError::SourceError(err)
248    }
249}
250
251impl From<gps::GpsError> for ClockSynchronizationError {
252    fn from(err: gps::GpsError) -> Self {
253        ClockSynchronizationError::GpsError(err)
254    }
255}
256
257impl From<network::NetworkSyncError> for ClockSynchronizationError {
258    fn from(err: network::NetworkSyncError) -> Self {
259        ClockSynchronizationError::NetworkError(err)
260    }
261}
262
263impl From<quality::QualityMonitorError> for ClockSynchronizationError {
264    fn from(err: quality::QualityMonitorError) -> Self {
265        ClockSynchronizationError::QualityError(err)
266    }
267}
268
269impl From<drift::DriftCompensationError> for ClockSynchronizationError {
270    fn from(err: drift::DriftCompensationError) -> Self {
271        ClockSynchronizationError::DriftError(err)
272    }
273}
274
275impl From<health::HealthMonitorError> for ClockSynchronizationError {
276    fn from(err: health::HealthMonitorError) -> Self {
277        ClockSynchronizationError::HealthError(err)
278    }
279}
280
281impl From<statistics::StatisticsError> for ClockSynchronizationError {
282    fn from(err: statistics::StatisticsError) -> Self {
283        ClockSynchronizationError::StatisticsError(err)
284    }
285}
286
287impl From<scirs2_core::CoreError> for ClockSynchronizationError {
288    fn from(err: scirs2_core::CoreError) -> Self {
289        ClockSynchronizationError::SystemError(err.to_string())
290    }
291}
292
293/// Builder for configuring clock synchronization
294///
295/// Provides a fluent interface for configuring the various aspects
296/// of clock synchronization with sensible defaults.
297#[derive(Debug)]
298pub struct ClockSynchronizationBuilder {
299    core_config: Option<core::ClockSynchronizationConfig>,
300    protocol_configs: Vec<protocols::ClockSyncProtocol>,
301    source_configs: Vec<sources::TimeSource>,
302    gps_config: Option<gps::GpsConfig>,
303    network_config: Option<network::NetworkSyncConfig>,
304    quality_config: Option<quality::QualityMonitoringConfig>,
305    drift_config: Option<drift::DriftCompensationConfig>,
306    health_config: Option<health::HealthMonitorConfig>,
307    statistics_config: Option<statistics::StatisticsCollectionConfig>,
308}
309
310impl ClockSynchronizationBuilder {
311    /// Create new builder with default configuration
312    pub fn new() -> Self {
313        Self {
314            core_config: None,
315            protocol_configs: Vec::new(),
316            source_configs: Vec::new(),
317            gps_config: None,
318            network_config: None,
319            quality_config: None,
320            drift_config: None,
321            health_config: None,
322            statistics_config: None,
323        }
324    }
325
326    /// Set core synchronization configuration
327    pub fn with_core_config(mut self, config: core::ClockSynchronizationConfig) -> Self {
328        self.core_config = Some(config);
329        self
330    }
331
332    /// Add synchronization protocol
333    pub fn with_protocol(mut self, protocol: protocols::ClockSyncProtocol) -> Self {
334        self.protocol_configs.push(protocol);
335        self
336    }
337
338    /// Add time source
339    pub fn with_source(mut self, source: sources::TimeSource) -> Self {
340        self.source_configs.push(source);
341        self
342    }
343
344    /// Set GPS configuration
345    pub fn with_gps_config(mut self, config: gps::GpsConfig) -> Self {
346        self.gps_config = Some(config);
347        self
348    }
349
350    /// Set network synchronization configuration
351    pub fn with_network_config(mut self, config: network::NetworkSyncConfig) -> Self {
352        self.network_config = Some(config);
353        self
354    }
355
356    /// Set quality monitoring configuration
357    pub fn with_quality_config(mut self, config: quality::QualityMonitoringConfig) -> Self {
358        self.quality_config = Some(config);
359        self
360    }
361
362    /// Set drift compensation configuration
363    pub fn with_drift_config(mut self, config: drift::DriftCompensationConfig) -> Self {
364        self.drift_config = Some(config);
365        self
366    }
367
368    /// Set health monitoring configuration
369    pub fn with_health_config(mut self, config: health::HealthMonitorConfig) -> Self {
370        self.health_config = Some(config);
371        self
372    }
373
374    /// Set statistics collection configuration
375    pub fn with_statistics_config(
376        mut self,
377        config: statistics::StatisticsCollectionConfig,
378    ) -> Self {
379        self.statistics_config = Some(config);
380        self
381    }
382
383    /// Build the clock synchronization manager
384    pub fn build(self) -> Result<ClockSynchronizationManager> {
385        let core_config = self.core_config.unwrap_or_default();
386
387        // Create and configure the synchronization manager
388        let mut manager = ClockSynchronizationManager::new();
389        manager.config = core_config;
390
391        // Configure protocols
392        for protocol in self.protocol_configs {
393            manager.add_protocol(protocol)?;
394        }
395
396        // Configure sources
397        for source in self.source_configs {
398            manager.add_time_source(source)?;
399        }
400
401        // Apply additional configurations
402        if let Some(gps_config) = self.gps_config {
403            manager.configure_gps(gps_config)?;
404        }
405
406        if let Some(network_config) = self.network_config {
407            manager.configure_network(network_config)?;
408        }
409
410        if let Some(quality_config) = self.quality_config {
411            manager.configure_quality_monitoring(quality_config)?;
412        }
413
414        if let Some(drift_config) = self.drift_config {
415            manager.configure_drift_compensation(drift_config)?;
416        }
417
418        if let Some(health_config) = self.health_config {
419            manager.configure_health_monitoring(health_config)?;
420        }
421
422        if let Some(statistics_config) = self.statistics_config {
423            manager.configure_statistics(statistics_config)?;
424        }
425
426        Ok(manager)
427    }
428}
429
430impl Default for ClockSynchronizationBuilder {
431    fn default() -> Self {
432        Self::new()
433    }
434}
435
436/// Utility functions for clock synchronization
437pub mod utils {
438    use super::*;
439
440    /// Create a basic NTP-based synchronization setup, one time source per
441    /// supplied server.
442    ///
443    /// The server list is the whole point of an NTP setup, so an empty list is
444    /// an honest configuration error rather than a manager with nothing to
445    /// synchronize against. (This used to loop over
446    /// `builder.source_configs.len()` -- which is zero on a fresh builder --
447    /// and so added no sources at all while ignoring `ntp_servers` entirely.)
448    pub fn create_ntp_sync_manager(
449        ntp_servers: Vec<String>,
450    ) -> Result<ClockSynchronizationManager> {
451        if ntp_servers.is_empty() {
452            return Err(ClockSynchronizationError::ConfigurationError(
453                "an NTP synchronization manager needs at least one server address".to_string(),
454            ));
455        }
456
457        let mut builder = ClockSynchronizationBuilder::new();
458
459        // Add NTP protocol
460        builder = builder.with_protocol(protocols::ClockSyncProtocol::NTP);
461
462        // One addressable network time source per configured server.
463        for server in ntp_servers {
464            builder = builder.with_source(sources::TimeSource {
465                source_type: sources::ClockSource::NTP,
466                address: Some(server),
467            });
468        }
469
470        // Enable basic monitoring
471        builder = builder.with_quality_config(quality::QualityMonitoringConfig::default());
472        builder = builder.with_health_config(health::HealthMonitorConfig::default());
473
474        builder.build()
475    }
476
477    /// Create a GPS-based synchronization setup
478    pub fn create_gps_sync_manager(
479        gps_config: gps::GpsConfig,
480    ) -> Result<ClockSynchronizationManager> {
481        let mut builder = ClockSynchronizationBuilder::new();
482
483        // Add GPS configuration
484        builder = builder.with_gps_config(gps_config.clone());
485
486        // Add GPS time source. The receiver's device path comes from the GPS
487        // configuration rather than being invented here.
488        let source = sources::TimeSource {
489            source_type: sources::ClockSource::GPS,
490            address: None,
491        };
492        builder = builder.with_source(source);
493
494        // Enable comprehensive monitoring for GPS
495        builder = builder.with_quality_config(quality::QualityMonitoringConfig::default());
496        builder = builder.with_drift_config(drift::DriftCompensationConfig::default());
497        builder = builder.with_health_config(health::HealthMonitorConfig::default());
498
499        builder.build()
500    }
501
502    /// Create a high-precision synchronization setup
503    pub fn create_precision_sync_manager() -> Result<ClockSynchronizationManager> {
504        let mut builder = ClockSynchronizationBuilder::new();
505
506        // Use PTP for high precision
507        builder = builder.with_protocol(protocols::ClockSyncProtocol::PTP);
508
509        // Add atomic clock source. A locally attached reference needs no
510        // network address.
511        let source = sources::TimeSource {
512            source_type: sources::ClockSource::Atomic,
513            address: None,
514        };
515        builder = builder.with_source(source);
516
517        // Enable all monitoring and compensation
518        builder = builder.with_quality_config(quality::QualityMonitoringConfig::default());
519        builder = builder.with_drift_config(drift::DriftCompensationConfig::default());
520        builder = builder.with_health_config(health::HealthMonitorConfig::default());
521        builder = builder.with_statistics_config(statistics::StatisticsCollectionConfig::default());
522
523        builder.build()
524    }
525
526    /// Convert duration to human-readable string
527    pub fn duration_to_string(duration: Duration) -> String {
528        let total_seconds = duration.as_secs();
529        let days = total_seconds / 86400;
530        let hours = (total_seconds % 86400) / 3600;
531        let minutes = (total_seconds % 3600) / 60;
532        let seconds = total_seconds % 60;
533        let millis = duration.subsec_millis();
534        let micros = duration.subsec_micros() % 1000;
535        let nanos = duration.subsec_nanos() % 1000;
536
537        if days > 0 {
538            format!("{}d {}h {}m {}s", days, hours, minutes, seconds)
539        } else if hours > 0 {
540            format!("{}h {}m {}s", hours, minutes, seconds)
541        } else if minutes > 0 {
542            format!("{}m {}s", minutes, seconds)
543        } else if seconds > 0 {
544            format!("{}.{:03}s", seconds, millis)
545        } else if millis > 0 {
546            format!("{}.{:03}ms", millis, micros)
547        } else if micros > 0 {
548            format!("{}.{:03}μs", micros, nanos)
549        } else {
550            format!("{}ns", nanos)
551        }
552    }
553
554    /// Validate clock offset against requirements
555    pub fn validate_clock_offset(
556        offset: ClockOffset,
557        requirements: &quality::ClockAccuracyRequirements,
558    ) -> bool {
559        offset.offset_ns.abs() as f64 <= requirements.max_drift_ppm
560    }
561
562    /// Calculate quality score from multiple metrics
563    pub fn calculate_quality_score(metrics: &std::collections::HashMap<String, f64>) -> f64 {
564        if metrics.is_empty() {
565            return 0.0;
566        }
567
568        let sum: f64 = metrics.values().sum();
569        sum / metrics.len() as f64
570    }
571
572    /// Wall-clock time elapsed since this process-uptime tracker was first
573    /// consulted, as a real, live measurement.
574    ///
575    /// There is no portable, pure-Rust way (no FFI, no platform-specific
576    /// `/proc`/`sysctl` parsing) to ask the OS for the true process start
577    /// time on every platform this crate targets. Rather than fabricate a
578    /// plausible-looking constant, this establishes a monotonic anchor the
579    /// first time it is called (via [`std::sync::OnceLock`]) and returns
580    /// [`Instant::elapsed`] against that anchor on every call thereafter
581    /// (including the first, which returns a value very close to zero).
582    ///
583    /// Renamed from the former `get_system_uptime`: that name promised the
584    /// OS-level system uptime, which this never measured (it always
585    /// returned a hardcoded `Duration::from_secs(86400)`). `process_uptime`
586    /// accurately describes what a pure-Rust anchor-based measurement can
587    /// honestly provide.
588    pub fn process_uptime() -> Duration {
589        static PROCESS_START: std::sync::OnceLock<Instant> = std::sync::OnceLock::new();
590        let start = *PROCESS_START.get_or_init(Instant::now);
591        start.elapsed()
592    }
593}
594
595/// Prelude module for common imports
596pub mod prelude {}
597
598// Module-level documentation tests
599#[cfg(test)]
600mod tests {
601    use super::*;
602
603    #[test]
604    fn test_builder_pattern() {
605        let builder =
606            ClockSynchronizationBuilder::new().with_protocol(protocols::ClockSyncProtocol::NTP);
607
608        // Builder should be constructible
609        assert!(builder.protocol_configs.len() == 1);
610    }
611
612    #[test]
613    fn test_error_conversions() {
614        let core_error = core::ClockSynchronizationError;
615        let sync_error: ClockSynchronizationError = core_error.into();
616
617        // The conversion should work
618        match sync_error {
619            ClockSynchronizationError::CoreError(_) => {}
620            _ => panic!("Error conversion failed"),
621        }
622    }
623
624    #[test]
625    fn test_utility_functions() {
626        // Test duration formatting
627        let duration = Duration::from_millis(1500);
628        let formatted = utils::duration_to_string(duration);
629        assert!(formatted.contains("s"));
630
631        // Test quality score calculation
632        let mut metrics = std::collections::HashMap::new();
633        metrics.insert("accuracy".to_string(), 0.9);
634        metrics.insert("stability".to_string(), 0.8);
635        let score = utils::calculate_quality_score(&metrics);
636        assert!((score - 0.85).abs() < 1e-10);
637    }
638
639    // Regression test: `process_uptime` (formerly `get_system_uptime`) used
640    // to always return a hardcoded `Duration::from_secs(86400)`. A fake
641    // constant would pass a naive "returns a Duration" check but can never
642    // reflect real elapsed time; this asserts it strictly increases with
643    // real wall-clock time and never equals the old fabricated value.
644    #[test]
645    fn process_uptime_reflects_real_elapsed_time_not_a_fixed_constant() {
646        let first = utils::process_uptime();
647        std::thread::sleep(Duration::from_millis(30));
648        let second = utils::process_uptime();
649
650        assert!(
651            second > first,
652            "process_uptime must strictly increase with real elapsed time \
653             (first={first:?}, second={second:?})"
654        );
655        assert!(
656            second - first >= Duration::from_millis(20),
657            "process_uptime delta should reflect the real 30ms sleep, got {:?}",
658            second - first
659        );
660        assert_ne!(
661            second,
662            Duration::from_secs(86400),
663            "must not be the old hardcoded fabricated constant"
664        );
665    }
666}