2019-04-27 09:32:26 -07:00
|
|
|
use hdrhistogram::Histogram;
|
2019-12-05 11:21:47 -08:00
|
|
|
use std::time::Duration;
|
|
|
|
use tokio::time::Instant;
|
2020-03-31 10:10:47 -07:00
|
|
|
use tracing::trace;
|
2019-04-27 09:32:26 -07:00
|
|
|
|
|
|
|
/// This represents a "rotating" histogram which stores two histogram, one which
|
|
|
|
/// should be read and one which should be written to. Every period, the read
|
|
|
|
/// histogram is discarded and replaced by the write histogram. The idea here
|
|
|
|
/// is that the read histogram should always contain a full period (the previous
|
|
|
|
/// period) of write operations.
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct RotatingHistogram {
|
|
|
|
read: Histogram<u64>,
|
|
|
|
write: Histogram<u64>,
|
|
|
|
last_rotation: Instant,
|
|
|
|
period: Duration,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl RotatingHistogram {
|
|
|
|
pub fn new(period: Duration) -> RotatingHistogram {
|
|
|
|
RotatingHistogram {
|
|
|
|
read: Histogram::<u64>::new_with_bounds(1, 10_000, 3)
|
|
|
|
.expect("Invalid histogram params"),
|
|
|
|
write: Histogram::<u64>::new_with_bounds(1, 10_000, 3)
|
|
|
|
.expect("Invalid histogram params"),
|
2019-12-05 11:21:47 -08:00
|
|
|
last_rotation: Instant::now(),
|
2019-04-27 09:32:26 -07:00
|
|
|
period,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn read(&mut self) -> &mut Histogram<u64> {
|
|
|
|
self.maybe_rotate();
|
|
|
|
&mut self.read
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn write(&mut self) -> &mut Histogram<u64> {
|
|
|
|
self.maybe_rotate();
|
|
|
|
&mut self.write
|
|
|
|
}
|
|
|
|
|
|
|
|
fn maybe_rotate(&mut self) {
|
2019-12-05 11:21:47 -08:00
|
|
|
let delta = Instant::now() - self.last_rotation;
|
2019-04-27 09:32:26 -07:00
|
|
|
// TODO: replace with delta.duration_div when it becomes stable.
|
|
|
|
let rotations = (nanos(delta) / nanos(self.period)) as u32;
|
|
|
|
if rotations >= 2 {
|
|
|
|
trace!("Time since last rotation is {:?}. clearing!", delta);
|
|
|
|
self.clear();
|
|
|
|
} else if rotations == 1 {
|
|
|
|
trace!("Time since last rotation is {:?}. rotating!", delta);
|
|
|
|
self.rotate();
|
|
|
|
}
|
|
|
|
self.last_rotation += self.period * rotations;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn rotate(&mut self) {
|
|
|
|
std::mem::swap(&mut self.read, &mut self.write);
|
|
|
|
trace!("Rotated {:?} points into read", self.read.len());
|
|
|
|
self.write.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
fn clear(&mut self) {
|
|
|
|
self.read.clear();
|
|
|
|
self.write.clear();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const NANOS_PER_SEC: u64 = 1_000_000_000;
|
|
|
|
fn nanos(duration: Duration) -> u64 {
|
|
|
|
duration
|
|
|
|
.as_secs()
|
|
|
|
.saturating_mul(NANOS_PER_SEC)
|
|
|
|
.saturating_add(u64::from(duration.subsec_nanos()))
|
|
|
|
}
|