use std::{
sync::atomic::{AtomicIsize, AtomicUsize, Ordering},
time::Duration,
};
use fastant::{Atomic, Instant};
use crate::Throttle;
#[derive(Debug)]
struct Metric {
value: AtomicUsize,
throttle: f64,
quota: AtomicIsize,
update: Atomic,
}
impl Metric {
fn new(throttle: f64) -> Self {
Self {
value: AtomicUsize::new(0),
throttle,
quota: AtomicIsize::new(0),
update: Atomic::new(Instant::now()),
}
}
fn load(&self) -> usize {
self.value.load(Ordering::Relaxed)
}
fn record(&self, value: usize) {
self.value.fetch_add(value, Ordering::Relaxed);
if self.throttle != 0.0 {
self.quota.fetch_sub(value as _, Ordering::Relaxed);
}
}
fn throttle(&self) -> Duration {
if self.throttle == 0.0 {
return Duration::ZERO;
}
let now = Instant::now();
let update = self.update.load(Ordering::Relaxed);
let dur = now.duration_since(update).as_secs_f64();
let fill = dur * self.throttle;
let quota = f64::min(self.throttle, self.quota.load(Ordering::Relaxed) as f64 + fill);
self.update.store(now, Ordering::Relaxed);
self.quota.store(quota as isize, Ordering::Relaxed);
if quota >= 0.0 {
Duration::ZERO
} else {
Duration::from_secs_f64(-quota / self.throttle)
}
}
}
#[derive(Debug)]
pub struct Statistics {
throttle: Throttle,
disk_write_bytes: Metric,
disk_read_bytes: Metric,
disk_write_ios: Metric,
disk_read_ios: Metric,
}
impl Statistics {
pub fn new(throttle: Throttle) -> Self {
let disk_write_bytes = Metric::new(throttle.write_throughput.map(|v| v.get()).unwrap_or_default() as f64);
let disk_read_bytes = Metric::new(throttle.read_throughput.map(|v| v.get()).unwrap_or_default() as f64);
let disk_write_ios = Metric::new(throttle.write_iops.map(|v| v.get()).unwrap_or_default() as f64);
let disk_read_ios = Metric::new(throttle.read_iops.map(|v| v.get()).unwrap_or_default() as f64);
Self {
throttle,
disk_write_bytes,
disk_read_bytes,
disk_write_ios,
disk_read_ios,
}
}
pub fn disk_write_bytes(&self) -> usize {
self.disk_write_bytes.load()
}
pub fn disk_read_bytes(&self) -> usize {
self.disk_read_bytes.load()
}
pub fn disk_write_ios(&self) -> usize {
self.disk_write_ios.load()
}
pub fn disk_read_ios(&self) -> usize {
self.disk_read_ios.load()
}
pub fn record_disk_write(&self, bytes: usize) {
self.disk_write_bytes.record(bytes);
self.disk_write_ios.record(self.throttle.iops_counter.count(bytes));
}
pub fn record_disk_read(&self, bytes: usize) {
self.disk_read_bytes.record(bytes);
self.disk_read_ios.record(self.throttle.iops_counter.count(bytes));
}
pub fn read_throttle(&self) -> Duration {
std::cmp::max(self.disk_read_bytes.throttle(), self.disk_read_ios.throttle())
}
pub fn write_throttle(&self) -> Duration {
std::cmp::max(self.disk_write_bytes.throttle(), self.disk_write_ios.throttle())
}
pub fn is_read_throttled(&self) -> bool {
self.read_throttle() > Duration::ZERO
}
pub fn is_write_throttled(&self) -> bool {
self.write_throttle() > Duration::ZERO
}
pub fn throttle(&self) -> &Throttle {
&self.throttle
}
}