use std::{
fmt::{Display, Formatter},
iter::FromIterator,
};
use lmdb_zero as lmdb;
#[derive(Debug, Clone)]
pub struct DbBasicStats {
root: DbStat,
env_info: EnvInfo,
db_stats: Vec<DbStat>,
}
impl DbBasicStats {
pub(super) fn new<I: IntoIterator<Item = (&'static str, lmdb::Stat)>>(
global: lmdb::Stat,
env_info: lmdb::EnvInfo,
db_stats: I,
) -> Self {
Self {
root: ("[root]", global).into(),
env_info: env_info.into(),
db_stats: db_stats.into_iter().map(Into::into).collect(),
}
}
pub fn root(&self) -> &DbStat {
&self.root
}
pub fn env_info(&self) -> &EnvInfo {
&self.env_info
}
pub fn db_stats(&self) -> &[DbStat] {
&self.db_stats
}
}
impl Display for DbBasicStats {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
writeln!(f, "Root: psize = {}, {}", self.root.psize, self.root)?;
for stat in &self.db_stats {
writeln!(f, "{stat}")?;
}
Ok(())
}
}
#[derive(Debug, Clone, Copy)]
pub struct DbStat {
pub name: &'static str,
pub psize: u32,
pub depth: u32,
pub branch_pages: usize,
pub leaf_pages: usize,
pub overflow_pages: usize,
pub entries: usize,
}
impl DbStat {
pub fn total_page_size(&self) -> usize {
self.psize as usize * (self.leaf_pages + self.branch_pages + self.overflow_pages)
}
}
impl From<(&'static str, lmdb::Stat)> for DbStat {
fn from((name, stat): (&'static str, lmdb::Stat)) -> Self {
Self {
name,
psize: stat.psize,
depth: stat.depth,
branch_pages: stat.branch_pages,
leaf_pages: stat.leaf_pages,
overflow_pages: stat.overflow_pages,
entries: stat.entries,
}
}
}
impl Display for DbStat {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"name: {}, Total page size: {}, entries: {}, depth: {}, branch_pages: {}, leaf_pages: {}, overflow_pages: \
{}",
self.name,
self.total_page_size(),
self.entries,
self.depth,
self.branch_pages,
self.leaf_pages,
self.overflow_pages,
)
}
}
#[derive(Debug, Clone)]
pub struct DbTotalSizeStats {
sizes: Vec<DbSize>,
}
impl DbTotalSizeStats {
pub fn sizes(&self) -> &[DbSize] {
&self.sizes
}
}
#[derive(Debug, Clone, Copy)]
pub struct DbSize {
pub name: &'static str,
pub num_entries: u64,
pub total_key_size: u64,
pub total_value_size: u64,
}
impl DbSize {
pub fn total(&self) -> u64 {
self.total_key_size.saturating_add(self.total_value_size)
}
pub fn avg_bytes_per_entry(&self) -> u64 {
if self.num_entries == 0 {
return 0;
}
self.total() / self.num_entries
}
}
impl From<Vec<DbSize>> for DbTotalSizeStats {
fn from(sizes: Vec<DbSize>) -> Self {
Self { sizes }
}
}
impl FromIterator<DbSize> for DbTotalSizeStats {
fn from_iter<T: IntoIterator<Item = DbSize>>(iter: T) -> Self {
Self {
sizes: iter.into_iter().collect(),
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct EnvInfo {
pub mapsize: usize,
pub last_pgno: usize,
pub last_txnid: usize,
pub maxreaders: u32,
pub numreaders: u32,
}
impl From<lmdb::EnvInfo> for EnvInfo {
fn from(info: lmdb::EnvInfo) -> Self {
Self {
mapsize: info.mapsize,
last_pgno: info.last_pgno,
last_txnid: info.last_txnid,
maxreaders: info.maxreaders,
numreaders: info.numreaders,
}
}
}
impl Display for EnvInfo {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"mapsize: {:.2} MiB, last_pgno: {}, last_txnid: {}, maxreaders: {}, numreaders: {}",
self.mapsize as f32 / 1024.0 / 1024.0,
self.last_pgno,
self.last_txnid,
self.maxreaders,
self.numreaders,
)
}
}
#[cfg(test)]
mod test {
use super::*;
impl DbStat {
pub fn sample() -> Self {
DbStat {
name: "coverage",
psize: 10,
depth: 0,
leaf_pages: 1,
branch_pages: 2,
overflow_pages: 3,
entries: 0,
}
}
}
impl DbSize {
pub fn sample() -> Self {
Self {
name: "coverage",
num_entries: 0,
total_key_size: u64::MAX,
total_value_size: 1,
}
}
}
impl EnvInfo {
pub fn sample() -> Self {
Self {
mapsize: 0,
last_pgno: 0,
last_txnid: 0,
maxreaders: 0,
numreaders: 0,
}
}
}
impl DbBasicStats {
pub fn sample() -> Self {
Self {
root: DbStat::sample(),
env_info: EnvInfo::sample(),
db_stats: vec![DbStat::sample()],
}
}
}
#[test]
fn coverage_db_stat() {
let obj = DbStat::sample();
assert_eq!(obj.total_page_size(), 60);
}
#[test]
fn coverage_db_basic_stats() {
let obj = DbBasicStats::sample();
obj.root();
obj.env_info();
obj.db_stats();
}
#[test]
fn coverage_db_size() {
let mut obj = DbSize::sample();
assert_eq!(obj.total(), u64::MAX);
assert_eq!(obj.avg_bytes_per_entry(), 0);
obj.num_entries = obj.total();
assert_eq!(obj.avg_bytes_per_entry(), 1);
}
#[test]
fn coverage_db_total_size_stats() {
let vec = vec![DbSize::sample()];
let obj = DbTotalSizeStats::from(vec);
let obj = obj.sizes.into_iter().collect::<DbTotalSizeStats>();
obj.sizes();
}
}