commonware_runtime/telemetry/metrics/
histogram.rs

1//! Utilities for working with histograms.
2
3use crate::Clock;
4use prometheus_client::metrics::histogram::Histogram;
5use std::{sync::Arc, time::SystemTime};
6
7/// Holds constants for bucket sizes for histograms.
8///
9/// The bucket sizes are in seconds.
10pub struct Buckets;
11
12impl Buckets {
13    /// For resolving items over a network.
14    ///
15    /// These tasks could either be between two peers or require multiple hops, rounds, retries,
16    /// etc.
17    pub const NETWORK: [f64; 13] = [
18        0.010, 0.020, 0.050, 0.100, 0.200, 0.500, 1.0, 2.0, 5.0, 10.0, 30.0, 60.0, 300.0,
19    ];
20
21    /// For resolving items locally.
22    ///
23    /// These tasks are expected to be fast and not require network access, but might require
24    /// expensive computation, disk access, etc.
25    pub const LOCAL: [f64; 12] = [
26        3e-6, 1e-5, 3e-5, 1e-4, 3e-4, 0.001, 0.003, 0.01, 0.03, 0.1, 0.3, 1.0,
27    ];
28
29    /// For cryptographic operations.
30    ///
31    /// These operations are expected to be fast and not require network access, but might
32    /// require expensive computation.
33    pub const CRYPTOGRAPHY: [f64; 16] = [
34        3e-6, 1e-5, 3e-5, 1e-4, 3e-4, 0.001, 0.002, 0.003, 0.005, 0.01, 0.015, 0.02, 0.025, 0.03,
35        0.1, 0.2,
36    ];
37}
38
39/// Extension trait for histograms.
40pub trait HistogramExt {
41    /// Observe the duration between two points in time, in seconds.
42    ///
43    /// If the clock goes backwards, the duration is 0.
44    fn observe_between(&self, start: SystemTime, end: SystemTime);
45}
46
47impl HistogramExt for Histogram {
48    fn observe_between(&self, start: SystemTime, end: SystemTime) {
49        let duration = match end.duration_since(start) {
50            Ok(duration) => duration.as_secs_f64(),
51            Err(_) => 0.0, // Clock went backwards
52        };
53        self.observe(duration);
54    }
55}
56
57/// A wrapper around a histogram that includes a clock.
58#[derive(Clone)]
59pub struct Timed<C: Clock> {
60    /// The histogram to record durations in.
61    histogram: Histogram,
62
63    /// The clock to use for recording durations.
64    clock: Arc<C>,
65}
66
67impl<C: Clock> Timed<C> {
68    /// Create a new timed histogram.
69    pub fn new(histogram: Histogram, clock: Arc<C>) -> Self {
70        Self { histogram, clock }
71    }
72
73    /// Create a new timer that can record a duration from the current time.
74    pub fn timer(&self) -> Timer<C> {
75        let start = self.clock.current();
76        Timer {
77            histogram: self.histogram.clone(),
78            clock: self.clock.clone(), // Arc clone
79            start,
80            canceled: false,
81        }
82    }
83}
84
85/// A timer that records a duration when dropped.
86pub struct Timer<C: Clock> {
87    /// The histogram to record durations in.
88    histogram: Histogram,
89
90    /// The clock to use for recording durations.
91    clock: Arc<C>,
92
93    /// The time at which the timer was started.
94    start: SystemTime,
95
96    /// Whether the timer was canceled.
97    canceled: bool,
98}
99
100impl<C: Clock> Timer<C> {
101    /// Record the duration and cancel the timer.
102    pub fn observe(&mut self) {
103        self.canceled = true;
104        let end = self.clock.current();
105        self.histogram.observe_between(self.start, end);
106    }
107
108    /// Cancel the timer, preventing the duration from being recorded when dropped.
109    pub fn cancel(mut self) {
110        self.canceled = true;
111    }
112}
113
114impl<C: Clock> Drop for Timer<C> {
115    fn drop(&mut self) {
116        if self.canceled {
117            return;
118        }
119        self.observe();
120    }
121}