1 use hdrhistogram::Histogram;
2 use std::time::Duration;
3 use tokio::time::Instant;
4 use tracing::trace;
5 
6 /// This represents a "rotating" histogram which stores two histogram, one which
7 /// should be read and one which should be written to.  Every period, the read
8 /// histogram is discarded and replaced by the write histogram.  The idea here
9 /// is that the read histogram should always contain a full period (the previous
10 /// period) of write operations.
11 #[derive(Debug)]
12 pub struct RotatingHistogram {
13     read: Histogram<u64>,
14     write: Histogram<u64>,
15     last_rotation: Instant,
16     period: Duration,
17 }
18 
19 impl RotatingHistogram {
new(period: Duration) -> RotatingHistogram20     pub fn new(period: Duration) -> RotatingHistogram {
21         RotatingHistogram {
22             // Use an auto-resizing histogram to avoid choosing
23             // a maximum latency bound for all users.
24             read: Histogram::<u64>::new(3).expect("Invalid histogram params"),
25             write: Histogram::<u64>::new(3).expect("Invalid histogram params"),
26             last_rotation: Instant::now(),
27             period,
28         }
29     }
30 
read(&mut self) -> &mut Histogram<u64>31     pub fn read(&mut self) -> &mut Histogram<u64> {
32         self.maybe_rotate();
33         &mut self.read
34     }
35 
write(&mut self) -> &mut Histogram<u64>36     pub fn write(&mut self) -> &mut Histogram<u64> {
37         self.maybe_rotate();
38         &mut self.write
39     }
40 
maybe_rotate(&mut self)41     fn maybe_rotate(&mut self) {
42         let delta = Instant::now().saturating_duration_since(self.last_rotation);
43         // TODO: replace with delta.duration_div when it becomes stable.
44         let rotations = (nanos(delta) / nanos(self.period)) as u32;
45         if rotations >= 2 {
46             trace!("Time since last rotation is {:?}.  clearing!", delta);
47             self.clear();
48         } else if rotations == 1 {
49             trace!("Time since last rotation is {:?}. rotating!", delta);
50             self.rotate();
51         }
52         self.last_rotation += self.period * rotations;
53     }
54 
rotate(&mut self)55     fn rotate(&mut self) {
56         std::mem::swap(&mut self.read, &mut self.write);
57         trace!("Rotated {:?} points into read", self.read.len());
58         self.write.clear();
59     }
60 
clear(&mut self)61     fn clear(&mut self) {
62         self.read.clear();
63         self.write.clear();
64     }
65 }
66 
67 const NANOS_PER_SEC: u64 = 1_000_000_000;
nanos(duration: Duration) -> u6468 fn nanos(duration: Duration) -> u64 {
69     duration
70         .as_secs()
71         .saturating_mul(NANOS_PER_SEC)
72         .saturating_add(u64::from(duration.subsec_nanos()))
73 }
74