feat(cli): add RocksDB table stats to reth db stats command (#21221)

Co-authored-by: joshieDo <93316087+joshieDo@users.noreply.github.com>
This commit is contained in:
Georgios Konstantopoulos
2026-01-21 00:45:17 -08:00
committed by GitHub
parent 238433e146
commit 37b5db0d47
5 changed files with 157 additions and 3 deletions

View File

@@ -11,7 +11,10 @@ use reth_db_common::DbTool;
use reth_fs_util as fs;
use reth_node_builder::{NodePrimitives, NodeTypesWithDB, NodeTypesWithDBAdapter};
use reth_node_core::dirs::{ChainPath, DataDirPath};
use reth_provider::providers::{ProviderNodeTypes, StaticFileProvider};
use reth_provider::{
providers::{ProviderNodeTypes, StaticFileProvider},
RocksDBProviderFactory,
};
use reth_static_file_types::SegmentRangeInclusive;
use std::{sync::Arc, time::Duration};
@@ -61,6 +64,11 @@ impl Command {
let db_stats_table = self.db_stats_table(tool)?;
println!("{db_stats_table}");
println!("\n");
let rocksdb_stats_table = self.rocksdb_stats_table(tool);
println!("{rocksdb_stats_table}");
Ok(())
}
@@ -148,6 +156,45 @@ impl Command {
Ok(table)
}
fn rocksdb_stats_table<N: NodeTypesWithDB>(&self, tool: &DbTool<N>) -> ComfyTable {
let mut table = ComfyTable::new();
table.load_preset(comfy_table::presets::ASCII_MARKDOWN);
table.set_header(["RocksDB Table Name", "# Entries", "Total Size", "Pending Compaction"]);
let stats = tool.provider_factory.rocksdb_provider().table_stats();
let mut total_size: u64 = 0;
let mut total_pending: u64 = 0;
for stat in &stats {
total_size += stat.estimated_size_bytes;
total_pending += stat.pending_compaction_bytes;
let mut row = Row::new();
row.add_cell(Cell::new(&stat.name))
.add_cell(Cell::new(stat.estimated_num_keys))
.add_cell(Cell::new(human_bytes(stat.estimated_size_bytes as f64)))
.add_cell(Cell::new(human_bytes(stat.pending_compaction_bytes as f64)));
table.add_row(row);
}
if !stats.is_empty() {
let max_widths = table.column_max_content_widths();
let mut separator = Row::new();
for width in max_widths {
separator.add_cell(Cell::new("-".repeat(width as usize)));
}
table.add_row(separator);
let mut row = Row::new();
row.add_cell(Cell::new("RocksDB Total"))
.add_cell(Cell::new(""))
.add_cell(Cell::new(human_bytes(total_size as f64)))
.add_cell(Cell::new(human_bytes(total_pending as f64)));
table.add_row(row);
}
table
}
fn static_files_stats_table<N: NodePrimitives>(
&self,
data_dir: ChainPath<DataDirPath>,

View File

@@ -38,7 +38,9 @@ pub use consistent::ConsistentProvider;
#[cfg_attr(not(all(unix, feature = "rocksdb")), path = "rocksdb_stub.rs")]
pub(crate) mod rocksdb;
pub use rocksdb::{RocksDBBatch, RocksDBBuilder, RocksDBProvider, RocksDBRawIter, RocksTx};
pub use rocksdb::{
RocksDBBatch, RocksDBBuilder, RocksDBProvider, RocksDBRawIter, RocksDBTableStats, RocksTx,
};
/// Helper trait to bound [`NodeTypes`] so that combined with database they satisfy
/// [`ProviderNodeTypes`].

View File

@@ -5,4 +5,6 @@ mod metrics;
mod provider;
pub(crate) use provider::{PendingRocksDBBatches, RocksDBWriteCtx};
pub use provider::{RocksDBBatch, RocksDBBuilder, RocksDBProvider, RocksDBRawIter, RocksTx};
pub use provider::{
RocksDBBatch, RocksDBBuilder, RocksDBProvider, RocksDBRawIter, RocksDBTableStats, RocksTx,
};

View File

@@ -38,6 +38,19 @@ use tracing::instrument;
/// Pending `RocksDB` batches type alias.
pub(crate) type PendingRocksDBBatches = Arc<Mutex<Vec<WriteBatchWithTransaction<true>>>>;
/// Statistics for a single `RocksDB` table (column family).
#[derive(Debug, Clone)]
pub struct RocksDBTableStats {
/// Name of the table/column family.
pub name: String,
/// Estimated number of keys in the table.
pub estimated_num_keys: u64,
/// Estimated size of live data in bytes (SST files + memtables).
pub estimated_size_bytes: u64,
/// Estimated bytes pending compaction (reclaimable space).
pub pending_compaction_bytes: u64,
}
/// Context for `RocksDB` block writes.
#[derive(Clone)]
pub(crate) struct RocksDBWriteCtx {
@@ -405,6 +418,69 @@ impl RocksDBProviderInner {
Self::ReadOnly { db, .. } => RocksDBIterEnum::ReadOnly(db.iterator_cf(cf, mode)),
}
}
/// Returns statistics for all column families in the database.
fn table_stats(&self) -> Vec<RocksDBTableStats> {
let cf_names = [
tables::TransactionHashNumbers::NAME,
tables::AccountsHistory::NAME,
tables::StoragesHistory::NAME,
];
let mut stats = Vec::new();
macro_rules! collect_stats {
($db:expr) => {
for cf_name in cf_names {
if let Some(cf) = $db.cf_handle(cf_name) {
let estimated_num_keys = $db
.property_int_value_cf(cf, rocksdb::properties::ESTIMATE_NUM_KEYS)
.ok()
.flatten()
.unwrap_or(0);
// SST files size (on-disk) + memtable size (in-memory)
let sst_size = $db
.property_int_value_cf(cf, rocksdb::properties::LIVE_SST_FILES_SIZE)
.ok()
.flatten()
.unwrap_or(0);
let memtable_size = $db
.property_int_value_cf(cf, rocksdb::properties::SIZE_ALL_MEM_TABLES)
.ok()
.flatten()
.unwrap_or(0);
let estimated_size_bytes = sst_size + memtable_size;
let pending_compaction_bytes = $db
.property_int_value_cf(
cf,
rocksdb::properties::ESTIMATE_PENDING_COMPACTION_BYTES,
)
.ok()
.flatten()
.unwrap_or(0);
stats.push(RocksDBTableStats {
name: cf_name.to_string(),
estimated_num_keys,
estimated_size_bytes,
pending_compaction_bytes,
});
}
}
};
}
match self {
Self::ReadWrite { db, .. } => collect_stats!(db),
Self::ReadOnly { db, .. } => collect_stats!(db),
}
stats
}
}
impl fmt::Debug for RocksDBProviderInner {
@@ -666,6 +742,13 @@ impl RocksDBProvider {
Ok(RocksDBIter { inner: iter, _marker: std::marker::PhantomData })
}
/// Returns statistics for all column families in the database.
///
/// Returns a vector of (`table_name`, `estimated_keys`, `estimated_size_bytes`) tuples.
pub fn table_stats(&self) -> Vec<RocksDBTableStats> {
self.0.table_stats()
}
/// Creates a raw iterator over all entries in the specified table.
///
/// Returns raw `(key_bytes, value_bytes)` pairs without decoding.

View File

@@ -14,6 +14,19 @@ use std::{path::Path, sync::Arc};
/// Pending `RocksDB` batches type alias (stub - uses unit type).
pub(crate) type PendingRocksDBBatches = Arc<Mutex<Vec<()>>>;
/// Statistics for a single `RocksDB` table (column family) - stub.
#[derive(Debug, Clone)]
pub struct RocksDBTableStats {
/// Name of the table/column family.
pub name: String,
/// Estimated number of keys in the table.
pub estimated_num_keys: u64,
/// Estimated size of live data in bytes (SST files + memtables).
pub estimated_size_bytes: u64,
/// Estimated bytes pending compaction (reclaimable space).
pub pending_compaction_bytes: u64,
}
/// Context for `RocksDB` block writes (stub).
#[derive(Debug, Clone)]
#[allow(dead_code)]
@@ -56,6 +69,13 @@ impl RocksDBProvider {
) -> ProviderResult<Option<BlockNumber>> {
Ok(None)
}
/// Returns statistics for all column families in the database (stub implementation).
///
/// Returns an empty vector since there is no `RocksDB` when the feature is disabled.
pub const fn table_stats(&self) -> Vec<RocksDBTableStats> {
Vec::new()
}
}
/// A stub batch writer for `RocksDB`.