1#[cfg(feature = "schema")]
7use schemars::JsonSchema;
8use serde::{Deserialize, Serialize};
9
10use super::core::Bytes;
11
12#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
37#[cfg_attr(feature = "schema", derive(JsonSchema))]
38pub struct OperationStats {
39 pub count: u64,
41 pub total_duration_ms: f64,
43 pub min_duration_ms: f64,
45 pub max_duration_ms: f64,
47 pub avg_duration_ms: f64,
49}
50
51impl OperationStats {
52 pub fn new(
54 count: u64,
55 total_duration_ms: f64,
56 min_duration_ms: f64,
57 max_duration_ms: f64,
58 ) -> Self {
59 let avg_duration_ms = if count > 0 {
60 total_duration_ms / count as f64
61 } else {
62 0.0
63 };
64
65 Self {
66 count,
67 total_duration_ms,
68 min_duration_ms,
69 max_duration_ms,
70 avg_duration_ms,
71 }
72 }
73
74 pub fn empty() -> Self {
76 Self {
77 count: 0,
78 total_duration_ms: 0.0,
79 min_duration_ms: f64::MAX,
80 max_duration_ms: 0.0,
81 avg_duration_ms: 0.0,
82 }
83 }
84
85 pub fn ops_per_second(&self) -> f64 {
87 if self.total_duration_ms == 0.0 {
88 return 0.0;
89 }
90 (self.count as f64 * 1000.0) / self.total_duration_ms
91 }
92
93 pub fn p99_estimate_ms(&self) -> f64 {
95 self.max_duration_ms
96 }
97
98 pub fn p50_estimate_ms(&self) -> f64 {
100 self.avg_duration_ms
101 }
102
103 pub fn is_slow(&self, threshold_ms: f64) -> bool {
105 self.avg_duration_ms > threshold_ms
106 }
107
108 pub fn total_duration_secs(&self) -> f64 {
110 self.total_duration_ms / 1000.0
111 }
112}
113
114impl Default for OperationStats {
115 fn default() -> Self {
116 Self::empty()
117 }
118}
119
120#[derive(Debug, Clone, Serialize, Deserialize)]
144#[cfg_attr(feature = "schema", derive(JsonSchema))]
145pub struct BandwidthMetrics {
146 pub bytes_transferred: Bytes,
148 pub total_time_ms: f64,
150 pub transfer_count: u64,
152 pub avg_bps: f64,
154 pub peak_bps: f64,
156}
157
158impl BandwidthMetrics {
159 pub fn new(
161 bytes_transferred: Bytes,
162 total_time_ms: f64,
163 transfer_count: u64,
164 peak_bps: f64,
165 ) -> Self {
166 let avg_bps = if total_time_ms > 0.0 {
167 (bytes_transferred as f64 * 1000.0) / total_time_ms
168 } else {
169 0.0
170 };
171
172 Self {
173 bytes_transferred,
174 total_time_ms,
175 transfer_count,
176 avg_bps,
177 peak_bps,
178 }
179 }
180
181 pub fn avg_transfer_time_ms(&self) -> f64 {
183 if self.transfer_count == 0 {
184 0.0
185 } else {
186 self.total_time_ms / self.transfer_count as f64
187 }
188 }
189
190 pub fn avg_bytes_per_transfer(&self) -> f64 {
192 if self.transfer_count == 0 {
193 0.0
194 } else {
195 self.bytes_transferred as f64 / self.transfer_count as f64
196 }
197 }
198
199 pub fn avg_mbps(&self) -> f64 {
201 (self.avg_bps * 8.0) / 1_000_000.0
202 }
203
204 pub fn peak_mbps(&self) -> f64 {
206 (self.peak_bps * 8.0) / 1_000_000.0
207 }
208
209 pub fn total_gb(&self) -> f64 {
211 self.bytes_transferred as f64 / (1024.0 * 1024.0 * 1024.0)
212 }
213}
214
215impl Default for BandwidthMetrics {
216 fn default() -> Self {
217 Self::new(0, 0.0, 0, 0.0)
218 }
219}
220
221#[derive(Debug, Clone, Serialize, Deserialize)]
223#[cfg_attr(feature = "schema", derive(JsonSchema))]
224pub struct LatencyStats {
225 pub count: u64,
227 pub min_ms: f64,
229 pub max_ms: f64,
231 pub avg_ms: f64,
233 pub p50_ms: f64,
235 pub p95_ms: f64,
237 pub p99_ms: f64,
239}
240
241impl LatencyStats {
242 #[allow(clippy::too_many_arguments)]
244 pub fn new(
245 count: u64,
246 min_ms: f64,
247 max_ms: f64,
248 avg_ms: f64,
249 p50_ms: f64,
250 p95_ms: f64,
251 p99_ms: f64,
252 ) -> Self {
253 Self {
254 count,
255 min_ms,
256 max_ms,
257 avg_ms,
258 p50_ms,
259 p95_ms,
260 p99_ms,
261 }
262 }
263
264 pub fn is_acceptable(&self, threshold_ms: f64) -> bool {
266 self.p95_ms <= threshold_ms
267 }
268
269 pub fn jitter_ms(&self) -> f64 {
271 self.max_ms - self.min_ms
272 }
273}
274
275impl Default for LatencyStats {
276 fn default() -> Self {
277 Self::new(0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
278 }
279}
280
281#[derive(Debug, Clone, Serialize, Deserialize)]
283#[cfg_attr(feature = "schema", derive(JsonSchema))]
284pub struct ThroughputMetrics {
285 pub operations_completed: u64,
287 pub time_period_secs: f64,
289 pub ops_per_second: f64,
291 pub peak_ops_per_second: f64,
293}
294
295impl ThroughputMetrics {
296 pub fn new(operations_completed: u64, time_period_secs: f64, peak_ops_per_second: f64) -> Self {
298 let ops_per_second = if time_period_secs > 0.0 {
299 operations_completed as f64 / time_period_secs
300 } else {
301 0.0
302 };
303
304 Self {
305 operations_completed,
306 time_period_secs,
307 ops_per_second,
308 peak_ops_per_second,
309 }
310 }
311
312 pub fn avg_time_per_op_ms(&self) -> f64 {
314 if self.operations_completed == 0 {
315 0.0
316 } else {
317 (self.time_period_secs * 1000.0) / self.operations_completed as f64
318 }
319 }
320
321 pub fn utilization_percentage(&self) -> f64 {
323 if self.peak_ops_per_second == 0.0 {
324 0.0
325 } else {
326 (self.ops_per_second / self.peak_ops_per_second) * 100.0
327 }
328 }
329}
330
331impl Default for ThroughputMetrics {
332 fn default() -> Self {
333 Self::new(0, 0.0, 0.0)
334 }
335}
336
337#[derive(Debug, Clone, Serialize, Deserialize)]
339#[cfg_attr(feature = "schema", derive(JsonSchema))]
340pub struct ResourceMetrics {
341 pub cpu_percent: f64,
343 pub memory_bytes: u64,
345 pub disk_bytes: u64,
347 pub network_bytes_sent: u64,
349 pub network_bytes_received: u64,
351}
352
353impl ResourceMetrics {
354 pub fn new(
356 cpu_percent: f64,
357 memory_bytes: u64,
358 disk_bytes: u64,
359 network_bytes_sent: u64,
360 network_bytes_received: u64,
361 ) -> Self {
362 Self {
363 cpu_percent,
364 memory_bytes,
365 disk_bytes,
366 network_bytes_sent,
367 network_bytes_received,
368 }
369 }
370
371 pub fn memory_mb(&self) -> f64 {
373 self.memory_bytes as f64 / (1024.0 * 1024.0)
374 }
375
376 pub fn disk_gb(&self) -> f64 {
378 self.disk_bytes as f64 / (1024.0 * 1024.0 * 1024.0)
379 }
380
381 pub fn total_network_bytes(&self) -> u64 {
383 self.network_bytes_sent + self.network_bytes_received
384 }
385}
386
387impl Default for ResourceMetrics {
388 fn default() -> Self {
389 Self::new(0.0, 0, 0, 0, 0)
390 }
391}
392
393#[derive(Debug, Default)]
395pub struct OperationStatsBuilder {
396 count: Option<u64>,
397 total_duration_ms: Option<f64>,
398 min_duration_ms: Option<f64>,
399 max_duration_ms: Option<f64>,
400}
401
402impl OperationStatsBuilder {
403 pub fn new() -> Self {
405 Self::default()
406 }
407
408 pub fn count(mut self, count: u64) -> Self {
410 self.count = Some(count);
411 self
412 }
413
414 pub fn total_duration_ms(mut self, duration: f64) -> Self {
416 self.total_duration_ms = Some(duration);
417 self
418 }
419
420 pub fn min_duration_ms(mut self, duration: f64) -> Self {
422 self.min_duration_ms = Some(duration);
423 self
424 }
425
426 pub fn max_duration_ms(mut self, duration: f64) -> Self {
428 self.max_duration_ms = Some(duration);
429 self
430 }
431
432 pub fn build(self) -> OperationStats {
434 OperationStats::new(
435 self.count.unwrap_or(0),
436 self.total_duration_ms.unwrap_or(0.0),
437 self.min_duration_ms.unwrap_or(f64::MAX),
438 self.max_duration_ms.unwrap_or(0.0),
439 )
440 }
441}
442
443#[derive(Debug, Default)]
445pub struct BandwidthMetricsBuilder {
446 bytes_transferred: Option<Bytes>,
447 total_time_ms: Option<f64>,
448 transfer_count: Option<u64>,
449 peak_bps: Option<f64>,
450}
451
452impl BandwidthMetricsBuilder {
453 pub fn new() -> Self {
455 Self::default()
456 }
457
458 pub fn bytes_transferred(mut self, bytes: Bytes) -> Self {
460 self.bytes_transferred = Some(bytes);
461 self
462 }
463
464 pub fn total_time_ms(mut self, time: f64) -> Self {
466 self.total_time_ms = Some(time);
467 self
468 }
469
470 pub fn transfer_count(mut self, count: u64) -> Self {
472 self.transfer_count = Some(count);
473 self
474 }
475
476 pub fn peak_bps(mut self, bps: f64) -> Self {
478 self.peak_bps = Some(bps);
479 self
480 }
481
482 pub fn build(self) -> BandwidthMetrics {
484 BandwidthMetrics::new(
485 self.bytes_transferred.unwrap_or(0),
486 self.total_time_ms.unwrap_or(0.0),
487 self.transfer_count.unwrap_or(0),
488 self.peak_bps.unwrap_or(0.0),
489 )
490 }
491}
492
493#[cfg(test)]
494mod tests {
495 use super::*;
496
497 #[test]
498 fn test_operation_stats_new() {
499 let stats = OperationStats::new(100, 5000.0, 10.0, 200.0);
500 assert_eq!(stats.count, 100);
501 assert_eq!(stats.total_duration_ms, 5000.0);
502 assert_eq!(stats.min_duration_ms, 10.0);
503 assert_eq!(stats.max_duration_ms, 200.0);
504 assert_eq!(stats.avg_duration_ms, 50.0);
505 }
506
507 #[test]
508 fn test_operation_stats_ops_per_second() {
509 let stats = OperationStats::new(100, 1000.0, 5.0, 20.0);
510 assert!((stats.ops_per_second() - 100.0).abs() < 0.001);
512 }
513
514 #[test]
515 fn test_operation_stats_is_slow() {
516 let stats = OperationStats::new(10, 1000.0, 50.0, 150.0);
517 assert!(stats.is_slow(50.0));
518 assert!(!stats.is_slow(200.0));
519 }
520
521 #[test]
522 fn test_bandwidth_metrics() {
523 let metrics = BandwidthMetrics::new(1_000_000, 1000.0, 10, 2_000_000.0);
524 assert_eq!(metrics.bytes_transferred, 1_000_000);
525 assert_eq!(metrics.transfer_count, 10);
526 assert_eq!(metrics.avg_bps, 1_000_000.0);
527 assert_eq!(metrics.avg_transfer_time_ms(), 100.0);
528 assert_eq!(metrics.avg_bytes_per_transfer(), 100_000.0);
529 }
530
531 #[test]
532 fn test_bandwidth_metrics_mbps() {
533 let metrics = BandwidthMetrics::new(1_000_000, 1000.0, 1, 1_000_000.0);
534 assert!((metrics.avg_mbps() - 8.0).abs() < 0.001);
536 }
537
538 #[test]
539 fn test_latency_stats() {
540 let stats = LatencyStats::new(100, 5.0, 100.0, 25.0, 20.0, 80.0, 95.0);
541 assert_eq!(stats.count, 100);
542 assert!(stats.is_acceptable(90.0));
543 assert!(!stats.is_acceptable(75.0));
544 assert_eq!(stats.jitter_ms(), 95.0);
545 }
546
547 #[test]
548 fn test_throughput_metrics() {
549 let metrics = ThroughputMetrics::new(1000, 10.0, 200.0);
550 assert_eq!(metrics.operations_completed, 1000);
551 assert_eq!(metrics.ops_per_second, 100.0);
552 assert_eq!(metrics.avg_time_per_op_ms(), 10.0);
553 assert_eq!(metrics.utilization_percentage(), 50.0);
554 }
555
556 #[test]
557 fn test_resource_metrics() {
558 let metrics = ResourceMetrics::new(
559 75.5,
560 512 * 1024 * 1024, 5 * 1024 * 1024 * 1024, 1_000_000,
563 2_000_000,
564 );
565
566 assert_eq!(metrics.cpu_percent, 75.5);
567 assert!((metrics.memory_mb() - 512.0).abs() < 0.1);
568 assert!((metrics.disk_gb() - 5.0).abs() < 0.1);
569 assert_eq!(metrics.total_network_bytes(), 3_000_000);
570 }
571
572 #[test]
573 fn test_operation_stats_serialization() {
574 let stats = OperationStats::new(100, 5000.0, 10.0, 200.0);
575 let json = serde_json::to_string(&stats).unwrap();
576 let deserialized: OperationStats = serde_json::from_str(&json).unwrap();
577 assert_eq!(stats, deserialized);
578 }
579
580 #[test]
581 fn test_operation_stats_empty() {
582 let stats = OperationStats::empty();
583 assert_eq!(stats.count, 0);
584 assert_eq!(stats.total_duration_ms, 0.0);
585 assert_eq!(stats.avg_duration_ms, 0.0);
586 assert_eq!(stats.ops_per_second(), 0.0);
587 }
588
589 #[test]
590 fn test_bandwidth_metrics_default() {
591 let metrics = BandwidthMetrics::default();
592 assert_eq!(metrics.bytes_transferred, 0);
593 assert_eq!(metrics.avg_bps, 0.0);
594 }
595
596 #[test]
597 fn test_operation_stats_builder() {
598 let stats = OperationStatsBuilder::new()
599 .count(100)
600 .total_duration_ms(5000.0)
601 .min_duration_ms(10.0)
602 .max_duration_ms(200.0)
603 .build();
604
605 assert_eq!(stats.count, 100);
606 assert_eq!(stats.total_duration_ms, 5000.0);
607 assert_eq!(stats.min_duration_ms, 10.0);
608 assert_eq!(stats.max_duration_ms, 200.0);
609 assert_eq!(stats.avg_duration_ms, 50.0);
610 }
611
612 #[test]
613 fn test_operation_stats_builder_partial() {
614 let stats = OperationStatsBuilder::new()
615 .count(10)
616 .total_duration_ms(1000.0)
617 .build();
618
619 assert_eq!(stats.count, 10);
620 assert_eq!(stats.total_duration_ms, 1000.0);
621 assert_eq!(stats.avg_duration_ms, 100.0);
622 }
623
624 #[test]
625 fn test_bandwidth_metrics_builder() {
626 let metrics = BandwidthMetricsBuilder::new()
627 .bytes_transferred(1_000_000)
628 .total_time_ms(1000.0)
629 .transfer_count(10)
630 .peak_bps(2_000_000.0)
631 .build();
632
633 assert_eq!(metrics.bytes_transferred, 1_000_000);
634 assert_eq!(metrics.total_time_ms, 1000.0);
635 assert_eq!(metrics.transfer_count, 10);
636 assert_eq!(metrics.peak_bps, 2_000_000.0);
637 }
638}