Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ledger/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ locktick = [
"snarkvm-ledger-store/locktick",
"snarkvm-synthesizer/locktick"
]
metrics = [ "snarkvm-ledger-committee/metrics" ]
metrics = [ "snarkvm-ledger-committee/metrics", "snarkvm-ledger-store/metrics" ]
prop-tests = [ "snarkvm-ledger-committee/prop-tests" ]
rocks = [ "snarkvm-ledger-store/rocks" ]
serial = [
Expand Down
5 changes: 5 additions & 0 deletions ledger/store/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ history = [ ]
history-staking-rewards = [ ]
slipstream-plugins = [ "dep:snarkvm-slipstream-plugin-manager" ]
locktick = [ "dep:locktick", "snarkvm-ledger-puzzle/locktick" ]
metrics = [ "dep:snarkvm-metrics" ]
rocks = [ "rocksdb", "smallvec" ]
serial = [
"snarkvm-console/serial",
Expand All @@ -43,6 +44,10 @@ wasm = [
]
test = [ ]

[dependencies.snarkvm-metrics]
workspace = true
optional = true

[dependencies.snarkvm-slipstream-plugin-manager]
workspace = true
optional = true
Expand Down
9 changes: 9 additions & 0 deletions ledger/store/src/block/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1559,6 +1559,15 @@ impl<N: Network, B: BlockStorage<N>> BlockStore<N, B> {
}
}

#[cfg(all(feature = "rocks", feature = "metrics"))]
impl<N: Network> BlockStore<N, crate::helpers::rocksdb::BlockDB<N>> {
/// Reads RocksDB internal properties and publishes them to the metrics registry.
/// This is a cheap, synchronous call — properties come from in-memory counters with no I/O.
pub fn export_rocksdb_metrics(&self) {
self.storage.export_rocksdb_metrics();
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
9 changes: 9 additions & 0 deletions ledger/store/src/helpers/rocksdb/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -268,3 +268,12 @@ impl<N: Network> BlockStorage<N> for BlockDB<N> {
}
}
}

#[cfg(all(feature = "rocks", feature = "metrics"))]
impl<N: Network> BlockDB<N> {
/// Reads RocksDB internal properties and publishes them to the metrics registry.
/// Any map can be used since they all share the same underlying RocksDB instance.
pub fn export_rocksdb_metrics(&self) {
self.id_map.export_rocksdb_metrics();
}
}
7 changes: 7 additions & 0 deletions ledger/store/src/helpers/rocksdb/internal/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@ impl<K: Serialize + DeserializeOwned, V: Serialize + DeserializeOwned> InnerData
let checkpoint = rocksdb::checkpoint::Checkpoint::new(&self.database)?;
checkpoint.create_checkpoint(path).map_err(|e| e.into_string())
}

/// Reads RocksDB internal properties and publishes them to the metrics registry.
/// Any map can be used since they all share the same underlying `RocksDB` instance.
#[cfg(feature = "metrics")]
pub fn export_rocksdb_metrics(&self) {
self.database.export_rocksdb_metrics();
}
}

impl<
Expand Down
58 changes: 58 additions & 0 deletions ledger/store/src/helpers/rocksdb/internal/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,64 @@ impl RocksDB {
fn are_atomic_writes_paused(&self) -> bool {
self.atomic_writes_paused.load(Ordering::SeqCst)
}

/// Reads key RocksDB internal properties and publishes them to the metrics registry.
///
/// This is a lightweight, synchronous call — properties are read from RocksDB's in-memory
/// counters with no disk I/O. Call it from an existing background loop (e.g. the
/// auto-checkpoint polling loop in snarkOS); there is no need to spawn a dedicated thread.
#[cfg(feature = "metrics")]
pub fn export_rocksdb_metrics(&self) {
use snarkvm_metrics::rocksdb as names;

/// Read a single integer property; silently skip on error (DB may be closing).
fn prop(db: &rocksdb::DB, key: &str) -> Option<u64> {
db.property_int_value(key).ok().flatten()
}

let db = &self.rocksdb;

// Compaction pressure
if let Some(v) = prop(db, "rocksdb.compaction-pending") {
snarkvm_metrics::gauge(names::COMPACTION_PENDING, v as f64);
}
if let Some(v) = prop(db, "rocksdb.estimate-pending-compaction-bytes") {
snarkvm_metrics::gauge(names::ESTIMATE_PENDING_COMPACTION_BYTES, v as f64);
}
if let Some(v) = prop(db, "rocksdb.num-running-compactions") {
snarkvm_metrics::gauge(names::NUM_RUNNING_COMPACTIONS, v as f64);
}
if let Some(v) = prop(db, "rocksdb.num-running-flushes") {
snarkvm_metrics::gauge(names::NUM_RUNNING_FLUSHES, v as f64);
}
if let Some(v) = prop(db, "rocksdb.mem-table-flush-pending") {
snarkvm_metrics::gauge(names::MEM_TABLE_FLUSH_PENDING, v as f64);
}

// Disk footprint
if let Some(v) = prop(db, "rocksdb.total-sst-files-size") {
snarkvm_metrics::gauge(names::TOTAL_SST_FILES_SIZE, v as f64);
}
if let Some(v) = prop(db, "rocksdb.live-sst-files-size") {
snarkvm_metrics::gauge(names::LIVE_SST_FILES_SIZE, v as f64);
}

// General state
if let Some(v) = prop(db, "rocksdb.estimate-num-keys") {
snarkvm_metrics::gauge(names::ESTIMATE_NUM_KEYS, v as f64);
}
if let Some(v) = prop(db, "rocksdb.num-snapshots") {
snarkvm_metrics::gauge(names::NUM_SNAPSHOTS, v as f64);
}

// Per-level SST file counts (levels 0–6)
for (level, &name) in names::NUM_FILES_AT_LEVEL.iter().enumerate() {
let key = format!("rocksdb.num-files-at-level{level}");
if let Some(v) = prop(db, &key) {
snarkvm_metrics::gauge(name, v as f64);
}
}
}
}

