Skip to main content

rocksdb/
perf.rs

1// Copyright 2020 Tran Tuan Linh
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use libc::{c_int, c_uchar};
16use std::marker::PhantomData;
17
18use crate::ffi_util::from_cstr_and_free;
19use crate::{db::DBInner, ffi, Cache, Error};
20use crate::{DBCommon, ThreadMode, TransactionDB, DB};
21
22#[derive(Debug, Copy, Clone, PartialEq, Eq)]
23#[repr(i32)]
24pub enum PerfStatsLevel {
25    /// Unknown settings
26    Uninitialized = 0,
27    /// Disable perf stats
28    Disable,
29    /// Enables only count stats
30    EnableCount,
31    /// Count stats and enable time stats except for mutexes
32    EnableTimeExceptForMutex,
33    /// Other than time, also measure CPU time counters. Still don't measure
34    /// time (neither wall time nor CPU time) for mutexes
35    EnableTimeAndCPUTimeExceptForMutex,
36    /// Enables count and time stats
37    EnableTime,
38    /// N.B must always be the last value!
39    OutOfBound,
40}
41
42#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
43#[non_exhaustive]
44#[repr(i32)]
45pub enum PerfMetric {
46    UserKeyComparisonCount = 0,
47    BlockCacheHitCount = 1,
48    BlockReadCount = 2,
49    BlockReadByte = 3,
50    BlockReadTime = 4,
51    BlockChecksumTime = 5,
52    BlockDecompressTime = 6,
53    GetReadBytes = 7,
54    MultigetReadBytes = 8,
55    IterReadBytes = 9,
56    InternalKeySkippedCount = 10,
57    InternalDeleteSkippedCount = 11,
58    InternalRecentSkippedCount = 12,
59    InternalMergeCount = 13,
60    GetSnapshotTime = 14,
61    GetFromMemtableTime = 15,
62    GetFromMemtableCount = 16,
63    GetPostProcessTime = 17,
64    GetFromOutputFilesTime = 18,
65    SeekOnMemtableTime = 19,
66    SeekOnMemtableCount = 20,
67    NextOnMemtableCount = 21,
68    PrevOnMemtableCount = 22,
69    SeekChildSeekTime = 23,
70    SeekChildSeekCount = 24,
71    SeekMinHeapTime = 25,
72    SeekMaxHeapTime = 26,
73    SeekInternalSeekTime = 27,
74    FindNextUserEntryTime = 28,
75    WriteWalTime = 29,
76    WriteMemtableTime = 30,
77    WriteDelayTime = 31,
78    WritePreAndPostProcessTime = 32,
79    DbMutexLockNanos = 33,
80    DbConditionWaitNanos = 34,
81    MergeOperatorTimeNanos = 35,
82    ReadIndexBlockNanos = 36,
83    ReadFilterBlockNanos = 37,
84    NewTableBlockIterNanos = 38,
85    NewTableIteratorNanos = 39,
86    BlockSeekNanos = 40,
87    FindTableNanos = 41,
88    BloomMemtableHitCount = 42,
89    BloomMemtableMissCount = 43,
90    BloomSstHitCount = 44,
91    BloomSstMissCount = 45,
92    KeyLockWaitTime = 46,
93    KeyLockWaitCount = 47,
94    EnvNewSequentialFileNanos = 48,
95    EnvNewRandomAccessFileNanos = 49,
96    EnvNewWritableFileNanos = 50,
97    EnvReuseWritableFileNanos = 51,
98    EnvNewRandomRwFileNanos = 52,
99    EnvNewDirectoryNanos = 53,
100    EnvFileExistsNanos = 54,
101    EnvGetChildrenNanos = 55,
102    EnvGetChildrenFileAttributesNanos = 56,
103    EnvDeleteFileNanos = 57,
104    EnvCreateDirNanos = 58,
105    EnvCreateDirIfMissingNanos = 59,
106    EnvDeleteDirNanos = 60,
107    EnvGetFileSizeNanos = 61,
108    EnvGetFileModificationTimeNanos = 62,
109    EnvRenameFileNanos = 63,
110    EnvLinkFileNanos = 64,
111    EnvLockFileNanos = 65,
112    EnvUnlockFileNanos = 66,
113    EnvNewLoggerNanos = 67,
114    TotalMetricCount = 68,
115}
116
117/// Sets the perf stats level for current thread.
118pub fn set_perf_stats(lvl: PerfStatsLevel) {
119    unsafe {
120        ffi::rocksdb_set_perf_level(lvl as c_int);
121    }
122}
123
124/// Thread local context for gathering performance counter efficiently
125/// and transparently.
126pub struct PerfContext {
127    pub(crate) inner: *mut ffi::rocksdb_perfcontext_t,
128}
129
130impl Default for PerfContext {
131    fn default() -> Self {
132        let ctx = unsafe { ffi::rocksdb_perfcontext_create() };
133        assert!(!ctx.is_null(), "Could not create Perf Context");
134
135        Self { inner: ctx }
136    }
137}
138
139impl Drop for PerfContext {
140    fn drop(&mut self) {
141        unsafe {
142            ffi::rocksdb_perfcontext_destroy(self.inner);
143        }
144    }
145}
146
147impl PerfContext {
148    /// Reset context
149    pub fn reset(&mut self) {
150        unsafe {
151            ffi::rocksdb_perfcontext_reset(self.inner);
152        }
153    }
154
155    /// Get the report on perf
156    pub fn report(&self, exclude_zero_counters: bool) -> String {
157        unsafe {
158            let ptr =
159                ffi::rocksdb_perfcontext_report(self.inner, c_uchar::from(exclude_zero_counters));
160            from_cstr_and_free(ptr)
161        }
162    }
163
164    /// Returns value of a metric
165    pub fn metric(&self, id: PerfMetric) -> u64 {
166        unsafe { ffi::rocksdb_perfcontext_metric(self.inner, id as c_int) }
167    }
168}
169
170/// Memory usage stats
171pub struct MemoryUsageStats {
172    /// Approximate memory usage of all the mem-tables
173    pub mem_table_total: u64,
174    /// Approximate memory usage of un-flushed mem-tables
175    pub mem_table_unflushed: u64,
176    /// Approximate memory usage of all the table readers
177    pub mem_table_readers_total: u64,
178    /// Approximate memory usage by cache
179    pub cache_total: u64,
180}
181
182/// Wrap over memory_usage_t. Hold current memory usage of the specified DB instances and caches
183pub struct MemoryUsage {
184    inner: *mut ffi::rocksdb_memory_usage_t,
185}
186
187impl Drop for MemoryUsage {
188    fn drop(&mut self) {
189        unsafe {
190            ffi::rocksdb_approximate_memory_usage_destroy(self.inner);
191        }
192    }
193}
194
195impl MemoryUsage {
196    /// Approximate memory usage of all the mem-tables
197    pub fn approximate_mem_table_total(&self) -> u64 {
198        unsafe { ffi::rocksdb_approximate_memory_usage_get_mem_table_total(self.inner) }
199    }
200
201    /// Approximate memory usage of un-flushed mem-tables
202    pub fn approximate_mem_table_unflushed(&self) -> u64 {
203        unsafe { ffi::rocksdb_approximate_memory_usage_get_mem_table_unflushed(self.inner) }
204    }
205
206    /// Approximate memory usage of all the table readers
207    pub fn approximate_mem_table_readers_total(&self) -> u64 {
208        unsafe { ffi::rocksdb_approximate_memory_usage_get_mem_table_readers_total(self.inner) }
209    }
210
211    /// Approximate memory usage by cache
212    pub fn approximate_cache_total(&self) -> u64 {
213        unsafe { ffi::rocksdb_approximate_memory_usage_get_cache_total(self.inner) }
214    }
215}
216
217/// Creates [`MemoryUsage`] from DBs and caches.
218///
219/// Most users should call [`get_memory_usage_stats`] instead.
220pub struct MemoryUsageBuilder<'a> {
221    inner: *mut ffi::rocksdb_memory_consumers_t,
222    base_dbs: Vec<*mut ffi::rocksdb_t>,
223    // must not outlive the DBs/caches that are added
224    _marker: PhantomData<&'a ()>,
225}
226
227impl Drop for MemoryUsageBuilder<'_> {
228    fn drop(&mut self) {
229        unsafe {
230            ffi::rocksdb_memory_consumers_destroy(self.inner);
231        }
232        for base_db in &self.base_dbs {
233            unsafe {
234                ffi::rocksdb_transactiondb_close_base_db(*base_db);
235            }
236        }
237    }
238}
239
240impl<'a> MemoryUsageBuilder<'a> {
241    /// Create new instance
242    pub fn new() -> Result<Self, Error> {
243        let mc = unsafe { ffi::rocksdb_memory_consumers_create() };
244        if mc.is_null() {
245            Err(Error::new(
246                "Could not create MemoryUsage builder".to_owned(),
247            ))
248        } else {
249            Ok(Self {
250                inner: mc,
251                base_dbs: Vec::new(),
252                _marker: PhantomData,
253            })
254        }
255    }
256
257    /// Add a DB instance to collect memory usage from it and add up in total stats
258    pub fn add_tx_db<T: ThreadMode>(&mut self, db: &'a TransactionDB<T>) {
259        unsafe {
260            let base_db = ffi::rocksdb_transactiondb_get_base_db(db.inner);
261            ffi::rocksdb_memory_consumers_add_db(self.inner, base_db);
262            // rocksdb_transactiondb_get_base_db allocates a struct that must be freed
263            self.base_dbs.push(base_db);
264        }
265    }
266
267    /// Add a DB instance to collect memory usage from it and add up in total stats
268    pub fn add_db<T: ThreadMode, D: DBInner>(&mut self, db: &'a DBCommon<T, D>) {
269        unsafe {
270            ffi::rocksdb_memory_consumers_add_db(self.inner, db.inner.inner());
271        }
272    }
273
274    /// Add a cache to collect memory usage from it and add up in total stats
275    pub fn add_cache(&mut self, cache: &'a Cache) {
276        unsafe {
277            ffi::rocksdb_memory_consumers_add_cache(self.inner, cache.0.inner.as_ptr());
278        }
279    }
280
281    /// Build up MemoryUsage
282    pub fn build(&self) -> Result<MemoryUsage, Error> {
283        unsafe {
284            let mu = ffi_try!(ffi::rocksdb_approximate_memory_usage_create(self.inner));
285            Ok(MemoryUsage { inner: mu })
286        }
287    }
288}
289
290/// Get memory usage stats from DB instances and Cache instances
291pub fn get_memory_usage_stats(
292    dbs: Option<&[&DB]>,
293    caches: Option<&[&Cache]>,
294) -> Result<MemoryUsageStats, Error> {
295    let mut builder = MemoryUsageBuilder::new()?;
296    if let Some(dbs_) = dbs {
297        dbs_.iter().for_each(|db| builder.add_db(db));
298    }
299    if let Some(caches_) = caches {
300        caches_.iter().for_each(|cache| builder.add_cache(cache));
301    }
302
303    let mu = builder.build()?;
304    Ok(MemoryUsageStats {
305        mem_table_total: mu.approximate_mem_table_total(),
306        mem_table_unflushed: mu.approximate_mem_table_unflushed(),
307        mem_table_readers_total: mu.approximate_mem_table_readers_total(),
308        cache_total: mu.approximate_cache_total(),
309    })
310}