use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::{Mutex, MutexGuard};
static OPENS: AtomicU32 = AtomicU32::new(0);
static FLUSHES: AtomicU32 = AtomicU32::new(0);
static SYNCS: AtomicU32 = AtomicU32::new(0);
static WATCH: Mutex<Option<PathBuf>> = Mutex::new(None);
static SERIALISE: Mutex<()> = Mutex::new(());
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct WalIoCounts {
pub opens: u32,
pub flushes: u32,
pub syncs: u32,
}
fn watched(wal_path: &Path) -> bool {
WATCH
.lock()
.map(|w| w.as_deref() == Some(wal_path))
.unwrap_or(false)
}
pub(super) fn record_open(wal_path: &Path, _context: &str) {
if watched(wal_path) {
OPENS.fetch_add(1, Ordering::Relaxed);
}
}
pub(super) fn record_flush(wal_path: &Path, _context: &str) {
if watched(wal_path) {
FLUSHES.fetch_add(1, Ordering::Relaxed);
}
}
pub(super) fn record_sync(wal_path: &Path, _context: &str) {
if watched(wal_path) {
SYNCS.fetch_add(1, Ordering::Relaxed);
}
}
struct Watch<'a>(#[allow(dead_code)] MutexGuard<'a, ()>);
impl Drop for Watch<'_> {
fn drop(&mut self) {
if let Ok(mut w) = WATCH.lock() {
*w = None;
}
}
}
pub(crate) fn count_wal_io<T>(wal_path: &Path, f: impl FnOnce() -> T) -> (T, WalIoCounts) {
let guard = SERIALISE
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
if let Ok(mut w) = WATCH.lock() {
*w = Some(wal_path.to_path_buf());
}
OPENS.store(0, Ordering::Relaxed);
FLUSHES.store(0, Ordering::Relaxed);
SYNCS.store(0, Ordering::Relaxed);
let _watch = Watch(guard);
let out = f();
let counts = WalIoCounts {
opens: OPENS.load(Ordering::Relaxed),
flushes: FLUSHES.load(Ordering::Relaxed),
syncs: SYNCS.load(Ordering::Relaxed),
};
(out, counts)
}