chie_shared/types/
profiling.rs

1//! Profiling and performance metrics types for CHIE Protocol.
2//!
3//! This module provides shared types for tracking operation performance,
4//! timings, and generating performance reports.
5
6#[cfg(feature = "schema")]
7use schemars::JsonSchema;
8use serde::{Deserialize, Serialize};
9
10use super::core::Bytes;
11
12/// Performance statistics for a profiled operation.
13///
14/// # Examples
15///
16/// ```
17/// use chie_shared::OperationStats;
18///
19/// // Track 100 database queries
20/// let stats = OperationStats::new(100, 5000.0, 10.0, 200.0);
21///
22/// // Analyze performance
23/// assert_eq!(stats.avg_duration_ms, 50.0);
24/// assert_eq!(stats.ops_per_second(), 20.0);
25///
26/// // Check if operations are slow (> 100ms threshold)
27/// assert!(!stats.is_slow(100.0));
28///
29/// // Get percentile estimates
30/// assert_eq!(stats.p50_estimate_ms(), 50.0);
31/// assert_eq!(stats.p99_estimate_ms(), 200.0);
32///
33/// // Convert to seconds
34/// assert_eq!(stats.total_duration_secs(), 5.0);
35/// ```
36#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
37#[cfg_attr(feature = "schema", derive(JsonSchema))]
38pub struct OperationStats {
39    /// Number of times the operation was executed.
40    pub count: u64,
41    /// Total time spent in milliseconds.
42    pub total_duration_ms: f64,
43    /// Minimum execution time in milliseconds.
44    pub min_duration_ms: f64,
45    /// Maximum execution time in milliseconds.
46    pub max_duration_ms: f64,
47    /// Average execution time in milliseconds.
48    pub avg_duration_ms: f64,
49}
50
51impl OperationStats {
52    /// Create new operation statistics.
53    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    /// Create empty operation statistics.
75    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    /// Get operations per second based on total time.
86    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    /// Get p99 estimate (uses max as approximation).
94    pub fn p99_estimate_ms(&self) -> f64 {
95        self.max_duration_ms
96    }
97
98    /// Get p50 estimate (uses avg as approximation).
99    pub fn p50_estimate_ms(&self) -> f64 {
100        self.avg_duration_ms
101    }
102
103    /// Check if operation is slow (avg > threshold).
104    pub fn is_slow(&self, threshold_ms: f64) -> bool {
105        self.avg_duration_ms > threshold_ms
106    }
107
108    /// Get total time in seconds.
109    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/// Bandwidth performance metrics.
121///
122/// # Examples
123///
124/// ```
125/// use chie_shared::BandwidthMetrics;
126///
127/// // Track bandwidth for 10 chunk transfers (1 GB total)
128/// let metrics = BandwidthMetrics::new(
129///     1024 * 1024 * 1024,  // 1 GB
130///     1000.0,              // 1 second
131///     10,                  // 10 transfers
132///     2_000_000.0,         // 2 MB/s peak
133/// );
134///
135/// // Check average speeds
136/// assert!(metrics.avg_mbps() > 1000.0);
137/// assert_eq!(metrics.peak_mbps(), 16.0);
138///
139/// // Calculate efficiency
140/// let avg_chunk_size = metrics.avg_bytes_per_transfer();
141/// assert!(avg_chunk_size > 0.0);
142/// ```
143#[derive(Debug, Clone, Serialize, Deserialize)]
144#[cfg_attr(feature = "schema", derive(JsonSchema))]
145pub struct BandwidthMetrics {
146    /// Total bytes transferred.
147    pub bytes_transferred: Bytes,
148    /// Total transfer time in milliseconds.
149    pub total_time_ms: f64,
150    /// Number of transfers.
151    pub transfer_count: u64,
152    /// Average bytes per second.
153    pub avg_bps: f64,
154    /// Peak bytes per second.
155    pub peak_bps: f64,
156}
157
158impl BandwidthMetrics {
159    /// Create new bandwidth metrics.
160    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    /// Get average transfer time in milliseconds.
182    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    /// Get average bytes per transfer.
191    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    /// Get bandwidth in Mbps.
200    pub fn avg_mbps(&self) -> f64 {
201        (self.avg_bps * 8.0) / 1_000_000.0
202    }
203
204    /// Get peak bandwidth in Mbps.
205    pub fn peak_mbps(&self) -> f64 {
206        (self.peak_bps * 8.0) / 1_000_000.0
207    }
208
209    /// Get total data in gigabytes.
210    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/// Latency statistics.
222#[derive(Debug, Clone, Serialize, Deserialize)]
223#[cfg_attr(feature = "schema", derive(JsonSchema))]
224pub struct LatencyStats {
225    /// Number of measurements.
226    pub count: u64,
227    /// Minimum latency in milliseconds.
228    pub min_ms: f64,
229    /// Maximum latency in milliseconds.
230    pub max_ms: f64,
231    /// Average latency in milliseconds.
232    pub avg_ms: f64,
233    /// P50 latency in milliseconds.
234    pub p50_ms: f64,
235    /// P95 latency in milliseconds.
236    pub p95_ms: f64,
237    /// P99 latency in milliseconds.
238    pub p99_ms: f64,
239}
240
241impl LatencyStats {
242    /// Create new latency statistics.
243    #[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    /// Check if latency is within acceptable range.
265    pub fn is_acceptable(&self, threshold_ms: f64) -> bool {
266        self.p95_ms <= threshold_ms
267    }
268
269    /// Get jitter (max - min).
270    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/// Throughput metrics.
282#[derive(Debug, Clone, Serialize, Deserialize)]
283#[cfg_attr(feature = "schema", derive(JsonSchema))]
284pub struct ThroughputMetrics {
285    /// Number of operations completed.
286    pub operations_completed: u64,
287    /// Time period in seconds.
288    pub time_period_secs: f64,
289    /// Operations per second.
290    pub ops_per_second: f64,
291    /// Peak ops per second.
292    pub peak_ops_per_second: f64,
293}
294
295impl ThroughputMetrics {
296    /// Create new throughput metrics.
297    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    /// Get average time per operation in milliseconds.
313    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    /// Get utilization percentage (current vs peak).
322    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/// Resource utilization metrics.
338#[derive(Debug, Clone, Serialize, Deserialize)]
339#[cfg_attr(feature = "schema", derive(JsonSchema))]
340pub struct ResourceMetrics {
341    /// CPU usage percentage (0.0 to 100.0).
342    pub cpu_percent: f64,
343    /// Memory usage in bytes.
344    pub memory_bytes: u64,
345    /// Disk usage in bytes.
346    pub disk_bytes: u64,
347    /// Network bytes sent.
348    pub network_bytes_sent: u64,
349    /// Network bytes received.
350    pub network_bytes_received: u64,
351}
352
353impl ResourceMetrics {
354    /// Create new resource metrics.
355    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    /// Get memory usage in megabytes.
372    pub fn memory_mb(&self) -> f64 {
373        self.memory_bytes as f64 / (1024.0 * 1024.0)
374    }
375
376    /// Get disk usage in gigabytes.
377    pub fn disk_gb(&self) -> f64 {
378        self.disk_bytes as f64 / (1024.0 * 1024.0 * 1024.0)
379    }
380
381    /// Get total network bytes.
382    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/// Builder for OperationStats with fluent API.
394#[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    /// Create a new builder.
404    pub fn new() -> Self {
405        Self::default()
406    }
407
408    /// Set the operation count.
409    pub fn count(mut self, count: u64) -> Self {
410        self.count = Some(count);
411        self
412    }
413
414    /// Set the total duration in milliseconds.
415    pub fn total_duration_ms(mut self, duration: f64) -> Self {
416        self.total_duration_ms = Some(duration);
417        self
418    }
419
420    /// Set the minimum duration in milliseconds.
421    pub fn min_duration_ms(mut self, duration: f64) -> Self {
422        self.min_duration_ms = Some(duration);
423        self
424    }
425
426    /// Set the maximum duration in milliseconds.
427    pub fn max_duration_ms(mut self, duration: f64) -> Self {
428        self.max_duration_ms = Some(duration);
429        self
430    }
431
432    /// Build the OperationStats.
433    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/// Builder for BandwidthMetrics with fluent API.
444#[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    /// Create a new builder.
454    pub fn new() -> Self {
455        Self::default()
456    }
457
458    /// Set the bytes transferred.
459    pub fn bytes_transferred(mut self, bytes: Bytes) -> Self {
460        self.bytes_transferred = Some(bytes);
461        self
462    }
463
464    /// Set the total time in milliseconds.
465    pub fn total_time_ms(mut self, time: f64) -> Self {
466        self.total_time_ms = Some(time);
467        self
468    }
469
470    /// Set the transfer count.
471    pub fn transfer_count(mut self, count: u64) -> Self {
472        self.transfer_count = Some(count);
473        self
474    }
475
476    /// Set the peak bytes per second.
477    pub fn peak_bps(mut self, bps: f64) -> Self {
478        self.peak_bps = Some(bps);
479        self
480    }
481
482    /// Build the BandwidthMetrics.
483    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        // 100 ops in 1000ms = 100 ops/sec
511        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        // 1 MB/s = 8 Mbps
535        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,      // 512 MB
561            5 * 1024 * 1024 * 1024, // 5 GB
562            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}