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 = end.duration_since(start).map_or(
50            // Clock went backwards
51            0.0,
52            |duration| duration.as_secs_f64(),
53        );
54        self.observe(duration);
55    }
56}
57
58/// A wrapper around a histogram that includes a clock.
59#[derive(Clone)]
60pub struct Timed<C: Clock> {
61    /// The histogram to record durations in.
62    histogram: Histogram,
63
64    /// The clock to use for recording durations.
65    clock: Arc<C>,
66}
67
68impl<C: Clock> Timed<C> {
69    /// Create a new timed histogram.
70    pub const fn new(histogram: Histogram, clock: Arc<C>) -> Self {
71        Self { histogram, clock }
72    }
73
74    /// Create a new timer that can record a duration from the current time.
75    pub fn timer(&self) -> Timer<C> {
76        let start = self.clock.current();
77        Timer {
78            histogram: self.histogram.clone(),
79            clock: self.clock.clone(), // Arc clone
80            start,
81            canceled: false,
82        }
83    }
84
85    /// Time an operation, recording only if it returns `Some`.
86    pub fn time_some<T, F: FnOnce() -> Option<T>>(&self, f: F) -> Option<T> {
87        let start = self.clock.current();
88        let result = f();
89        if result.is_some() {
90            self.histogram.observe_between(start, self.clock.current());
91        }
92        result
93    }
94}
95
96/// A timer that records a duration when dropped.
97pub struct Timer<C: Clock> {
98    /// The histogram to record durations in.
99    histogram: Histogram,
100
101    /// The clock to use for recording durations.
102    clock: Arc<C>,
103
104    /// The time at which the timer was started.
105    start: SystemTime,
106
107    /// Whether the timer was canceled.
108    canceled: bool,
109}
110
111impl<C: Clock> Timer<C> {
112    /// Record the duration and cancel the timer.
113    pub fn observe(&mut self) {
114        self.canceled = true;
115        let end = self.clock.current();
116        self.histogram.observe_between(self.start, end);
117    }
118
119    /// Cancel the timer, preventing the duration from being recorded when dropped.
120    pub fn cancel(mut self) {
121        self.canceled = true;
122    }
123}
124
125impl<C: Clock> Drop for Timer<C> {
126    fn drop(&mut self) {
127        if self.canceled {
128            return;
129        }
130        self.observe();
131    }
132}