Implement metrics for overlay state provider operations

This commit is contained in:
Yong Kang
2025-10-30 23:02:28 +08:00
parent 59bf11779c
commit 99d3f66719
3 changed files with 76 additions and 3 deletions

View File

@@ -44,8 +44,8 @@ revm-state = { workspace = true, optional = true }
tracing.workspace = true
# metrics
reth-metrics.workspace = true
metrics.workspace = true
reth-metrics = { workspace = true, optional = true }
metrics = { workspace = true, optional = true }
# misc
itertools.workspace = true
@@ -81,6 +81,8 @@ rand.workspace = true
tokio = { workspace = true, features = ["sync", "macros", "rt-multi-thread"] }
[features]
default = ["metrics"]
metrics = ["reth-metrics", "dep:metrics"]
test-utils = [
"reth-db/test-utils",
"reth-nippy-jar/test-utils",

View File

@@ -19,6 +19,12 @@ use reth_trie_db::{
use std::sync::Arc;
use tracing::debug;
#[cfg(feature = "metrics")]
#[path = "overlay_metrics.rs"]
mod overlay_metrics;
#[cfg(feature = "metrics")]
use overlay_metrics::OverlayStateProviderMetrics;
/// Factory for creating overlay state providers with optional reverts and overlays.
///
/// This factory allows building an `OverlayStateProvider` whose DB state has been reverted to a
@@ -33,17 +39,33 @@ pub struct OverlayStateProviderFactory<F> {
trie_overlay: Option<Arc<TrieUpdatesSorted>>,
/// Optional hashed state overlay
hashed_state_overlay: Option<Arc<HashedPostStateSorted>>,
/// Metrics for overlay state provider operations
#[cfg(feature = "metrics")]
metrics: OverlayStateProviderMetrics,
}
impl<F> OverlayStateProviderFactory<F> {
/// Create a new overlay state provider factory
#[cfg(not(feature = "metrics"))]
pub const fn new(factory: F) -> Self {
Self { factory, block_hash: None, trie_overlay: None, hashed_state_overlay: None }
}
/// Create a new overlay state provider factory
#[cfg(feature = "metrics")]
pub fn new(factory: F) -> Self {
Self {
factory,
block_hash: None,
trie_overlay: None,
hashed_state_overlay: None,
metrics: OverlayStateProviderMetrics::default(),
}
}
/// Set the block hash for collecting reverts. All state will be reverted to the point
/// _after_ this block has been processed.
pub const fn with_block_hash(mut self, block_hash: Option<B256>) -> Self {
pub fn with_block_hash(mut self, block_hash: Option<B256>) -> Self {
self.block_hash = block_hash;
self
}
@@ -136,32 +158,59 @@ where
/// Create a read-only [`OverlayStateProvider`].
fn database_provider_ro(&self) -> ProviderResult<OverlayStateProvider<F::Provider>> {
#[cfg(feature = "metrics")]
let total_start = std::time::Instant::now();
// Get a read-only provider
#[cfg(feature = "metrics")]
let base_start = std::time::Instant::now();
let provider = self.factory.database_provider_ro()?;
#[cfg(feature = "metrics")]
self.metrics.base_provider_creation_duration.record(base_start.elapsed().as_secs_f64());
// If block_hash is provided, collect reverts
let (trie_updates, hashed_state) = if let Some(block_hash) = self.block_hash {
// Convert block hash to block number
#[cfg(feature = "metrics")]
let lookup_start = std::time::Instant::now();
let from_block = provider
.convert_hash_or_number(block_hash.into())?
.ok_or_else(|| ProviderError::BlockHashNotFound(block_hash))?;
#[cfg(feature = "metrics")]
self.metrics.block_hash_lookup_duration.record(lookup_start.elapsed().as_secs_f64());
// Validate that we have sufficient changesets for the requested block
self.validate_changesets_availability(&provider, from_block)?;
// Collect trie reverts
#[cfg(feature = "metrics")]
let trie_start = std::time::Instant::now();
let mut trie_reverts = provider.trie_reverts(from_block + 1)?;
#[cfg(feature = "metrics")]
self.metrics.trie_reverts_duration.record(trie_start.elapsed().as_secs_f64());
// Collect state reverts
//
// TODO(mediocregopher) make from_reverts return sorted
// https://github.com/paradigmxyz/reth/issues/19382
#[cfg(feature = "metrics")]
let state_start = std::time::Instant::now();
let mut hashed_state_reverts = HashedPostState::from_reverts::<KeccakKeyHasher>(
provider.tx_ref(),
from_block + 1..,
)?
.into_sorted();
#[cfg(feature = "metrics")]
self.metrics.state_reverts_duration.record(state_start.elapsed().as_secs_f64());
// Extend with overlays if provided. If the reverts are empty we should just use the
// overlays directly, because `extend_ref` will actually clone the overlay.
let trie_updates = match self.trie_overlay.as_ref() {
@@ -206,6 +255,11 @@ where
(trie_updates, hashed_state)
};
#[cfg(feature = "metrics")]
self.metrics
.total_database_provider_ro_duration
.record(total_start.elapsed().as_secs_f64());
Ok(OverlayStateProvider::new(provider, trie_updates, hashed_state))
}
}

View File

@@ -0,0 +1,17 @@
use reth_metrics::{metrics::Histogram, Metrics};
/// Metrics for overlay state provider operations.
#[derive(Clone, Metrics)]
#[metrics(scope = "storage.overlay_state_provider")]
pub(super) struct OverlayStateProviderMetrics {
/// Time to create base database provider
pub(super) base_provider_creation_duration: Histogram,
/// Time to convert block hash to block number
pub(super) block_hash_lookup_duration: Histogram,
/// Time to fetch trie reverts from DB
pub(super) trie_reverts_duration: Histogram,
/// Time to fetch state reverts from DB
pub(super) state_reverts_duration: Histogram,
/// Total time in database_provider_ro()
pub(super) total_database_provider_ro_duration: Histogram,
}