use crate::layout::BlobGuid;
use crate::layout::{DATA_AREA_START, PAGE_SIZE};
#[derive(Debug, Clone, Copy)]
pub struct BlobStats {
pub guid: BlobGuid,
pub space_used: u32,
pub gap_space: u32,
pub num_slots: u16,
pub num_ext_blobs: u16,
pub compact_times: u32,
pub tombstone_leaf_cnt: u32,
}
#[derive(Debug, Clone, Copy, Default)]
pub struct RouteCacheStats {
pub entries: usize,
pub hits: u64,
pub misses: u64,
pub learns: u64,
pub evictions: u64,
pub invalidations: u64,
}
#[derive(Debug, Clone, Copy, Default)]
pub struct StoreStats {
pub live_blobs: usize,
pub live_slots: u64,
pub next_slot: u64,
pub reusable_slots: u64,
pub pending_free_slots: u64,
pub data_file_bytes: u64,
pub data_allocated_bytes: u64,
pub data_high_water_bytes: u64,
pub read_index_file_bytes: u64,
pub read_index_allocated_bytes: u64,
pub read_index_high_water_bytes: u64,
pub value_segment_file_bytes: u64,
pub value_segment_allocated_bytes: u64,
pub value_segment_high_water_bytes: u64,
pub tail_reclaimable_slots: u64,
pub tail_reclaimable_bytes: u64,
pub middle_reusable_slots: u64,
pub manifest_log_bytes: u64,
}
#[derive(Debug, Clone, Copy, Default)]
pub struct VacuumStats {
pub unreachable_blobs: usize,
pub slots_trimmed: u64,
pub slots_relocated: u64,
pub bytes_relocated: u64,
pub bytes_truncated: u64,
pub slots_punched: u64,
pub bytes_punched: u64,
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct TreeStats {
pub blob_count: u32,
pub total_space_used: u64,
pub total_gap_space: u64,
pub total_slots: u64,
pub total_compactions: u64,
pub total_tombstones: u64,
pub total_blob_edges: u64,
pub leaf_blob_count: u32,
pub max_blob_depth: u32,
pub total_blob_depth: u64,
pub max_blob_fill_per_mille: u32,
pub underfilled_child_blobs: u32,
pub overfull_child_blobs: u32,
pub blobs: Vec<BlobStats>,
pub bm_dirty_count: usize,
pub bm_pending_delete_count: usize,
pub bm_gc_orphan_backlog_count: usize,
pub bm_gc_reclaimed_count: u64,
pub bm_gc_last_full_sweep_deferred_count: usize,
pub bm_write_delta_count: usize,
pub bm_read_index_token_count: usize,
pub bm_read_index_cache_entries: usize,
pub bm_read_index_cache_bytes: usize,
pub bm_read_index_cache_budget_bytes: usize,
pub bm_read_page_cache_entries: usize,
pub bm_read_page_cache_bytes: usize,
pub bm_read_page_cache_ghost_entries: usize,
pub bm_read_page_cache_budget_bytes: usize,
pub bm_cache_hits: u64,
pub bm_cache_misses: u64,
pub bm_full_blob_reads: u64,
pub bm_full_blob_read_bytes: u64,
pub bm_point_full_blob_reads: u64,
pub bm_scan_full_blob_reads: u64,
pub bm_silent_full_blob_reads: u64,
pub bm_read_page_hits: u64,
pub bm_read_page_misses: u64,
pub bm_read_index_cache_hits: u64,
pub bm_read_index_cache_misses: u64,
pub bm_read_index_loads: u64,
pub bm_read_index_dir_read_bytes: u64,
pub bm_read_index_bucket_reads: u64,
pub bm_read_index_bucket_read_bytes: u64,
pub bm_read_index_inline_hits: u64,
pub bm_read_index_value_hits: u64,
pub bm_read_index_value_read_bytes: u64,
pub bm_read_index_offset_hits: u64,
pub bm_read_index_negative_hits: u64,
pub bm_read_index_crossing_hits: u64,
pub bm_read_index_unknowns: u64,
pub bm_optimistic_restarts: u64,
pub bm_range_restarts: u64,
pub bm_walker_ops: u64,
pub bm_walker_blob_hops: u64,
pub bm_max_blob_hops: u64,
pub bm_max_cross_blob_depth: u64,
pub bm_spillovers: u64,
pub bm_merges: u64,
pub bm_route_resident_count: usize,
pub bm_route_resident_demotions: u64,
pub bm_cache_evictions: u64,
pub bm_eviction_skips_protected: u64,
pub bm_eviction_skips_route_resident: u64,
pub bm_admission_protects: u64,
pub store: StoreStats,
pub route_cache: RouteCacheStats,
pub open: OpenStats,
pub journal: Option<JournalStats>,
pub checkpointer: Option<CheckpointerStats>,
}
impl TreeStats {
#[must_use]
#[allow(clippy::cast_precision_loss)] pub fn bm_avg_blob_hops(&self) -> f64 {
if self.bm_walker_ops == 0 {
0.0
} else {
self.bm_walker_blob_hops as f64 / self.bm_walker_ops as f64
}
}
#[must_use]
#[allow(clippy::cast_precision_loss)]
pub fn avg_blob_depth(&self) -> f64 {
if self.blob_count == 0 {
0.0
} else {
self.total_blob_depth as f64 / f64::from(self.blob_count)
}
}
#[must_use]
#[allow(clippy::cast_precision_loss)]
pub fn leaf_blob_ratio(&self) -> f64 {
if self.blob_count == 0 {
0.0
} else {
f64::from(self.leaf_blob_count) / f64::from(self.blob_count)
}
}
#[must_use]
#[allow(clippy::cast_precision_loss)]
pub fn avg_blob_fill_ratio(&self) -> f64 {
if self.blob_count == 0 {
return 0.0;
}
let header_bytes = u64::from(self.blob_count) * DATA_AREA_START as u64;
let data_used = self.total_space_used.saturating_sub(header_bytes);
let data_capacity = u64::from(self.blob_count) * (PAGE_SIZE - DATA_AREA_START) as u64;
if data_capacity == 0 {
0.0
} else {
data_used as f64 / data_capacity as f64
}
}
#[must_use]
pub fn max_blob_fill_ratio(&self) -> f64 {
f64::from(self.max_blob_fill_per_mille) / 1000.0
}
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct DBStats {
pub open_tree_count: usize,
pub bm_dirty_count: usize,
pub bm_pending_delete_count: usize,
pub bm_gc_orphan_backlog_count: usize,
pub bm_gc_reclaimed_count: u64,
pub bm_gc_last_full_sweep_deferred_count: usize,
pub bm_write_delta_count: usize,
pub bm_read_index_token_count: usize,
pub bm_read_index_cache_entries: usize,
pub bm_read_index_cache_bytes: usize,
pub bm_read_index_cache_budget_bytes: usize,
pub bm_read_page_cache_entries: usize,
pub bm_read_page_cache_bytes: usize,
pub bm_read_page_cache_ghost_entries: usize,
pub bm_read_page_cache_budget_bytes: usize,
pub bm_cache_hits: u64,
pub bm_cache_misses: u64,
pub bm_full_blob_reads: u64,
pub bm_full_blob_read_bytes: u64,
pub bm_point_full_blob_reads: u64,
pub bm_scan_full_blob_reads: u64,
pub bm_silent_full_blob_reads: u64,
pub bm_read_page_hits: u64,
pub bm_read_page_misses: u64,
pub bm_read_index_cache_hits: u64,
pub bm_read_index_cache_misses: u64,
pub bm_read_index_loads: u64,
pub bm_read_index_dir_read_bytes: u64,
pub bm_read_index_bucket_reads: u64,
pub bm_read_index_bucket_read_bytes: u64,
pub bm_read_index_inline_hits: u64,
pub bm_read_index_value_hits: u64,
pub bm_read_index_value_read_bytes: u64,
pub bm_read_index_offset_hits: u64,
pub bm_read_index_negative_hits: u64,
pub bm_read_index_crossing_hits: u64,
pub bm_read_index_unknowns: u64,
pub bm_optimistic_restarts: u64,
pub bm_range_restarts: u64,
pub bm_walker_ops: u64,
pub bm_walker_blob_hops: u64,
pub bm_max_blob_hops: u64,
pub bm_max_cross_blob_depth: u64,
pub bm_spillovers: u64,
pub bm_merges: u64,
pub bm_route_resident_count: usize,
pub bm_route_resident_demotions: u64,
pub bm_cache_evictions: u64,
pub bm_eviction_skips_protected: u64,
pub bm_eviction_skips_route_resident: u64,
pub bm_admission_protects: u64,
pub store: StoreStats,
pub open: OpenStats,
pub journal: Option<JournalStats>,
pub checkpointer: Option<CheckpointerStats>,
}
impl DBStats {
#[must_use]
#[allow(clippy::cast_precision_loss)]
pub fn bm_avg_blob_hops(&self) -> f64 {
if self.bm_walker_ops == 0 {
0.0
} else {
self.bm_walker_blob_hops as f64 / self.bm_walker_ops as f64
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct JournalStats {
pub appends: u64,
pub batches: u64,
pub syncs: u64,
pub queued_work: u64,
pub written_work: u64,
pub flushed_work: u64,
pub checkpointed_work: u64,
pub pending_work: u64,
pub checkpoint_debt: u64,
}
#[derive(Debug, Clone, Copy, Default)]
#[non_exhaustive]
pub struct OpenStats {
pub wal_replay_records: u64,
pub wal_replay_bytes: u64,
pub wal_replay_micros: u64,
pub epoch_recovery_micros: u64,
pub wal_torn_tail: bool,
}
#[derive(Debug, Clone, Copy)]
pub struct CheckpointerStats {
pub rounds_attempted: u64,
pub rounds_succeeded: u64,
pub rounds_failed: u64,
pub blobs_flushed: u64,
pub merges_total: u64,
pub truncates: u64,
pub evictions: u64,
pub last_dirty_count: usize,
pub last_pending_delete_count: usize,
pub last_round_micros: u64,
}