use crate::NonceError;
use async_trait::async_trait;
use std::sync::Arc;
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::{Duration, Instant};
#[derive(Debug, Clone, Default)]
pub struct NonceMetrics {
pub nonces_generated: u64,
pub verification_attempts: u64,
pub verification_successes: u64,
pub verification_failures: u64,
pub storage_operations: u64,
pub cleanup_operations: u64,
pub error_counts: ErrorMetrics,
pub performance: PerformanceMetrics,
}
#[derive(Debug, Clone, Default)]
pub struct ErrorMetrics {
pub duplicate_nonce: u64,
pub timestamp_out_of_window: u64,
pub invalid_signature: u64,
pub storage_errors: u64,
pub crypto_errors: u64,
pub other_errors: u64,
}
#[derive(Debug, Clone, Default)]
pub struct PerformanceMetrics {
pub avg_generation_time_us: u64,
pub avg_verification_time_us: u64,
pub avg_storage_time_us: u64,
pub sample_count: u64,
}
#[derive(Debug, Clone)]
pub enum MetricEvent {
NonceGenerated {
duration: Duration,
context: Option<String>,
},
VerificationAttempt {
duration: Duration,
success: bool,
context: Option<String>,
},
StorageOperation {
operation: String,
duration: Duration,
success: bool,
},
CleanupOperation {
items_cleaned: usize,
duration: Duration,
},
Error {
error_code: &'static str,
error_message: String,
context: Option<String>,
},
}
#[async_trait]
pub trait MetricsCollector: Send + Sync {
async fn record_event(&self, event: MetricEvent);
async fn get_metrics(&self) -> Result<NonceMetrics, NonceError>;
async fn reset_metrics(&self) -> Result<(), NonceError>;
async fn flush(&self) -> Result<(), NonceError> {
Ok(())
}
}
#[derive(Debug)]
pub struct InMemoryMetricsCollector {
nonces_generated: AtomicU64,
verification_attempts: AtomicU64,
verification_successes: AtomicU64,
verification_failures: AtomicU64,
storage_operations: AtomicU64,
cleanup_operations: AtomicU64,
duplicate_nonce_errors: AtomicU64,
timestamp_out_of_window_errors: AtomicU64,
invalid_signature_errors: AtomicU64,
storage_errors: AtomicU64,
crypto_errors: AtomicU64,
other_errors: AtomicU64,
generation_time_total: AtomicU64,
verification_time_total: AtomicU64,
storage_time_total: AtomicU64,
generation_samples: AtomicU64,
verification_samples: AtomicU64,
storage_samples: AtomicU64,
}
impl InMemoryMetricsCollector {
pub fn new() -> Self {
Self {
nonces_generated: AtomicU64::new(0),
verification_attempts: AtomicU64::new(0),
verification_successes: AtomicU64::new(0),
verification_failures: AtomicU64::new(0),
storage_operations: AtomicU64::new(0),
cleanup_operations: AtomicU64::new(0),
duplicate_nonce_errors: AtomicU64::new(0),
timestamp_out_of_window_errors: AtomicU64::new(0),
invalid_signature_errors: AtomicU64::new(0),
storage_errors: AtomicU64::new(0),
crypto_errors: AtomicU64::new(0),
other_errors: AtomicU64::new(0),
generation_time_total: AtomicU64::new(0),
verification_time_total: AtomicU64::new(0),
storage_time_total: AtomicU64::new(0),
generation_samples: AtomicU64::new(0),
verification_samples: AtomicU64::new(0),
storage_samples: AtomicU64::new(0),
}
}
}
impl Default for InMemoryMetricsCollector {
fn default() -> Self {
Self::new()
}
}
#[async_trait]
impl MetricsCollector for InMemoryMetricsCollector {
async fn record_event(&self, event: MetricEvent) {
match event {
MetricEvent::NonceGenerated { duration, .. } => {
self.nonces_generated.fetch_add(1, Ordering::Relaxed);
self.generation_time_total
.fetch_add(duration.as_micros() as u64, Ordering::Relaxed);
self.generation_samples.fetch_add(1, Ordering::Relaxed);
}
MetricEvent::VerificationAttempt {
duration, success, ..
} => {
self.verification_attempts.fetch_add(1, Ordering::Relaxed);
if success {
self.verification_successes.fetch_add(1, Ordering::Relaxed);
} else {
self.verification_failures.fetch_add(1, Ordering::Relaxed);
}
self.verification_time_total
.fetch_add(duration.as_micros() as u64, Ordering::Relaxed);
self.verification_samples.fetch_add(1, Ordering::Relaxed);
}
MetricEvent::StorageOperation {
duration, success, ..
} => {
if success {
self.storage_operations.fetch_add(1, Ordering::Relaxed);
}
self.storage_time_total
.fetch_add(duration.as_micros() as u64, Ordering::Relaxed);
self.storage_samples.fetch_add(1, Ordering::Relaxed);
}
MetricEvent::CleanupOperation { .. } => {
self.cleanup_operations.fetch_add(1, Ordering::Relaxed);
}
MetricEvent::Error { error_code, .. } => match error_code {
"duplicate_nonce" => {
self.duplicate_nonce_errors.fetch_add(1, Ordering::Relaxed);
}
"timestamp_out_of_window" => {
self.timestamp_out_of_window_errors
.fetch_add(1, Ordering::Relaxed);
}
"invalid_signature" => {
self.invalid_signature_errors
.fetch_add(1, Ordering::Relaxed);
}
"storage_error" => {
self.storage_errors.fetch_add(1, Ordering::Relaxed);
}
"crypto_error" => {
self.crypto_errors.fetch_add(1, Ordering::Relaxed);
}
_ => {
self.other_errors.fetch_add(1, Ordering::Relaxed);
}
},
}
}
async fn get_metrics(&self) -> Result<NonceMetrics, NonceError> {
let generation_samples = self.generation_samples.load(Ordering::Relaxed);
let verification_samples = self.verification_samples.load(Ordering::Relaxed);
let storage_samples = self.storage_samples.load(Ordering::Relaxed);
Ok(NonceMetrics {
nonces_generated: self.nonces_generated.load(Ordering::Relaxed),
verification_attempts: self.verification_attempts.load(Ordering::Relaxed),
verification_successes: self.verification_successes.load(Ordering::Relaxed),
verification_failures: self.verification_failures.load(Ordering::Relaxed),
storage_operations: self.storage_operations.load(Ordering::Relaxed),
cleanup_operations: self.cleanup_operations.load(Ordering::Relaxed),
error_counts: ErrorMetrics {
duplicate_nonce: self.duplicate_nonce_errors.load(Ordering::Relaxed),
timestamp_out_of_window: self
.timestamp_out_of_window_errors
.load(Ordering::Relaxed),
invalid_signature: self.invalid_signature_errors.load(Ordering::Relaxed),
storage_errors: self.storage_errors.load(Ordering::Relaxed),
crypto_errors: self.crypto_errors.load(Ordering::Relaxed),
other_errors: self.other_errors.load(Ordering::Relaxed),
},
performance: PerformanceMetrics {
avg_generation_time_us: if generation_samples > 0 {
self.generation_time_total.load(Ordering::Relaxed) / generation_samples
} else {
0
},
avg_verification_time_us: if verification_samples > 0 {
self.verification_time_total.load(Ordering::Relaxed) / verification_samples
} else {
0
},
avg_storage_time_us: if storage_samples > 0 {
self.storage_time_total.load(Ordering::Relaxed) / storage_samples
} else {
0
},
sample_count: generation_samples + verification_samples + storage_samples,
},
})
}
async fn reset_metrics(&self) -> Result<(), NonceError> {
self.nonces_generated.store(0, Ordering::Relaxed);
self.verification_attempts.store(0, Ordering::Relaxed);
self.verification_successes.store(0, Ordering::Relaxed);
self.verification_failures.store(0, Ordering::Relaxed);
self.storage_operations.store(0, Ordering::Relaxed);
self.cleanup_operations.store(0, Ordering::Relaxed);
self.duplicate_nonce_errors.store(0, Ordering::Relaxed);
self.timestamp_out_of_window_errors
.store(0, Ordering::Relaxed);
self.invalid_signature_errors.store(0, Ordering::Relaxed);
self.storage_errors.store(0, Ordering::Relaxed);
self.crypto_errors.store(0, Ordering::Relaxed);
self.other_errors.store(0, Ordering::Relaxed);
self.generation_time_total.store(0, Ordering::Relaxed);
self.verification_time_total.store(0, Ordering::Relaxed);
self.storage_time_total.store(0, Ordering::Relaxed);
self.generation_samples.store(0, Ordering::Relaxed);
self.verification_samples.store(0, Ordering::Relaxed);
self.storage_samples.store(0, Ordering::Relaxed);
Ok(())
}
}
#[derive(Debug, Default)]
pub struct NoOpMetricsCollector;
impl NoOpMetricsCollector {
pub fn new() -> Self {
Self
}
}
#[async_trait]
impl MetricsCollector for NoOpMetricsCollector {
async fn record_event(&self, _event: MetricEvent) {
}
async fn get_metrics(&self) -> Result<NonceMetrics, NonceError> {
Ok(NonceMetrics::default())
}
async fn reset_metrics(&self) -> Result<(), NonceError> {
Ok(())
}
}
pub struct MetricsTimer {
collector: Arc<dyn MetricsCollector>,
start_time: Instant,
}
impl MetricsTimer {
pub fn new(collector: Arc<dyn MetricsCollector>) -> Self {
Self {
collector,
start_time: Instant::now(),
}
}
pub fn elapsed(&self) -> Duration {
self.start_time.elapsed()
}
pub fn reset(&mut self) {
self.start_time = Instant::now();
}
pub async fn record(&self, event: MetricEvent) {
self.collector.record_event(event).await;
}
pub async fn time_async<F, T, E>(&mut self, operation: F) -> Result<T, E>
where
F: std::future::Future<Output = Result<T, E>>,
{
self.reset();
operation.await
}
}
#[cfg(test)]
mod tests {
use super::*;
use tokio::time::{Duration as TokioDuration, sleep};
#[tokio::test]
async fn test_in_memory_metrics_collector() -> Result<(), NonceError> {
let collector = InMemoryMetricsCollector::new();
collector
.record_event(MetricEvent::NonceGenerated {
duration: Duration::from_micros(100),
context: None,
})
.await;
collector
.record_event(MetricEvent::VerificationAttempt {
duration: Duration::from_micros(200),
success: true,
context: None,
})
.await;
collector
.record_event(MetricEvent::VerificationAttempt {
duration: Duration::from_micros(150),
success: false,
context: None,
})
.await;
collector
.record_event(MetricEvent::Error {
error_code: "duplicate_nonce",
error_message: "Duplicate nonce error".to_string(),
context: None,
})
.await;
let metrics = collector.get_metrics().await?;
assert_eq!(metrics.nonces_generated, 1);
assert_eq!(metrics.verification_attempts, 2);
assert_eq!(metrics.verification_successes, 1);
assert_eq!(metrics.verification_failures, 1);
assert_eq!(metrics.error_counts.duplicate_nonce, 1);
assert_eq!(metrics.performance.avg_generation_time_us, 100);
assert_eq!(metrics.performance.avg_verification_time_us, 175);
collector.reset_metrics().await?;
let reset_metrics = collector.get_metrics().await?;
assert_eq!(reset_metrics.nonces_generated, 0);
assert_eq!(reset_metrics.verification_attempts, 0);
Ok(())
}
#[tokio::test]
async fn test_no_op_metrics_collector() -> Result<(), NonceError> {
let collector = NoOpMetricsCollector::new();
collector
.record_event(MetricEvent::NonceGenerated {
duration: Duration::from_micros(100),
context: None,
})
.await;
let metrics = collector.get_metrics().await?;
assert_eq!(metrics.nonces_generated, 0);
assert_eq!(metrics.verification_attempts, 0);
Ok(())
}
#[tokio::test]
async fn test_metrics_timer() -> Result<(), NonceError> {
let collector = Arc::new(InMemoryMetricsCollector::new());
let mut timer = MetricsTimer::new(Arc::clone(&collector) as Arc<dyn MetricsCollector>);
let result = timer
.time_async(async {
sleep(TokioDuration::from_millis(10)).await;
Ok::<i32, NonceError>(42)
})
.await?;
assert_eq!(result, 42);
assert!(timer.elapsed() >= Duration::from_millis(10));
timer
.record(MetricEvent::NonceGenerated {
duration: timer.elapsed(),
context: None,
})
.await;
let metrics = collector.get_metrics().await?;
assert_eq!(metrics.nonces_generated, 1);
assert!(metrics.performance.avg_generation_time_us >= 10000);
Ok(())
}
#[tokio::test]
async fn test_error_categorization() -> Result<(), NonceError> {
let collector = InMemoryMetricsCollector::new();
let error_codes = vec![
"duplicate_nonce",
"timestamp_out_of_window",
"invalid_signature",
"storage_error",
"crypto_error",
"invalid_input",
];
for error_code in error_codes {
collector
.record_event(MetricEvent::Error {
error_code,
error_message: format!("Test error: {error_code}"),
context: None,
})
.await;
}
let metrics = collector.get_metrics().await?;
assert_eq!(metrics.error_counts.duplicate_nonce, 1);
assert_eq!(metrics.error_counts.timestamp_out_of_window, 1);
assert_eq!(metrics.error_counts.invalid_signature, 1);
assert_eq!(metrics.error_counts.storage_errors, 1);
assert_eq!(metrics.error_counts.crypto_errors, 1);
assert_eq!(metrics.error_counts.other_errors, 1);
Ok(())
}
#[tokio::test]
async fn test_concurrent_metrics_collection() -> Result<(), NonceError> {
let collector = Arc::new(InMemoryMetricsCollector::new());
let mut handles = vec![];
for i in 0..100 {
let collector_clone = Arc::clone(&collector);
let handle = tokio::spawn(async move {
collector_clone
.record_event(MetricEvent::NonceGenerated {
duration: Duration::from_micros(i),
context: None,
})
.await;
});
handles.push(handle);
}
for handle in handles {
handle.await.unwrap();
}
let metrics = collector.get_metrics().await?;
assert_eq!(metrics.nonces_generated, 100);
assert!(metrics.performance.sample_count >= 100);
Ok(())
}
#[tokio::test]
async fn test_performance_averages_calculation() -> Result<(), NonceError> {
let collector = InMemoryMetricsCollector::new();
collector
.record_event(MetricEvent::NonceGenerated {
duration: Duration::from_micros(100),
context: None,
})
.await;
collector
.record_event(MetricEvent::NonceGenerated {
duration: Duration::from_micros(200),
context: None,
})
.await;
collector
.record_event(MetricEvent::NonceGenerated {
duration: Duration::from_micros(300),
context: None,
})
.await;
collector
.record_event(MetricEvent::VerificationAttempt {
duration: Duration::from_micros(500),
success: true,
context: None,
})
.await;
collector
.record_event(MetricEvent::VerificationAttempt {
duration: Duration::from_micros(700),
success: false,
context: None,
})
.await;
collector
.record_event(MetricEvent::StorageOperation {
operation: "set".to_string(),
duration: Duration::from_micros(150),
success: true,
})
.await;
collector
.record_event(MetricEvent::StorageOperation {
operation: "get".to_string(),
duration: Duration::from_micros(50),
success: true,
})
.await;
let metrics = collector.get_metrics().await?;
assert_eq!(metrics.performance.avg_generation_time_us, 200); assert_eq!(metrics.performance.avg_verification_time_us, 600); assert_eq!(metrics.performance.avg_storage_time_us, 100); assert_eq!(metrics.performance.sample_count, 7);
Ok(())
}
#[tokio::test]
async fn test_storage_operation_success_tracking() -> Result<(), NonceError> {
let collector = InMemoryMetricsCollector::new();
collector
.record_event(MetricEvent::StorageOperation {
operation: "set".to_string(),
duration: Duration::from_micros(100),
success: true,
})
.await;
collector
.record_event(MetricEvent::StorageOperation {
operation: "get".to_string(),
duration: Duration::from_micros(50),
success: true,
})
.await;
collector
.record_event(MetricEvent::StorageOperation {
operation: "cleanup".to_string(),
duration: Duration::from_micros(200),
success: false,
})
.await;
let metrics = collector.get_metrics().await?;
assert_eq!(metrics.storage_operations, 2);
assert_eq!(metrics.performance.avg_storage_time_us, 116); assert_eq!(metrics.performance.sample_count, 3);
Ok(())
}
#[tokio::test]
async fn test_cleanup_operations_tracking() -> Result<(), NonceError> {
let collector = InMemoryMetricsCollector::new();
collector
.record_event(MetricEvent::CleanupOperation {
items_cleaned: 100,
duration: Duration::from_millis(5),
})
.await;
collector
.record_event(MetricEvent::CleanupOperation {
items_cleaned: 50,
duration: Duration::from_millis(2),
})
.await;
let metrics = collector.get_metrics().await?;
assert_eq!(metrics.cleanup_operations, 2);
Ok(())
}
#[tokio::test]
async fn test_metrics_reset_comprehensive() -> Result<(), NonceError> {
let collector = InMemoryMetricsCollector::new();
collector
.record_event(MetricEvent::NonceGenerated {
duration: Duration::from_micros(100),
context: Some("test".to_string()),
})
.await;
collector
.record_event(MetricEvent::VerificationAttempt {
duration: Duration::from_micros(200),
success: true,
context: None,
})
.await;
collector
.record_event(MetricEvent::StorageOperation {
operation: "set".to_string(),
duration: Duration::from_micros(50),
success: true,
})
.await;
collector
.record_event(MetricEvent::CleanupOperation {
items_cleaned: 10,
duration: Duration::from_millis(1),
})
.await;
collector
.record_event(MetricEvent::Error {
error_code: "invalid_signature",
error_message: "Test error".to_string(),
context: None,
})
.await;
let before_reset = collector.get_metrics().await?;
assert!(before_reset.nonces_generated > 0);
assert!(before_reset.verification_attempts > 0);
assert!(before_reset.storage_operations > 0);
assert!(before_reset.cleanup_operations > 0);
assert!(before_reset.error_counts.invalid_signature > 0);
assert!(before_reset.performance.sample_count > 0);
collector.reset_metrics().await?;
let after_reset = collector.get_metrics().await?;
assert_eq!(after_reset.nonces_generated, 0);
assert_eq!(after_reset.verification_attempts, 0);
assert_eq!(after_reset.verification_successes, 0);
assert_eq!(after_reset.verification_failures, 0);
assert_eq!(after_reset.storage_operations, 0);
assert_eq!(after_reset.cleanup_operations, 0);
assert_eq!(after_reset.error_counts.duplicate_nonce, 0);
assert_eq!(after_reset.error_counts.timestamp_out_of_window, 0);
assert_eq!(after_reset.error_counts.invalid_signature, 0);
assert_eq!(after_reset.error_counts.storage_errors, 0);
assert_eq!(after_reset.error_counts.crypto_errors, 0);
assert_eq!(after_reset.error_counts.other_errors, 0);
assert_eq!(after_reset.performance.avg_generation_time_us, 0);
assert_eq!(after_reset.performance.avg_verification_time_us, 0);
assert_eq!(after_reset.performance.avg_storage_time_us, 0);
assert_eq!(after_reset.performance.sample_count, 0);
Ok(())
}
#[tokio::test]
async fn test_metrics_timer_reset_and_reuse() -> Result<(), NonceError> {
let collector = Arc::new(InMemoryMetricsCollector::new());
let mut timer = MetricsTimer::new(Arc::clone(&collector) as Arc<dyn MetricsCollector>);
let result1 = timer
.time_async(async {
sleep(TokioDuration::from_millis(10)).await;
Ok::<i32, NonceError>(1)
})
.await?;
let first_elapsed = timer.elapsed();
timer
.record(MetricEvent::NonceGenerated {
duration: first_elapsed,
context: None,
})
.await;
timer.reset();
let result2 = timer
.time_async(async {
sleep(TokioDuration::from_millis(5)).await;
Ok::<i32, NonceError>(2)
})
.await?;
let second_elapsed = timer.elapsed();
timer
.record(MetricEvent::VerificationAttempt {
duration: second_elapsed,
success: true,
context: None,
})
.await;
assert_eq!(result1, 1);
assert_eq!(result2, 2);
assert!(first_elapsed >= Duration::from_millis(10));
assert!(second_elapsed >= Duration::from_millis(5));
assert!(second_elapsed < first_elapsed);
let metrics = collector.get_metrics().await?;
assert_eq!(metrics.nonces_generated, 1);
assert_eq!(metrics.verification_attempts, 1);
Ok(())
}
#[tokio::test]
async fn test_edge_cases_zero_samples() -> Result<(), NonceError> {
let collector = InMemoryMetricsCollector::new();
let metrics = collector.get_metrics().await?;
assert_eq!(metrics.performance.avg_generation_time_us, 0);
assert_eq!(metrics.performance.avg_verification_time_us, 0);
assert_eq!(metrics.performance.avg_storage_time_us, 0);
assert_eq!(metrics.performance.sample_count, 0);
Ok(())
}
#[tokio::test]
async fn test_context_information_preservation() -> Result<(), NonceError> {
let collector = InMemoryMetricsCollector::new();
collector
.record_event(MetricEvent::NonceGenerated {
duration: Duration::from_micros(100),
context: Some("user123".to_string()),
})
.await;
collector
.record_event(MetricEvent::VerificationAttempt {
duration: Duration::from_micros(200),
success: true,
context: Some("api_key_auth".to_string()),
})
.await;
collector
.record_event(MetricEvent::Error {
error_code: "duplicate_nonce",
error_message: "Nonce already used".to_string(),
context: Some("mobile_app".to_string()),
})
.await;
let metrics = collector.get_metrics().await?;
assert_eq!(metrics.nonces_generated, 1);
assert_eq!(metrics.verification_attempts, 1);
assert_eq!(metrics.verification_successes, 1);
assert_eq!(metrics.error_counts.duplicate_nonce, 1);
Ok(())
}
}