1#[allow(unused_imports)]
7use std::sync::Arc;
8use std::{
9 sync::atomic::{AtomicU64, Ordering},
10 time::Instant,
11};
12
13#[derive(Debug, Clone)]
15pub struct QueryPerformance {
16 pub duration_us: u64,
18
19 pub db_queries: u32,
21
22 pub complexity: u32,
24
25 pub cached: bool,
27
28 pub db_duration_us: u64,
30
31 pub parse_duration_us: u64,
33
34 pub validation_duration_us: u64,
36}
37
38impl QueryPerformance {
39 #[must_use]
41 pub fn new(
42 duration_us: u64,
43 db_queries: u32,
44 complexity: u32,
45 cached: bool,
46 db_duration_us: u64,
47 ) -> Self {
48 Self {
49 duration_us,
50 db_queries,
51 complexity,
52 cached,
53 db_duration_us,
54 parse_duration_us: 0,
55 validation_duration_us: 0,
56 }
57 }
58
59 #[must_use]
61 pub fn with_parse_duration(mut self, duration_us: u64) -> Self {
62 self.parse_duration_us = duration_us;
63 self
64 }
65
66 #[must_use]
68 pub fn with_validation_duration(mut self, duration_us: u64) -> Self {
69 self.validation_duration_us = duration_us;
70 self
71 }
72
73 #[must_use]
75 pub fn non_db_duration_us(&self) -> u64 {
76 self.duration_us.saturating_sub(self.db_duration_us)
77 }
78
79 #[must_use]
81 pub fn db_percentage(&self) -> f64 {
82 if self.duration_us == 0 {
83 0.0
84 } else {
85 (self.db_duration_us as f64 / self.duration_us as f64) * 100.0
86 }
87 }
88
89 #[must_use]
91 pub fn is_slow(&self, threshold_ms: f64) -> bool {
92 (self.duration_us as f64 / 1000.0) > threshold_ms
93 }
94}
95
96#[derive(Debug, Clone)]
98pub struct OperationProfile {
99 pub operation: String,
101
102 pub count: u64,
104
105 pub total_duration_us: u64,
107
108 pub min_duration_us: u64,
110
111 pub max_duration_us: u64,
113
114 pub total_db_queries: u64,
116
117 pub avg_complexity: f64,
119
120 pub cache_hit_rate: f64,
122}
123
124impl OperationProfile {
125 #[must_use]
127 pub fn avg_duration_ms(&self) -> f64 {
128 if self.count == 0 {
129 0.0
130 } else {
131 (self.total_duration_us as f64 / self.count as f64) / 1000.0
132 }
133 }
134
135 #[must_use]
137 pub fn avg_db_queries(&self) -> f64 {
138 if self.count == 0 {
139 0.0
140 } else {
141 self.total_db_queries as f64 / self.count as f64
142 }
143 }
144}
145
146#[derive(Debug, Clone)]
148pub struct PerformanceMonitor {
149 queries_tracked: Arc<AtomicU64>,
151
152 slow_queries: Arc<AtomicU64>,
154
155 cached_queries: Arc<AtomicU64>,
157
158 db_queries_total: Arc<AtomicU64>,
160
161 total_duration_us: Arc<AtomicU64>,
163
164 min_duration_us: Arc<AtomicU64>,
166
167 max_duration_us: Arc<AtomicU64>,
169
170 slow_query_threshold_ms: f64,
172}
173
174impl PerformanceMonitor {
175 #[must_use]
177 pub fn new(slow_query_threshold_ms: f64) -> Self {
178 Self {
179 queries_tracked: Arc::new(AtomicU64::new(0)),
180 slow_queries: Arc::new(AtomicU64::new(0)),
181 cached_queries: Arc::new(AtomicU64::new(0)),
182 db_queries_total: Arc::new(AtomicU64::new(0)),
183 total_duration_us: Arc::new(AtomicU64::new(0)),
184 min_duration_us: Arc::new(AtomicU64::new(u64::MAX)),
185 max_duration_us: Arc::new(AtomicU64::new(0)),
186 slow_query_threshold_ms,
187 }
188 }
189
190 pub fn record_query(&self, performance: QueryPerformance) {
192 self.queries_tracked.fetch_add(1, Ordering::Relaxed);
194
195 self.total_duration_us.fetch_add(performance.duration_us, Ordering::Relaxed);
197
198 let mut min = self.min_duration_us.load(Ordering::Relaxed);
200 while performance.duration_us < min
201 && self
202 .min_duration_us
203 .compare_exchange(
204 min,
205 performance.duration_us,
206 Ordering::Relaxed,
207 Ordering::Relaxed,
208 )
209 .is_err()
210 {
211 min = self.min_duration_us.load(Ordering::Relaxed);
212 }
213
214 let mut max = self.max_duration_us.load(Ordering::Relaxed);
215 while performance.duration_us > max
216 && self
217 .max_duration_us
218 .compare_exchange(
219 max,
220 performance.duration_us,
221 Ordering::Relaxed,
222 Ordering::Relaxed,
223 )
224 .is_err()
225 {
226 max = self.max_duration_us.load(Ordering::Relaxed);
227 }
228
229 if performance.is_slow(self.slow_query_threshold_ms) {
231 self.slow_queries.fetch_add(1, Ordering::Relaxed);
232 }
233
234 if performance.cached {
236 self.cached_queries.fetch_add(1, Ordering::Relaxed);
237 }
238
239 self.db_queries_total
241 .fetch_add(u64::from(performance.db_queries), Ordering::Relaxed);
242 }
243
244 #[must_use]
246 pub fn stats(&self) -> PerformanceStats {
247 let queries_tracked = self.queries_tracked.load(Ordering::Relaxed);
248 let slow_queries = self.slow_queries.load(Ordering::Relaxed);
249 let cached_queries = self.cached_queries.load(Ordering::Relaxed);
250 let db_queries_total = self.db_queries_total.load(Ordering::Relaxed);
251 let total_duration_us = self.total_duration_us.load(Ordering::Relaxed);
252 let min_duration_us = self.min_duration_us.load(Ordering::Relaxed);
253 let max_duration_us = self.max_duration_us.load(Ordering::Relaxed);
254
255 PerformanceStats {
256 queries_tracked,
257 slow_queries,
258 cached_queries,
259 db_queries_total,
260 total_duration_us,
261 min_duration_us,
262 max_duration_us,
263 }
264 }
265
266 #[must_use]
268 pub fn avg_duration_ms(&self) -> f64 {
269 let stats = self.stats();
270 if stats.queries_tracked == 0 {
271 0.0
272 } else {
273 (stats.total_duration_us as f64 / stats.queries_tracked as f64) / 1000.0
274 }
275 }
276
277 #[must_use]
279 pub fn slow_query_percentage(&self) -> f64 {
280 let stats = self.stats();
281 if stats.queries_tracked == 0 {
282 0.0
283 } else {
284 (stats.slow_queries as f64 / stats.queries_tracked as f64) * 100.0
285 }
286 }
287
288 #[must_use]
290 pub fn cache_hit_rate(&self) -> f64 {
291 let stats = self.stats();
292 if stats.queries_tracked == 0 {
293 0.0
294 } else {
295 stats.cached_queries as f64 / stats.queries_tracked as f64
296 }
297 }
298
299 #[must_use]
301 pub fn create_timer(&self) -> PerformanceTimer {
302 PerformanceTimer::new()
303 }
304}
305
306#[derive(Debug, Clone)]
308pub struct PerformanceStats {
309 pub queries_tracked: u64,
311
312 pub slow_queries: u64,
314
315 pub cached_queries: u64,
317
318 pub db_queries_total: u64,
320
321 pub total_duration_us: u64,
323
324 pub min_duration_us: u64,
326
327 pub max_duration_us: u64,
329}
330
331impl PerformanceStats {
332 #[must_use]
334 pub fn avg_duration_ms(&self) -> f64 {
335 if self.queries_tracked == 0 {
336 0.0
337 } else {
338 (self.total_duration_us as f64 / self.queries_tracked as f64) / 1000.0
339 }
340 }
341
342 #[must_use]
344 pub fn avg_db_queries(&self) -> f64 {
345 if self.queries_tracked == 0 {
346 0.0
347 } else {
348 self.db_queries_total as f64 / self.queries_tracked as f64
349 }
350 }
351
352 #[must_use]
354 pub fn slow_query_percentage(&self) -> f64 {
355 if self.queries_tracked == 0 {
356 0.0
357 } else {
358 (self.slow_queries as f64 / self.queries_tracked as f64) * 100.0
359 }
360 }
361}
362
363#[derive(Debug)]
365pub struct PerformanceTimer {
366 start: Instant,
367}
368
369impl PerformanceTimer {
370 #[must_use]
372 pub fn new() -> Self {
373 Self {
374 start: Instant::now(),
375 }
376 }
377
378 #[must_use]
380 pub fn record(self) -> u64 {
381 self.start.elapsed().as_micros() as u64
382 }
383
384 #[must_use]
386 pub fn elapsed_us(&self) -> u64 {
387 self.start.elapsed().as_micros() as u64
388 }
389}
390
391impl Default for PerformanceTimer {
392 fn default() -> Self {
393 Self::new()
394 }
395}
396
397#[cfg(test)]
398mod tests {
399 use super::*;
400
401 #[test]
402 fn test_query_performance_creation() {
403 let perf = QueryPerformance::new(1000, 2, 5, false, 500);
404 assert_eq!(perf.duration_us, 1000);
405 assert_eq!(perf.db_queries, 2);
406 assert_eq!(perf.complexity, 5);
407 assert!(!perf.cached);
408 }
409
410 #[test]
411 fn test_query_performance_with_durations() {
412 let perf = QueryPerformance::new(1000, 2, 5, false, 500)
413 .with_parse_duration(100)
414 .with_validation_duration(150);
415
416 assert_eq!(perf.parse_duration_us, 100);
417 assert_eq!(perf.validation_duration_us, 150);
418 }
419
420 #[test]
421 fn test_query_performance_calculations() {
422 let perf = QueryPerformance::new(1000, 2, 5, false, 600);
423
424 assert_eq!(perf.non_db_duration_us(), 400);
425 assert!(perf.db_percentage() > 59.0 && perf.db_percentage() < 61.0); }
427
428 #[test]
429 fn test_query_performance_is_slow() {
430 let fast = QueryPerformance::new(5000, 1, 3, false, 1000); let slow = QueryPerformance::new(50000, 3, 8, false, 40000); assert!(!fast.is_slow(10.0));
434 assert!(slow.is_slow(10.0));
435 }
436
437 #[test]
438 fn test_performance_monitor_creation() {
439 let monitor = PerformanceMonitor::new(100.0);
440 let stats = monitor.stats();
441
442 assert_eq!(stats.queries_tracked, 0);
443 assert_eq!(stats.slow_queries, 0);
444 }
445
446 #[test]
447 fn test_performance_monitor_record_query() {
448 let monitor = PerformanceMonitor::new(10.0);
449 let perf = QueryPerformance::new(5000, 2, 5, false, 2500);
450
451 monitor.record_query(perf);
452
453 let stats = monitor.stats();
454 assert_eq!(stats.queries_tracked, 1);
455 assert_eq!(stats.total_duration_us, 5000);
456 assert_eq!(stats.db_queries_total, 2);
457 }
458
459 #[test]
460 fn test_performance_monitor_slow_queries() {
461 let monitor = PerformanceMonitor::new(10.0);
462
463 let slow_perf = QueryPerformance::new(20000, 2, 5, false, 10000); monitor.record_query(slow_perf);
465
466 let stats = monitor.stats();
467 assert_eq!(stats.slow_queries, 1);
468 assert_eq!(stats.queries_tracked, 1);
469 }
470
471 #[test]
472 fn test_performance_monitor_cached_queries() {
473 let monitor = PerformanceMonitor::new(10.0);
474
475 let cached_perf = QueryPerformance::new(1000, 0, 5, true, 0);
476 monitor.record_query(cached_perf);
477
478 let stats = monitor.stats();
479 assert_eq!(stats.cached_queries, 1);
480 }
481
482 #[test]
483 fn test_performance_monitor_avg_calculations() {
484 let monitor = PerformanceMonitor::new(10.0);
485
486 monitor.record_query(QueryPerformance::new(2000, 1, 5, false, 1000));
487 monitor.record_query(QueryPerformance::new(4000, 3, 5, false, 2000));
488
489 assert!((monitor.avg_duration_ms() - 3.0).abs() < f64::EPSILON); }
491
492 #[test]
493 fn test_performance_monitor_cache_hit_rate() {
494 let monitor = PerformanceMonitor::new(10.0);
495
496 monitor.record_query(QueryPerformance::new(1000, 1, 5, false, 800));
497 monitor.record_query(QueryPerformance::new(1000, 0, 5, true, 0));
498 monitor.record_query(QueryPerformance::new(1000, 1, 5, false, 800));
499
500 let rate = monitor.cache_hit_rate();
501 assert!(rate > 0.32 && rate < 0.34); }
503
504 #[test]
505 fn test_performance_timer_creation() {
506 let timer = PerformanceTimer::new();
507 let duration = timer.elapsed_us();
508 assert!(duration < 1000); }
510
511 #[test]
512 fn test_performance_timer_record() {
513 let timer = PerformanceTimer::new();
514 std::thread::sleep(std::time::Duration::from_millis(10));
515 let duration = timer.record();
516
517 assert!(duration >= 10000); }
519
520 #[test]
521 fn test_performance_stats_calculations() {
522 let stats = PerformanceStats {
523 queries_tracked: 100,
524 slow_queries: 10,
525 cached_queries: 30,
526 db_queries_total: 200,
527 total_duration_us: 500_000,
528 min_duration_us: 1000,
529 max_duration_us: 50_000,
530 };
531
532 assert!((stats.avg_duration_ms() - 5.0).abs() < f64::EPSILON);
533 assert!((stats.avg_db_queries() - 2.0).abs() < f64::EPSILON);
534 assert!((stats.slow_query_percentage() - 10.0).abs() < f64::EPSILON);
535 }
536
537 #[test]
538 fn test_operation_profile_creation() {
539 let profile = OperationProfile {
540 operation: "GetUser".to_string(),
541 count: 100,
542 total_duration_us: 500_000,
543 min_duration_us: 1000,
544 max_duration_us: 50_000,
545 total_db_queries: 200,
546 avg_complexity: 5.5,
547 cache_hit_rate: 0.75,
548 };
549
550 assert!((profile.avg_duration_ms() - 5.0).abs() < f64::EPSILON);
551 assert!((profile.avg_db_queries() - 2.0).abs() < f64::EPSILON);
552 }
553}