quantrs2_anneal/comprehensive_integration_testing/
monitoring.rs1use std::collections::HashMap;
4use std::time::{Duration, SystemTime};
5
6pub struct TestPerformanceMonitor {
8 pub metrics: TestPerformanceMetrics,
10 pub benchmarks: HashMap<String, BenchmarkComparison>,
12 pub trends: PerformanceTrends,
14 pub alert_system: PerformanceAlertSystem,
16}
17
18impl TestPerformanceMonitor {
19 #[must_use]
20 pub fn new() -> Self {
21 Self {
22 metrics: TestPerformanceMetrics::default(),
23 benchmarks: HashMap::new(),
24 trends: PerformanceTrends::default(),
25 alert_system: PerformanceAlertSystem::new(),
26 }
27 }
28
29 pub fn record_execution_time(&mut self, duration: Duration) {
31 self.metrics.execution_time_distribution.push(duration);
32 self.trends
33 .execution_time_trend
34 .push((SystemTime::now(), duration));
35
36 if !self.metrics.execution_time_distribution.is_empty() {
38 let sum: Duration = self.metrics.execution_time_distribution.iter().sum();
39 self.metrics.avg_execution_time =
40 sum / self.metrics.execution_time_distribution.len() as u32;
41 }
42
43 if self.metrics.execution_time_distribution.len() > 1000 {
45 self.metrics.execution_time_distribution.drain(0..1);
46 }
47 if self.trends.execution_time_trend.len() > 1000 {
48 self.trends.execution_time_trend.drain(0..1);
49 }
50 }
51
52 pub fn update_success_rate(&mut self, success: bool) {
54 let current_count = self.metrics.execution_time_distribution.len();
55 if current_count == 0 {
56 self.metrics.success_rate = if success { 1.0 } else { 0.0 };
57 } else {
58 let current_successes = (self.metrics.success_rate * current_count as f64) as usize;
59 let new_successes = if success {
60 current_successes + 1
61 } else {
62 current_successes
63 };
64 self.metrics.success_rate = new_successes as f64 / (current_count + 1) as f64;
65 }
66
67 self.trends
68 .success_rate_trend
69 .push((SystemTime::now(), self.metrics.success_rate));
70
71 if self.trends.success_rate_trend.len() > 1000 {
73 self.trends.success_rate_trend.drain(0..1);
74 }
75 }
76
77 pub fn set_benchmark(&mut self, name: String, baseline: PerformanceBaseline) {
79 let comparison = BenchmarkComparison {
80 baseline,
81 current: self.metrics.clone(),
82 delta: PerformanceDelta {
83 execution_time_change: 0.0,
84 success_rate_change: 0.0,
85 resource_usage_change: 0.0,
86 overall_change: 0.0,
87 },
88 timestamp: SystemTime::now(),
89 };
90 self.benchmarks.insert(name, comparison);
91 }
92
93 #[must_use]
95 pub const fn get_metrics(&self) -> &TestPerformanceMetrics {
96 &self.metrics
97 }
98
99 #[must_use]
101 pub const fn get_trends(&self) -> &PerformanceTrends {
102 &self.trends
103 }
104
105 pub fn analyze_trends(&mut self) {
107 let window_size = 10;
109
110 if self.trends.execution_time_trend.len() >= window_size {
112 let recent: Vec<_> = self
113 .trends
114 .execution_time_trend
115 .iter()
116 .rev()
117 .take(window_size)
118 .collect();
119
120 let first_avg = recent
121 .iter()
122 .rev()
123 .take(window_size / 2)
124 .map(|(_, d)| d.as_secs_f64())
125 .sum::<f64>()
126 / (window_size / 2) as f64;
127
128 let second_avg = recent
129 .iter()
130 .take(window_size / 2)
131 .map(|(_, d)| d.as_secs_f64())
132 .sum::<f64>()
133 / (window_size / 2) as f64;
134
135 let change = (second_avg - first_avg) / first_avg;
136
137 self.trends.trend_analysis.execution_time_direction = if change < -0.1 {
138 TrendDirection::Improving
139 } else if change > 0.1 {
140 TrendDirection::Degrading
141 } else {
142 TrendDirection::Stable
143 };
144 }
145 }
146
147 pub fn check_alerts(&mut self) {
149 self.alert_system.check_alerts(&self.metrics);
150 }
151}
152
153#[derive(Debug, Clone)]
155pub struct TestPerformanceMetrics {
156 pub avg_execution_time: Duration,
158 pub execution_time_distribution: Vec<Duration>,
160 pub success_rate: f64,
162 pub resource_efficiency: f64,
164 pub throughput_rate: f64,
166}
167
168impl Default for TestPerformanceMetrics {
169 fn default() -> Self {
170 Self {
171 avg_execution_time: Duration::from_secs(0),
172 execution_time_distribution: vec![],
173 success_rate: 0.0,
174 resource_efficiency: 0.0,
175 throughput_rate: 0.0,
176 }
177 }
178}
179
180#[derive(Debug, Clone)]
182pub struct BenchmarkComparison {
183 pub baseline: PerformanceBaseline,
185 pub current: TestPerformanceMetrics,
187 pub delta: PerformanceDelta,
189 pub timestamp: SystemTime,
191}
192
193#[derive(Debug, Clone)]
195pub struct PerformanceBaseline {
196 pub execution_time: Duration,
198 pub success_rate: f64,
200 pub resource_usage: f64,
202 pub timestamp: SystemTime,
204}
205
206#[derive(Debug, Clone)]
208pub struct PerformanceDelta {
209 pub execution_time_change: f64,
211 pub success_rate_change: f64,
213 pub resource_usage_change: f64,
215 pub overall_change: f64,
217}
218
219#[derive(Debug, Clone)]
221pub struct PerformanceTrends {
222 pub execution_time_trend: Vec<(SystemTime, Duration)>,
224 pub success_rate_trend: Vec<(SystemTime, f64)>,
226 pub resource_usage_trend: Vec<(SystemTime, f64)>,
228 pub trend_analysis: TrendAnalysis,
230}
231
232impl Default for PerformanceTrends {
233 fn default() -> Self {
234 Self {
235 execution_time_trend: vec![],
236 success_rate_trend: vec![],
237 resource_usage_trend: vec![],
238 trend_analysis: TrendAnalysis::default(),
239 }
240 }
241}
242
243#[derive(Debug, Clone)]
245pub struct TrendAnalysis {
246 pub execution_time_direction: TrendDirection,
248 pub success_rate_direction: TrendDirection,
250 pub resource_usage_direction: TrendDirection,
252 pub confidence: f64,
254}
255
256impl Default for TrendAnalysis {
257 fn default() -> Self {
258 Self {
259 execution_time_direction: TrendDirection::Stable,
260 success_rate_direction: TrendDirection::Stable,
261 resource_usage_direction: TrendDirection::Stable,
262 confidence: 0.0,
263 }
264 }
265}
266
267#[derive(Debug, Clone, PartialEq, Eq)]
269pub enum TrendDirection {
270 Improving,
271 Stable,
272 Degrading,
273 Volatile,
274 Unknown,
275}
276
277pub struct PerformanceAlertSystem {
279 pub alert_rules: Vec<AlertRule>,
281 pub active_alerts: HashMap<String, PerformanceAlert>,
283 pub alert_history: Vec<PerformanceAlert>,
285}
286
287impl PerformanceAlertSystem {
288 #[must_use]
289 pub fn new() -> Self {
290 Self {
291 alert_rules: vec![],
292 active_alerts: HashMap::new(),
293 alert_history: vec![],
294 }
295 }
296
297 pub fn add_rule(&mut self, rule: AlertRule) {
299 self.alert_rules.push(rule);
300 }
301
302 pub fn remove_rule(&mut self, rule_name: &str) {
304 self.alert_rules.retain(|r| r.name != rule_name);
305 }
306
307 pub fn check_alerts(&mut self, metrics: &TestPerformanceMetrics) {
309 let triggered_rules: Vec<String> = self
311 .alert_rules
312 .iter()
313 .filter_map(|rule| {
314 let should_alert = match &rule.condition {
315 AlertCondition::ThresholdExceeded(threshold) => {
316 metrics.avg_execution_time.as_secs_f64() > *threshold
318 }
319 AlertCondition::ThresholdBelow(threshold) => {
320 metrics.success_rate < *threshold
322 }
323 AlertCondition::PercentageChange(_percentage) => {
324 false }
327 AlertCondition::Custom(_) => false,
328 };
329
330 should_alert.then(|| rule.name.clone())
331 })
332 .collect();
333
334 for rule_name in triggered_rules {
336 self.trigger_alert(&rule_name, metrics);
337 }
338 }
339
340 fn trigger_alert(&mut self, rule_name: &str, metrics: &TestPerformanceMetrics) {
342 let rule = self.alert_rules.iter().find(|r| r.name == rule_name);
343 if let Some(rule) = rule {
344 let alert = PerformanceAlert {
345 id: format!(
346 "alert_{}_{}",
347 rule_name,
348 SystemTime::now()
349 .duration_since(SystemTime::UNIX_EPOCH)
350 .unwrap_or(Duration::ZERO)
351 .as_secs()
352 ),
353 rule_name: rule_name.to_string(),
354 message: format!("Performance alert triggered for rule: {rule_name}"),
355 severity: rule.severity.clone(),
356 timestamp: SystemTime::now(),
357 metric_value: metrics.avg_execution_time.as_secs_f64(),
358 threshold_value: 0.0, status: AlertStatus::Active,
360 };
361
362 self.active_alerts.insert(alert.id.clone(), alert.clone());
363 self.alert_history.push(alert);
364
365 if self.alert_history.len() > 1000 {
367 self.alert_history.drain(0..1);
368 }
369 }
370 }
371
372 pub fn acknowledge_alert(&mut self, alert_id: &str) -> Result<(), String> {
374 let alert = self
375 .active_alerts
376 .get_mut(alert_id)
377 .ok_or_else(|| format!("Alert {alert_id} not found"))?;
378 alert.status = AlertStatus::Acknowledged;
379 Ok(())
380 }
381
382 pub fn resolve_alert(&mut self, alert_id: &str) -> Result<(), String> {
384 let alert = self
385 .active_alerts
386 .remove(alert_id)
387 .ok_or_else(|| format!("Alert {alert_id} not found"))?;
388
389 for hist_alert in &mut self.alert_history {
391 if hist_alert.id == alert_id {
392 hist_alert.status = AlertStatus::Resolved;
393 }
394 }
395 Ok(())
396 }
397
398 #[must_use]
400 pub fn get_active_alerts(&self) -> Vec<&PerformanceAlert> {
401 self.active_alerts.values().collect()
402 }
403
404 #[must_use]
406 pub fn get_alert_history(&self) -> &[PerformanceAlert] {
407 &self.alert_history
408 }
409
410 pub fn clear_all_alerts(&mut self) {
412 self.active_alerts.clear();
413 self.alert_history.clear();
414 }
415}
416
417#[derive(Debug, Clone)]
419pub struct AlertRule {
420 pub name: String,
422 pub metric: String,
424 pub condition: AlertCondition,
426 pub severity: AlertSeverity,
428 pub actions: Vec<AlertAction>,
430}
431
432#[derive(Debug, Clone)]
434pub enum AlertCondition {
435 ThresholdExceeded(f64),
437 ThresholdBelow(f64),
439 PercentageChange(f64),
441 Custom(String),
443}
444
445#[derive(Debug, Clone, PartialEq, Eq)]
447pub enum AlertSeverity {
448 Info,
449 Warning,
450 Error,
451 Critical,
452}
453
454#[derive(Debug, Clone)]
456pub enum AlertAction {
457 Log,
459 Email(String),
461 ExecuteScript(String),
463 Custom(String),
465}
466
467#[derive(Debug, Clone)]
469pub struct PerformanceAlert {
470 pub id: String,
472 pub rule_name: String,
474 pub message: String,
476 pub severity: AlertSeverity,
478 pub timestamp: SystemTime,
480 pub metric_value: f64,
482 pub threshold_value: f64,
484 pub status: AlertStatus,
486}
487
488#[derive(Debug, Clone, PartialEq, Eq)]
490pub enum AlertStatus {
491 Active,
492 Resolved,
493 Acknowledged,
494 Suppressed,
495}