Adds logging of column family size and database size on startup and s… (#8336)

* Adds logging of column family size and database size on startup and shutdown

* Changes log level of column families size strings to debug. Adds TODO comment to use human_bytes crate for human-readable format of metrics. Adds print_db_metrics function to ZebraDb struct.

* Calls enumerate() on column_families to access index var

* Resolves cargo fmt checker results

* Resolves clippy lint

* Runs and fixes changes from fmt, clippy, check and test

* Removes prop.txt

* minor doc changes

---------

Co-authored-by: Elijah Hampton <elijahhampton@MBP-de-Elijah-2.lan>
Co-authored-by: Pili Guerra <mpguerra@users.noreply.github.com>
Co-authored-by: Alfredo Garcia <oxarbitrage@gmail.com>
This commit is contained in:
Elijah Hampton 2024-03-28 20:00:28 -04:00 committed by GitHub
parent 7723736736
commit 49fca309cf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 102 additions and 1 deletions

View File

@ -254,6 +254,10 @@ impl Drop for StateService {
"dropping the state: dropped unused non-finalized state queue block",
);
// Log database metrics before shutting down
info!("dropping the state: logging database metrics");
self.log_db_metrics();
// Then drop self.read_service, which checks the block write task for panics,
// and tries to shut down the database.
}
@ -451,6 +455,11 @@ impl StateService {
(state, read_service, latest_chain_tip, chain_tip_change)
}
/// Call read only state service to log rocksdb database metrics.
pub fn log_db_metrics(&self) {
self.read_service.db.print_db_metrics();
}
/// Queue a checkpoint verified block for verification and storage in the finalized state.
///
/// Returns a channel receiver that provides the result of the block commit.
@ -853,6 +862,11 @@ impl ReadStateService {
pub fn db(&self) -> &ZebraDb {
&self.db
}
/// Logs rocksdb metrics using the read only state service.
pub fn log_db_metrics(&self) {
self.db.print_db_metrics();
}
}
impl Service<Request> for StateService {

View File

@ -13,6 +13,7 @@
use std::{
collections::{BTreeMap, HashMap},
fmt::Debug,
fmt::Write,
ops::RangeBounds,
path::Path,
sync::Arc,
@ -21,7 +22,7 @@ use std::{
use itertools::Itertools;
use rlimit::increase_nofile_limit;
use rocksdb::ReadOptions;
use rocksdb::{ColumnFamilyDescriptor, Options, ReadOptions};
use semver::Version;
use zebra_chain::{parameters::Network, primitives::byte_array::increment_big_endian};
@ -512,6 +513,55 @@ impl DiskWriteBatch {
}
impl DiskDb {
/// Prints rocksdb metrics for each column family along with total database disk size, live data disk size and database memory size.
pub fn print_db_metrics(&self) {
let mut total_size_on_disk = 0;
let mut total_live_size_on_disk = 0;
let mut total_size_in_mem = 0;
let db: &Arc<DB> = &self.db;
let db_options = DiskDb::options();
let column_families = DiskDb::construct_column_families(&db_options, db.path(), &[]);
let mut column_families_log_string = String::from("");
write!(column_families_log_string, "Column families and sizes: ").unwrap();
for cf_descriptor in column_families.iter() {
let cf_name = &cf_descriptor.name();
let cf_handle = db
.cf_handle(cf_name)
.expect("Column family handle must exist");
let live_data_size = db
.property_int_value_cf(cf_handle, "rocksdb.estimate-live-data-size")
.unwrap_or(Some(0));
let total_sst_files_size = db
.property_int_value_cf(cf_handle, "rocksdb.total-sst-files-size")
.unwrap_or(Some(0));
let cf_disk_size = live_data_size.unwrap_or(0) + total_sst_files_size.unwrap_or(0);
total_size_on_disk += cf_disk_size;
total_live_size_on_disk += live_data_size.unwrap_or(0);
let mem_table_size = db
.property_int_value_cf(cf_handle, "rocksdb.size-all-mem-tables")
.unwrap_or(Some(0));
total_size_in_mem += mem_table_size.unwrap_or(0);
// TODO: Consider displaying the disk and memory sizes in a human-readable format - #8380.
write!(
column_families_log_string,
"{} (Disk: {} bytes, Memory: {} bytes)",
cf_name,
cf_disk_size,
mem_table_size.unwrap_or(0)
)
.unwrap();
}
debug!("{}", column_families_log_string);
info!("Total Database Disk Size: {} bytes", total_size_on_disk);
info!(
"Total Live Data Disk Size: {} bytes",
total_live_size_on_disk
);
info!("Total Database Memory Size: {} bytes", total_size_in_mem);
}
/// Returns a forward iterator over the items in `cf` in `range`.
///
/// Holding this iterator open might delay block commit transactions.
@ -720,6 +770,32 @@ impl DiskDb {
/// <https://github.com/facebook/rocksdb/wiki/RocksDB-FAQ#configuration-and-tuning>
const MEMTABLE_RAM_CACHE_MEGABYTES: usize = 128;
/// Build a vector of current column families on the disk and optionally any new column families.
/// Returns an iterable collection of all column families.
fn construct_column_families(
db_options: &Options,
path: &Path,
column_families_in_code: &[String],
) -> Vec<ColumnFamilyDescriptor> {
// When opening the database in read/write mode, all column families must be opened.
//
// To make Zebra forward-compatible with databases updated by later versions,
// we read any existing column families off the disk, then add any new column families
// from the current implementation.
//
// <https://github.com/facebook/rocksdb/wiki/Column-Families#reference>
let column_families_on_disk = DB::list_cf(db_options, path).unwrap_or_default();
let column_families = column_families_on_disk
.into_iter()
.chain(column_families_in_code.iter().cloned())
.unique()
.collect::<Vec<_>>();
column_families
.into_iter()
.map(|cf_name| ColumnFamilyDescriptor::new(cf_name, db_options.clone()))
.collect()
}
/// Opens or creates the database at a path based on the kind, major version and network,
/// with the supplied column families, preserving any existing column families,
/// and returns a shared low-level database wrapper.

View File

@ -322,6 +322,14 @@ impl ZebraDb {
Ok(())
}
/// Logs metrics related to the underlying RocksDB instance.
///
/// This function prints various metrics and statistics about the RocksDB database,
/// such as disk usage, memory usage, and other performance-related metrics.
pub fn print_db_metrics(&self) {
self.db.print_db_metrics();
}
}
impl Drop for ZebraDb {

View File

@ -131,6 +131,9 @@ impl StartCmd {
)
.await?;
info!("logging database metrics on startup");
read_only_state_service.log_db_metrics();
let state = ServiceBuilder::new()
.buffer(Self::state_buffer_bound())
.service(state_service);