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
30/// Extension trait for histograms.
31pub trait HistogramExt {
32    /// Observe the duration between two points in time, in seconds.
33    ///
34    /// If the clock goes backwards, the duration is 0.
35    fn observe_between(&self, start: SystemTime, end: SystemTime);
36}
37
38impl HistogramExt for Histogram {
39    fn observe_between(&self, start: SystemTime, end: SystemTime) {
40        let duration = match end.duration_since(start) {
41            Ok(duration) => duration.as_secs_f64(),
42            Err(_) => 0.0, // Clock went backwards
43        };
44        self.observe(duration);
45    }
46}
47
48/// A wrapper around a histogram that includes a clock.
49pub struct Timed<C: Clock> {
50    /// The histogram to record durations in.
51    histogram: Histogram,
52
53    /// The clock to use for recording durations.
54    clock: Arc<C>,
55}
56
57impl<C: Clock> Timed<C> {
58    /// Create a new timed histogram.
59    pub fn new(histogram: Histogram, clock: Arc<C>) -> Self {
60        Self { histogram, clock }
61    }
62
63    /// Create a new timer that can record a duration from the current time.
64    pub fn timer(&self) -> Timer<C> {
65        let start = self.clock.current();
66        Timer {
67            histogram: self.histogram.clone(),
68            clock: self.clock.clone(), // Arc clone
69            start,
70            canceled: false,
71        }
72    }
73}
74
75/// A timer that records a duration when dropped.
76pub struct Timer<C: Clock> {
77    /// The histogram to record durations in.
78    histogram: Histogram,
79
80    /// The clock to use for recording durations.
81    clock: Arc<C>,
82
83    /// The time at which the timer was started.
84    start: SystemTime,
85
86    /// Whether the timer was canceled.
87    canceled: bool,
88}
89
90impl<C: Clock> Timer<C> {
91    /// Record the duration and cancel the timer.
92    pub fn observe(&mut self) {
93        self.canceled = true;
94        let end = self.clock.current();
95        self.histogram.observe_between(self.start, end);
96    }
97
98    /// Cancel the timer, preventing the duration from being recorded when dropped.
99    pub fn cancel(mut self) {
100        self.canceled = true;
101    }
102}
103
104impl<C: Clock> Drop for Timer<C> {
105    fn drop(&mut self) {
106        if self.canceled {
107            return;
108        }
109        self.observe();
110    }
111}