commonware_runtime/telemetry/
histogram.rs

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