use libc::{c_int, c_uchar, c_void};
use crate::{db::DBInner, ffi, ffi_util::from_cstr, Cache, Error, DB};
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[repr(i32)]
pub enum PerfStatsLevel {
Uninitialized = 0,
Disable,
EnableCount,
EnableTimeExceptForMutex,
EnableTimeAndCPUTimeExceptForMutex,
EnableTime,
OutOfBound,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[non_exhaustive]
#[repr(i32)]
pub enum PerfMetric {
UserKeyComparisonCount = 0,
BlockCacheHitCount = 1,
BlockReadCount = 2,
BlockReadByte = 3,
BlockReadTime = 4,
BlockChecksumTime = 5,
BlockDecompressTime = 6,
GetReadBytes = 7,
MultigetReadBytes = 8,
IterReadBytes = 9,
InternalKeySkippedCount = 10,
InternalDeleteSkippedCount = 11,
InternalRecentSkippedCount = 12,
InternalMergeCount = 13,
GetSnapshotTime = 14,
GetFromMemtableTime = 15,
GetFromMemtableCount = 16,
GetPostProcessTime = 17,
GetFromOutputFilesTime = 18,
SeekOnMemtableTime = 19,
SeekOnMemtableCount = 20,
NextOnMemtableCount = 21,
PrevOnMemtableCount = 22,
SeekChildSeekTime = 23,
SeekChildSeekCount = 24,
SeekMinHeapTime = 25,
SeekMaxHeapTime = 26,
SeekInternalSeekTime = 27,
FindNextUserEntryTime = 28,
WriteWalTime = 29,
WriteMemtableTime = 30,
WriteDelayTime = 31,
WritePreAndPostProcessTime = 32,
DbMutexLockNanos = 33,
DbConditionWaitNanos = 34,
MergeOperatorTimeNanos = 35,
ReadIndexBlockNanos = 36,
ReadFilterBlockNanos = 37,
NewTableBlockIterNanos = 38,
NewTableIteratorNanos = 39,
BlockSeekNanos = 40,
FindTableNanos = 41,
BloomMemtableHitCount = 42,
BloomMemtableMissCount = 43,
BloomSstHitCount = 44,
BloomSstMissCount = 45,
KeyLockWaitTime = 46,
KeyLockWaitCount = 47,
EnvNewSequentialFileNanos = 48,
EnvNewRandomAccessFileNanos = 49,
EnvNewWritableFileNanos = 50,
EnvReuseWritableFileNanos = 51,
EnvNewRandomRwFileNanos = 52,
EnvNewDirectoryNanos = 53,
EnvFileExistsNanos = 54,
EnvGetChildrenNanos = 55,
EnvGetChildrenFileAttributesNanos = 56,
EnvDeleteFileNanos = 57,
EnvCreateDirNanos = 58,
EnvCreateDirIfMissingNanos = 59,
EnvDeleteDirNanos = 60,
EnvGetFileSizeNanos = 61,
EnvGetFileModificationTimeNanos = 62,
EnvRenameFileNanos = 63,
EnvLinkFileNanos = 64,
EnvLockFileNanos = 65,
EnvUnlockFileNanos = 66,
EnvNewLoggerNanos = 67,
TotalMetricCount = 68,
}
pub fn set_perf_stats(lvl: PerfStatsLevel) {
unsafe {
ffi::rocksdb_set_perf_level(lvl as c_int);
}
}
pub struct PerfContext {
pub(crate) inner: *mut ffi::rocksdb_perfcontext_t,
}
impl Default for PerfContext {
fn default() -> Self {
let ctx = unsafe { ffi::rocksdb_perfcontext_create() };
assert!(!ctx.is_null(), "Could not create Perf Context");
Self { inner: ctx }
}
}
impl Drop for PerfContext {
fn drop(&mut self) {
unsafe {
ffi::rocksdb_perfcontext_destroy(self.inner);
}
}
}
impl PerfContext {
pub fn reset(&mut self) {
unsafe {
ffi::rocksdb_perfcontext_reset(self.inner);
}
}
pub fn report(&self, exclude_zero_counters: bool) -> String {
unsafe {
let ptr =
ffi::rocksdb_perfcontext_report(self.inner, c_uchar::from(exclude_zero_counters));
let report = from_cstr(ptr);
libc::free(ptr as *mut c_void);
report
}
}
pub fn metric(&self, id: PerfMetric) -> u64 {
unsafe { ffi::rocksdb_perfcontext_metric(self.inner, id as c_int) }
}
}
pub struct MemoryUsageStats {
pub mem_table_total: u64,
pub mem_table_unflushed: u64,
pub mem_table_readers_total: u64,
pub cache_total: u64,
}
struct MemoryUsage {
inner: *mut ffi::rocksdb_memory_usage_t,
}
impl Drop for MemoryUsage {
fn drop(&mut self) {
unsafe {
ffi::rocksdb_approximate_memory_usage_destroy(self.inner);
}
}
}
impl MemoryUsage {
fn approximate_mem_table_total(&self) -> u64 {
unsafe { ffi::rocksdb_approximate_memory_usage_get_mem_table_total(self.inner) }
}
fn approximate_mem_table_unflushed(&self) -> u64 {
unsafe { ffi::rocksdb_approximate_memory_usage_get_mem_table_unflushed(self.inner) }
}
fn approximate_mem_table_readers_total(&self) -> u64 {
unsafe { ffi::rocksdb_approximate_memory_usage_get_mem_table_readers_total(self.inner) }
}
fn approximate_cache_total(&self) -> u64 {
unsafe { ffi::rocksdb_approximate_memory_usage_get_cache_total(self.inner) }
}
}
struct MemoryUsageBuilder {
inner: *mut ffi::rocksdb_memory_consumers_t,
}
impl Drop for MemoryUsageBuilder {
fn drop(&mut self) {
unsafe {
ffi::rocksdb_memory_consumers_destroy(self.inner);
}
}
}
impl MemoryUsageBuilder {
fn new() -> Result<Self, Error> {
let mc = unsafe { ffi::rocksdb_memory_consumers_create() };
if mc.is_null() {
Err(Error::new(
"Could not create MemoryUsage builder".to_owned(),
))
} else {
Ok(Self { inner: mc })
}
}
fn add_db(&mut self, db: &DB) {
unsafe {
ffi::rocksdb_memory_consumers_add_db(self.inner, db.inner.inner());
}
}
fn add_cache(&mut self, cache: &Cache) {
unsafe {
ffi::rocksdb_memory_consumers_add_cache(self.inner, cache.0.inner.as_ptr());
}
}
fn build(&self) -> Result<MemoryUsage, Error> {
unsafe {
let mu = ffi_try!(ffi::rocksdb_approximate_memory_usage_create(self.inner));
Ok(MemoryUsage { inner: mu })
}
}
}
pub fn get_memory_usage_stats(
dbs: Option<&[&DB]>,
caches: Option<&[&Cache]>,
) -> Result<MemoryUsageStats, Error> {
let mut builder = MemoryUsageBuilder::new()?;
if let Some(dbs_) = dbs {
dbs_.iter().for_each(|db| builder.add_db(db));
}
if let Some(caches_) = caches {
caches_.iter().for_each(|cache| builder.add_cache(cache));
}
let mu = builder.build()?;
Ok(MemoryUsageStats {
mem_table_total: mu.approximate_mem_table_total(),
mem_table_unflushed: mu.approximate_mem_table_unflushed(),
mem_table_readers_total: mu.approximate_mem_table_readers_total(),
cache_total: mu.approximate_cache_total(),
})
}