use std::cell::RefCell;
use super::metrics::{GcKeysCF, GcKeysDetail};
use engine_rocks::PerfContext;
use engine_traits::{CF_DEFAULT, CF_LOCK, CF_WRITE};
use kvproto::kvrpcpb::{ScanDetail, ScanDetailV2, ScanInfo};
pub use raftstore::store::{FlowStatistics, FlowStatsReporter};
const STAT_PROCESSED_KEYS: &str = "processed_keys";
const STAT_GET: &str = "get";
const STAT_NEXT: &str = "next";
const STAT_PREV: &str = "prev";
const STAT_SEEK: &str = "seek";
const STAT_SEEK_FOR_PREV: &str = "seek_for_prev";
const STAT_OVER_SEEK_BOUND: &str = "over_seek_bound";
const STAT_NEXT_TOMBSTONE: &str = "next_tombstone";
const STAT_PREV_TOMBSTONE: &str = "prev_tombstone";
const STAT_SEEK_TOMBSTONE: &str = "seek_tombstone";
const STAT_SEEK_FOR_PREV_TOMBSTONE: &str = "seek_for_prev_tombstone";
const STAT_TTL_TOMBSTONE: &str = "ttl_tombstone";
thread_local! {
pub static TTL_TOMBSTONE : RefCell<usize> = RefCell::new(0);
}
pub enum StatsKind {
Next,
Prev,
Seek,
SeekForPrev,
}
pub struct StatsCollector<'a> {
stats: &'a mut CfStatistics,
kind: StatsKind,
internal_tombstone: usize,
ttl_tombstone: usize,
}
impl<'a> StatsCollector<'a> {
pub fn new(kind: StatsKind, stats: &'a mut CfStatistics) -> Self {
StatsCollector {
stats,
kind,
internal_tombstone: PerfContext::get().internal_delete_skipped_count() as usize,
ttl_tombstone: TTL_TOMBSTONE.with(|m| *m.borrow()),
}
}
}
impl Drop for StatsCollector<'_> {
fn drop(&mut self) {
self.stats.ttl_tombstone += TTL_TOMBSTONE.with(|m| *m.borrow()) - self.ttl_tombstone;
let internal_tombstone =
PerfContext::get().internal_delete_skipped_count() as usize - self.internal_tombstone;
match self.kind {
StatsKind::Next => {
self.stats.next += 1;
self.stats.next_tombstone += internal_tombstone;
}
StatsKind::Prev => {
self.stats.prev += 1;
self.stats.prev_tombstone += internal_tombstone;
}
StatsKind::Seek => {
self.stats.seek += 1;
self.stats.seek_tombstone += internal_tombstone;
}
StatsKind::SeekForPrev => {
self.stats.seek_for_prev += 1;
self.stats.seek_for_prev_tombstone += internal_tombstone;
}
}
}
}
#[derive(Default, Clone, Debug)]
pub struct CfStatistics {
pub processed_keys: usize,
pub get: usize,
pub next: usize,
pub prev: usize,
pub seek: usize,
pub seek_for_prev: usize,
pub over_seek_bound: usize,
pub flow_stats: FlowStatistics,
pub next_tombstone: usize,
pub prev_tombstone: usize,
pub seek_tombstone: usize,
pub seek_for_prev_tombstone: usize,
pub ttl_tombstone: usize,
}
const STATS_COUNT: usize = 12;
impl CfStatistics {
#[inline]
pub fn total_op_count(&self) -> usize {
self.get + self.next + self.prev + self.seek + self.seek_for_prev
}
pub fn details(&self) -> [(&'static str, usize); STATS_COUNT] {
[
(STAT_PROCESSED_KEYS, self.processed_keys),
(STAT_GET, self.get),
(STAT_NEXT, self.next),
(STAT_PREV, self.prev),
(STAT_SEEK, self.seek),
(STAT_SEEK_FOR_PREV, self.seek_for_prev),
(STAT_OVER_SEEK_BOUND, self.over_seek_bound),
(STAT_NEXT_TOMBSTONE, self.next_tombstone),
(STAT_PREV_TOMBSTONE, self.prev_tombstone),
(STAT_SEEK_TOMBSTONE, self.seek_tombstone),
(STAT_SEEK_FOR_PREV_TOMBSTONE, self.seek_for_prev_tombstone),
(STAT_TTL_TOMBSTONE, self.ttl_tombstone),
]
}
pub fn details_enum(&self) -> [(GcKeysDetail, usize); STATS_COUNT] {
[
(GcKeysDetail::processed_keys, self.processed_keys),
(GcKeysDetail::get, self.get),
(GcKeysDetail::next, self.next),
(GcKeysDetail::prev, self.prev),
(GcKeysDetail::seek, self.seek),
(GcKeysDetail::seek_for_prev, self.seek_for_prev),
(GcKeysDetail::over_seek_bound, self.over_seek_bound),
(GcKeysDetail::next_tombstone, self.next_tombstone),
(GcKeysDetail::prev_tombstone, self.prev_tombstone),
(GcKeysDetail::seek_tombstone, self.seek_tombstone),
(
GcKeysDetail::seek_for_prev_tombstone,
self.seek_for_prev_tombstone,
),
(GcKeysDetail::ttl_tombstone, self.ttl_tombstone),
]
}
pub fn add(&mut self, other: &Self) {
self.processed_keys = self.processed_keys.saturating_add(other.processed_keys);
self.get = self.get.saturating_add(other.get);
self.next = self.next.saturating_add(other.next);
self.prev = self.prev.saturating_add(other.prev);
self.seek = self.seek.saturating_add(other.seek);
self.seek_for_prev = self.seek_for_prev.saturating_add(other.seek_for_prev);
self.over_seek_bound = self.over_seek_bound.saturating_add(other.over_seek_bound);
self.flow_stats.add(&other.flow_stats);
self.next_tombstone = self.next_tombstone.saturating_add(other.next_tombstone);
self.prev_tombstone = self.prev_tombstone.saturating_add(other.prev_tombstone);
self.seek_tombstone = self.seek_tombstone.saturating_add(other.seek_tombstone);
self.seek_for_prev_tombstone = self
.seek_for_prev_tombstone
.saturating_add(other.seek_for_prev_tombstone);
self.ttl_tombstone = self.ttl_tombstone.saturating_add(other.ttl_tombstone);
}
pub fn scan_info(&self) -> ScanInfo {
let mut info = ScanInfo::default();
info.set_processed(self.processed_keys as i64);
info.set_total(self.total_op_count() as i64);
info
}
}
#[derive(Default, Clone, Debug)]
pub struct Statistics {
pub lock: CfStatistics,
pub write: CfStatistics,
pub data: CfStatistics,
}
impl Statistics {
pub fn details(&self) -> [(&'static str, [(&'static str, usize); STATS_COUNT]); 3] {
[
(CF_DEFAULT, self.data.details()),
(CF_LOCK, self.lock.details()),
(CF_WRITE, self.write.details()),
]
}
pub fn details_enum(&self) -> [(GcKeysCF, [(GcKeysDetail, usize); STATS_COUNT]); 3] {
[
(GcKeysCF::default, self.data.details_enum()),
(GcKeysCF::lock, self.lock.details_enum()),
(GcKeysCF::write, self.write.details_enum()),
]
}
pub fn add(&mut self, other: &Self) {
self.lock.add(&other.lock);
self.write.add(&other.write);
self.data.add(&other.data);
}
pub fn scan_detail(&self) -> ScanDetail {
let mut detail = ScanDetail::default();
detail.set_data(self.data.scan_info());
detail.set_lock(self.lock.scan_info());
detail.set_write(self.write.scan_info());
detail
}
pub fn mut_cf_statistics(&mut self, cf: &str) -> &mut CfStatistics {
if cf.is_empty() {
return &mut self.data;
}
match cf {
CF_DEFAULT => &mut self.data,
CF_LOCK => &mut self.lock,
CF_WRITE => &mut self.write,
_ => unreachable!(),
}
}
pub fn write_scan_detail(&self, detail_v2: &mut ScanDetailV2) {
detail_v2.set_processed_versions(self.write.processed_keys as u64);
detail_v2.set_total_versions(self.write.total_op_count() as u64);
}
}
#[derive(Default, Debug)]
pub struct StatisticsSummary {
pub stat: Statistics,
pub count: u64,
}
impl StatisticsSummary {
pub fn add_statistics(&mut self, v: &Statistics) {
self.stat.add(v);
self.count += 1;
}
}