// impl RocksDB {
Expand Down
56 changes: 55 additions & 1 deletion metrics/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,66 @@

#![forbid(unsafe_code)]

const GAUGE_NAMES: [&str; 1] = [committee::TOTAL_STAKE];
const GAUGE_NAMES: &[&str] = &[
committee::TOTAL_STAKE,
rocksdb::COMPACTION_PENDING,
rocksdb::ESTIMATE_PENDING_COMPACTION_BYTES,
rocksdb::NUM_RUNNING_COMPACTIONS,
rocksdb::NUM_RUNNING_FLUSHES,
rocksdb::MEM_TABLE_FLUSH_PENDING,
rocksdb::TOTAL_SST_FILES_SIZE,
rocksdb::LIVE_SST_FILES_SIZE,
rocksdb::ESTIMATE_NUM_KEYS,
rocksdb::NUM_SNAPSHOTS,
rocksdb::NUM_FILES_AT_LEVEL[0],
rocksdb::NUM_FILES_AT_LEVEL[1],
rocksdb::NUM_FILES_AT_LEVEL[2],
rocksdb::NUM_FILES_AT_LEVEL[3],
rocksdb::NUM_FILES_AT_LEVEL[4],
rocksdb::NUM_FILES_AT_LEVEL[5],
rocksdb::NUM_FILES_AT_LEVEL[6],
];

pub mod committee {
pub const TOTAL_STAKE: &str = "snarkvm_ledger_committee_total_stake";
}

/// RocksDB internal database metrics.
///
/// Polled and published by calling `BlockStore::export_rocksdb_metrics()` from an existing
/// background loop (e.g. the auto-checkpoint task in snarkOS). All sizes are in bytes;
/// counts are dimensionless. Requires the `rocks` and `metrics` features on `snarkvm-ledger-store`.
pub mod rocksdb {
/// 1 if a compaction is pending (background compaction requested but not yet running), else 0.
pub const COMPACTION_PENDING: &str = "snarkvm_rocksdb_compaction_pending";
/// Estimated total bytes of data to be compacted. A sustained non-zero value signals backpressure.
pub const ESTIMATE_PENDING_COMPACTION_BYTES: &str = "snarkvm_rocksdb_estimate_pending_compaction_bytes";
/// Number of compactions currently running in the background.
pub const NUM_RUNNING_COMPACTIONS: &str = "snarkvm_rocksdb_num_running_compactions";
/// Number of memtable flushes currently running.
pub const NUM_RUNNING_FLUSHES: &str = "snarkvm_rocksdb_num_running_flushes";
/// 1 if a memtable flush is pending (memtable full but flush not yet started), else 0.
pub const MEM_TABLE_FLUSH_PENDING: &str = "snarkvm_rocksdb_mem_table_flush_pending";
/// Total size of all SST files on disk (includes files pending deletion).
pub const TOTAL_SST_FILES_SIZE: &str = "snarkvm_rocksdb_total_sst_files_size_bytes";
/// Size of live (referenced) SST files only.
pub const LIVE_SST_FILES_SIZE: &str = "snarkvm_rocksdb_live_sst_files_size_bytes";
/// Estimated number of keys in the database.
pub const ESTIMATE_NUM_KEYS: &str = "snarkvm_rocksdb_estimate_num_keys";
/// Number of snapshots currently held (non-zero blocks deletion of old SST files).
pub const NUM_SNAPSHOTS: &str = "snarkvm_rocksdb_num_snapshots";
/// Number of SST files per LSM level (levels 0–6).
pub const NUM_FILES_AT_LEVEL: [&str; 7] = [
"snarkvm_rocksdb_num_files_at_level0",
"snarkvm_rocksdb_num_files_at_level1",
"snarkvm_rocksdb_num_files_at_level2",
"snarkvm_rocksdb_num_files_at_level3",
"snarkvm_rocksdb_num_files_at_level4",
"snarkvm_rocksdb_num_files_at_level5",
"snarkvm_rocksdb_num_files_at_level6",
];
}

/// Registers all snarkVM metrics.
pub fn register_metrics() {
for name in GAUGE_NAMES {
Expand Down