use std::sync::atomic::{AtomicU64, Ordering};
pub(crate) const MAX_LEVELS: usize = 32;
pub struct DbStats {
pub bytes_written: AtomicU64,
pub bytes_read: AtomicU64,
pub compactions_completed: AtomicU64,
pub compaction_bytes_written: AtomicU64,
pub flushes_completed: AtomicU64,
pub block_cache_hits: AtomicU64,
pub block_cache_misses: AtomicU64,
pub read_level_samples: [AtomicU64; MAX_LEVELS],
pub read_sample_counter: AtomicU64,
}
impl DbStats {
pub fn new() -> Self {
Self {
bytes_written: AtomicU64::new(0),
bytes_read: AtomicU64::new(0),
compactions_completed: AtomicU64::new(0),
compaction_bytes_written: AtomicU64::new(0),
flushes_completed: AtomicU64::new(0),
block_cache_hits: AtomicU64::new(0),
block_cache_misses: AtomicU64::new(0),
read_level_samples: std::array::from_fn(|_| AtomicU64::new(0)),
read_sample_counter: AtomicU64::new(0),
}
}
pub fn record_write(&self, bytes: u64) {
self.bytes_written.fetch_add(bytes, Ordering::Relaxed);
}
pub fn record_read(&self, bytes: u64) {
self.bytes_read.fetch_add(bytes, Ordering::Relaxed);
}
pub fn record_compaction_bytes(&self, bytes_written: u64) {
self.compaction_bytes_written
.fetch_add(bytes_written, Ordering::Relaxed);
}
pub fn record_compaction_completed(&self) {
self.compactions_completed.fetch_add(1, Ordering::Relaxed);
}
pub fn record_flush(&self) {
self.flushes_completed.fetch_add(1, Ordering::Relaxed);
}
pub fn record_cache_hit(&self) {
self.block_cache_hits.fetch_add(1, Ordering::Relaxed);
}
pub fn record_cache_miss(&self) {
self.block_cache_misses.fetch_add(1, Ordering::Relaxed);
}
pub fn maybe_sample_read_level(&self, level: usize) {
let count = self.read_sample_counter.fetch_add(1, Ordering::Relaxed);
if count.is_multiple_of(16) && level < self.read_level_samples.len() {
self.read_level_samples[level].fetch_add(1, Ordering::Relaxed);
}
}
pub fn take_read_level_samples(&self) -> [u64; MAX_LEVELS] {
let mut result = [0u64; MAX_LEVELS];
for (i, slot) in result.iter_mut().enumerate() {
*slot = self.read_level_samples[i].swap(0, Ordering::Relaxed);
}
result
}
}
impl Default for DbStats {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_stats_basic() {
let stats = DbStats::new();
stats.record_write(100);
stats.record_write(200);
assert_eq!(stats.bytes_written.load(Ordering::Relaxed), 300);
stats.record_compaction_bytes(1000);
stats.record_compaction_completed();
assert_eq!(stats.compactions_completed.load(Ordering::Relaxed), 1);
assert_eq!(stats.compaction_bytes_written.load(Ordering::Relaxed), 1000);
}
}