use std::sync::atomic::{AtomicU64, Ordering};
use std::time::Instant;
use rust_rocksdb::{IteratorMode, WriteBatch};
use super::RocksDb as DB;
use crate::error::{DbError, DbResult};
static CF_OP_COUNT: AtomicU64 = AtomicU64::new(0);
static CF_OP_NANOS: AtomicU64 = AtomicU64::new(0);
static CF_REUSE_COUNT: AtomicU64 = AtomicU64::new(0);
static AUTO_CREATE_COUNT: AtomicU64 = AtomicU64::new(0);
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct CfOpSnapshot {
pub ops: u64,
pub nanos: u64,
}
pub fn snapshot() -> CfOpSnapshot {
CfOpSnapshot {
ops: CF_OP_COUNT.load(Ordering::Relaxed),
nanos: CF_OP_NANOS.load(Ordering::Relaxed),
}
}
impl CfOpSnapshot {
pub fn ops_since(&self, later: &CfOpSnapshot) -> u64 {
later.ops.saturating_sub(self.ops)
}
pub fn ms_since(&self, later: &CfOpSnapshot) -> f64 {
later.nanos.saturating_sub(self.nanos) as f64 / 1_000_000.0
}
}
pub fn timed<R>(op: impl FnOnce() -> R) -> R {
let start = Instant::now();
let result = op();
CF_OP_COUNT.fetch_add(1, Ordering::Relaxed);
CF_OP_NANOS.fetch_add(start.elapsed().as_nanos() as u64, Ordering::Relaxed);
result
}
pub fn reuses() -> u64 {
CF_REUSE_COUNT.load(Ordering::Relaxed)
}
pub fn record_reuse() {
CF_REUSE_COUNT.fetch_add(1, Ordering::Relaxed);
}
pub fn autocreates() -> u64 {
AUTO_CREATE_COUNT.load(Ordering::Relaxed)
}
pub fn record_autocreate() {
AUTO_CREATE_COUNT.fetch_add(1, Ordering::Relaxed);
}
pub fn auto_create_enabled() -> bool {
!matches!(
std::env::var("SOLIDB_AUTO_CREATE_COLLECTIONS")
.unwrap_or_default()
.to_ascii_lowercase()
.as_str(),
"0" | "false" | "off" | "no"
)
}
pub fn wipe_cf(db: &DB, cf_name: &str) -> DbResult<()> {
let cf = db
.cf_handle(cf_name)
.ok_or_else(|| DbError::InternalError(format!("column family '{cf_name}' missing")))?;
let first = db
.iterator_cf(&cf, IteratorMode::Start)
.next()
.and_then(|r| r.ok())
.map(|(k, _)| k);
let last = db
.iterator_cf(&cf, IteratorMode::End)
.next()
.and_then(|r| r.ok())
.map(|(k, _)| k);
if let (Some(first), Some(last)) = (first, last) {
let mut end = last.to_vec();
end.push(0);
let mut batch = WriteBatch::default();
batch.delete_range_cf(&cf, first.as_ref(), end.as_slice());
db.write(&batch).map_err(|e| {
DbError::InternalError(format!("Failed to wipe column family '{cf_name}': {e}"))
})?;
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn timed_accumulates_count_and_duration() {
let before = snapshot();
timed(|| std::thread::sleep(std::time::Duration::from_millis(5)));
timed(|| ());
let after = snapshot();
assert!(before.ops_since(&after) >= 2);
assert!(before.ms_since(&after) >= 5.0);
}
}