cli: prettier table in garage stats

This commit is contained in:
Alex Auvolat 2022-12-13 15:43:22 +01:00
parent d7f90cabb0
commit d6040e32a6
No known key found for this signature in database
GPG Key ID: 0E496D15096376BE
7 changed files with 93 additions and 39 deletions

View File

@ -318,6 +318,11 @@ impl BlockManager {
Ok(self.rc.rc.len()?) Ok(self.rc.rc.len()?)
} }
/// Get number of items in the refcount table
pub fn rc_fast_len(&self) -> Result<Option<usize>, Error> {
Ok(self.rc.rc.fast_len()?)
}
/// Send command to start/stop/manager scrub worker /// Send command to start/stop/manager scrub worker
pub async fn send_scrub_command(&self, cmd: ScrubWorkerCommand) { pub async fn send_scrub_command(&self, cmd: ScrubWorkerCommand) {
let _ = self.tx_scrub_command.send(cmd).await; let _ = self.tx_scrub_command.send(cmd).await;

View File

@ -181,6 +181,10 @@ impl Tree {
pub fn len(&self) -> Result<usize> { pub fn len(&self) -> Result<usize> {
self.0.len(self.1) self.0.len(self.1)
} }
#[inline]
pub fn fast_len(&self) -> Result<Option<usize>> {
self.0.fast_len(self.1)
}
#[inline] #[inline]
pub fn first(&self) -> Result<Option<(Value, Value)>> { pub fn first(&self) -> Result<Option<(Value, Value)>> {
@ -323,6 +327,9 @@ pub(crate) trait IDb: Send + Sync {
fn get(&self, tree: usize, key: &[u8]) -> Result<Option<Value>>; fn get(&self, tree: usize, key: &[u8]) -> Result<Option<Value>>;
fn len(&self, tree: usize) -> Result<usize>; fn len(&self, tree: usize) -> Result<usize>;
fn fast_len(&self, _tree: usize) -> Result<Option<usize>> {
Ok(None)
}
fn insert(&self, tree: usize, key: &[u8], value: &[u8]) -> Result<Option<Value>>; fn insert(&self, tree: usize, key: &[u8], value: &[u8]) -> Result<Option<Value>>;
fn remove(&self, tree: usize, key: &[u8]) -> Result<Option<Value>>; fn remove(&self, tree: usize, key: &[u8]) -> Result<Option<Value>>;

View File

@ -121,6 +121,10 @@ impl IDb for LmdbDb {
Ok(tree.len(&tx)?.try_into().unwrap()) Ok(tree.len(&tx)?.try_into().unwrap())
} }
fn fast_len(&self, tree: usize) -> Result<Option<usize>> {
Ok(Some(self.len(tree)?))
}
fn insert(&self, tree: usize, key: &[u8], value: &[u8]) -> Result<Option<Value>> { fn insert(&self, tree: usize, key: &[u8], value: &[u8]) -> Result<Option<Value>> {
let tree = self.get_tree(tree)?; let tree = self.get_tree(tree)?;
let mut tx = self.db.write_txn()?; let mut tx = self.db.write_txn()?;

View File

@ -144,6 +144,10 @@ impl IDb for SqliteDb {
} }
} }
fn fast_len(&self, tree: usize) -> Result<Option<usize>> {
Ok(Some(self.len(tree)?))
}
fn insert(&self, tree: usize, key: &[u8], value: &[u8]) -> Result<Option<Value>> { fn insert(&self, tree: usize, key: &[u8], value: &[u8]) -> Result<Option<Value>> {
trace!("insert {}: lock db", tree); trace!("insert {}: lock db", tree);
let this = self.0.lock().unwrap(); let this = self.0.lock().unwrap();

View File

@ -8,6 +8,7 @@ use serde::{Deserialize, Serialize};
use garage_util::crdt::*; use garage_util::crdt::*;
use garage_util::data::*; use garage_util::data::*;
use garage_util::error::Error as GarageError; use garage_util::error::Error as GarageError;
use garage_util::formater::format_table_to_string;
use garage_util::time::*; use garage_util::time::*;
use garage_table::replication::*; use garage_table::replication::*;
@ -808,6 +809,7 @@ impl AdminRpcHandler {
.unwrap_or_else(|| "(unknown)".into()), .unwrap_or_else(|| "(unknown)".into()),
) )
.unwrap(); .unwrap();
writeln!(&mut ret, "\nDatabase engine: {}", self.garage.db.engine()).unwrap(); writeln!(&mut ret, "\nDatabase engine: {}", self.garage.db.engine()).unwrap();
// Gather ring statistics // Gather ring statistics
@ -826,21 +828,38 @@ impl AdminRpcHandler {
writeln!(&mut ret, " {:?} {}", n, c).unwrap(); writeln!(&mut ret, " {:?} {}", n, c).unwrap();
} }
self.gather_table_stats(&mut ret, &self.garage.bucket_table, &opt)?; // Gather table statistics
self.gather_table_stats(&mut ret, &self.garage.key_table, &opt)?; let mut table = vec![" Table\tItems\tMklItems\tMklTodo\tGcTodo".into()];
self.gather_table_stats(&mut ret, &self.garage.object_table, &opt)?; table.push(self.gather_table_stats(&self.garage.bucket_table, opt.detailed)?);
self.gather_table_stats(&mut ret, &self.garage.version_table, &opt)?; table.push(self.gather_table_stats(&self.garage.key_table, opt.detailed)?);
self.gather_table_stats(&mut ret, &self.garage.block_ref_table, &opt)?; table.push(self.gather_table_stats(&self.garage.object_table, opt.detailed)?);
table.push(self.gather_table_stats(&self.garage.version_table, opt.detailed)?);
table.push(self.gather_table_stats(&self.garage.block_ref_table, opt.detailed)?);
write!(
&mut ret,
"\nTable stats:\n{}",
format_table_to_string(table)
)
.unwrap();
// Gather block manager statistics
writeln!(&mut ret, "\nBlock manager stats:").unwrap(); writeln!(&mut ret, "\nBlock manager stats:").unwrap();
if opt.detailed { let rc_len = if opt.detailed {
writeln!( self.garage.block_manager.rc_len()?.to_string()
&mut ret, } else {
" number of RC entries (~= number of blocks): {}", self.garage
self.garage.block_manager.rc_len()? .block_manager
) .rc_fast_len()?
.unwrap(); .map(|x| x.to_string())
} .unwrap_or_else(|| "NC".into())
};
writeln!(
&mut ret,
" number of RC entries (~= number of blocks): {}",
rc_len
)
.unwrap();
writeln!( writeln!(
&mut ret, &mut ret,
" resync queue length: {}", " resync queue length: {}",
@ -854,43 +873,50 @@ impl AdminRpcHandler {
) )
.unwrap(); .unwrap();
if !opt.detailed {
writeln!(&mut ret, "\nIf values are missing (marked as NC), consider adding the --detailed flag - this will be slow.").unwrap();
}
Ok(ret) Ok(ret)
} }
fn gather_table_stats<F, R>( fn gather_table_stats<F, R>(
&self, &self,
to: &mut String,
t: &Arc<Table<F, R>>, t: &Arc<Table<F, R>>,
opt: &StatsOpt, detailed: bool,
) -> Result<(), Error> ) -> Result<String, Error>
where where
F: TableSchema + 'static, F: TableSchema + 'static,
R: TableReplication + 'static, R: TableReplication + 'static,
{ {
writeln!(to, "\nTable stats for {}", F::TABLE_NAME).unwrap(); let (data_len, mkl_len) = if detailed {
if opt.detailed { (
writeln!( t.data.store.len().map_err(GarageError::from)?.to_string(),
to, t.merkle_updater.merkle_tree_len()?.to_string(),
" number of items: {}",
t.data.store.len().map_err(GarageError::from)?
) )
.unwrap(); } else {
writeln!( (
to, t.data
" Merkle tree size: {}", .store
t.merkle_updater.merkle_tree_len()? .fast_len()
.map_err(GarageError::from)?
.map(|x| x.to_string())
.unwrap_or_else(|| "NC".into()),
t.merkle_updater
.merkle_tree_fast_len()?
.map(|x| x.to_string())
.unwrap_or_else(|| "NC".into()),
) )
.unwrap(); };
}
writeln!(
to,
" Merkle updater todo queue length: {}",
t.merkle_updater.todo_len()?
)
.unwrap();
writeln!(to, " GC todo queue length: {}", t.data.gc_todo_len()?).unwrap();
Ok(()) Ok(format!(
" {}\t{}\t{}\t{}\t{}",
F::TABLE_NAME,
data_len,
mkl_len,
t.merkle_updater.todo_len()?,
t.data.gc_todo_len()?
))
} }
// ================ WORKER COMMANDS ==================== // ================ WORKER COMMANDS ====================

View File

@ -293,6 +293,10 @@ where
Ok(self.data.merkle_tree.len()?) Ok(self.data.merkle_tree.len()?)
} }
pub fn merkle_tree_fast_len(&self) -> Result<Option<usize>, Error> {
Ok(self.data.merkle_tree.fast_len()?)
}
pub fn todo_len(&self) -> Result<usize, Error> { pub fn todo_len(&self) -> Result<usize, Error> {
Ok(self.data.merkle_todo.len()?) Ok(self.data.merkle_todo.len()?)
} }

View File

@ -1,4 +1,4 @@
pub fn format_table(data: Vec<String>) { pub fn format_table_to_string(data: Vec<String>) -> String {
let data = data let data = data
.iter() .iter()
.map(|s| s.split('\t').collect::<Vec<_>>()) .map(|s| s.split('\t').collect::<Vec<_>>())
@ -24,5 +24,9 @@ pub fn format_table(data: Vec<String>) {
out.push('\n'); out.push('\n');
} }
print!("{}", out); out
}
pub fn format_table(data: Vec<String>) {
print!("{}", format_table_to_string(data));
} }