use std::sync::Arc;
use std::time::{Duration, Instant};
#[derive(Debug)]
pub struct PhaseTimer {
phase: &'static str,
start: Instant,
}
impl PhaseTimer {
pub fn new(phase: &'static str) -> Self {
Self {
phase,
start: Instant::now(),
}
}
pub fn elapsed(&self) -> Duration {
self.start.elapsed()
}
pub fn phase(&self) -> &'static str {
self.phase
}
}
pub trait SyncMetricsCollector: Send + Sync {
fn record_message_sent(&self, protocol: &str, bytes: usize);
fn record_round_trip(&self, protocol: &str);
fn record_entities_transferred(&self, count: usize);
fn record_merge(&self, crdt_type: &str);
fn record_comparison(&self);
fn start_phase(&self, phase: &'static str) -> PhaseTimer {
PhaseTimer::new(phase)
}
fn record_phase_complete(&self, timer: PhaseTimer);
fn record_snapshot_blocked(&self);
fn record_verification_failure(&self);
fn record_lww_fallback(&self);
fn record_buffer_drop(&self);
fn record_sync_start(&self, context_id: &str, protocol: &str, trigger: &str);
fn record_sync_complete(
&self,
context_id: &str,
protocol: &str,
duration: Duration,
entities: usize,
);
fn record_sync_failure(&self, context_id: &str, protocol: &str, reason: &str);
fn record_protocol_selected(&self, protocol: &str, reason: &str, divergence: f64);
}
#[derive(Debug, Default, Clone, Copy)]
pub struct NoOpMetrics;
impl SyncMetricsCollector for NoOpMetrics {
#[inline]
fn record_message_sent(&self, _protocol: &str, _bytes: usize) {}
#[inline]
fn record_round_trip(&self, _protocol: &str) {}
#[inline]
fn record_entities_transferred(&self, _count: usize) {}
#[inline]
fn record_merge(&self, _crdt_type: &str) {}
#[inline]
fn record_comparison(&self) {}
#[inline]
fn record_phase_complete(&self, _timer: PhaseTimer) {}
#[inline]
fn record_snapshot_blocked(&self) {}
#[inline]
fn record_verification_failure(&self) {}
#[inline]
fn record_lww_fallback(&self) {}
#[inline]
fn record_buffer_drop(&self) {}
#[inline]
fn record_sync_start(&self, _context_id: &str, _protocol: &str, _trigger: &str) {}
#[inline]
fn record_sync_complete(
&self,
_context_id: &str,
_protocol: &str,
_duration: Duration,
_entities: usize,
) {
}
#[inline]
fn record_sync_failure(&self, _context_id: &str, _protocol: &str, _reason: &str) {}
#[inline]
fn record_protocol_selected(&self, _protocol: &str, _reason: &str, _divergence: f64) {}
}
pub type SharedMetrics = Arc<dyn SyncMetricsCollector>;
pub fn no_op_metrics() -> Arc<NoOpMetrics> {
Arc::new(NoOpMetrics)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_phase_timer() {
let timer = PhaseTimer::new("test_phase");
assert_eq!(timer.phase(), "test_phase");
std::thread::sleep(std::time::Duration::from_millis(1));
assert!(timer.elapsed() >= std::time::Duration::from_millis(1));
}
#[test]
fn test_no_op_metrics_is_send_sync() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<NoOpMetrics>();
}
#[test]
fn test_no_op_metrics_all_methods() {
let metrics = NoOpMetrics;
metrics.record_message_sent("test", 100);
metrics.record_round_trip("test");
metrics.record_entities_transferred(10);
metrics.record_merge("GCounter");
metrics.record_comparison();
let timer = metrics.start_phase("test");
metrics.record_phase_complete(timer);
metrics.record_snapshot_blocked();
metrics.record_verification_failure();
metrics.record_lww_fallback();
metrics.record_buffer_drop();
metrics.record_sync_start("ctx-123", "HashComparison", "timer");
metrics.record_sync_complete("ctx-123", "HashComparison", Duration::from_secs(1), 50);
metrics.record_sync_failure("ctx-123", "HashComparison", "timeout");
metrics.record_protocol_selected("HashComparison", "divergence < 10%", 0.05);
}
}