feat(storage, blockchain-tree): disable backtrace on long read tx when it's safe (#6177)

This commit is contained in:
Alexey Shekhirin
2024-01-23 13:44:01 +00:00
committed by GitHub
parent a6f8e449f7
commit 1e4d125616
6 changed files with 38 additions and 3 deletions

2
Cargo.lock generated
View File

@ -5760,6 +5760,7 @@ dependencies = [
"alloy-chains",
"alloy-rlp",
"aquamarine",
"assert_matches",
"backon",
"clap",
"comfy-table",
@ -5774,6 +5775,7 @@ dependencies = [
"itertools 0.12.0",
"jemalloc-ctl",
"jemallocator",
"jsonrpsee",
"metrics",
"metrics-exporter-prometheus",
"metrics-process",

View File

@ -1174,7 +1174,13 @@ impl<DB: Database, EF: ExecutorFactory> BlockchainTree<DB, EF> {
}
None => {
debug!(target: "blockchain_tree", blocks = ?block_hash_numbers, "Recomputing state root for insert");
let provider = self.externals.provider_factory.provider()?;
let provider = self
.externals
.provider_factory
.provider()?
// State root calculation can take a while, and we're sure no write transaction
// will be open in parallel. See https://github.com/paradigmxyz/reth/issues/6168.
.disable_backtrace_on_long_read_transaction();
let (state_root, trie_updates) = hashed_state
.state_root_with_updates(provider.tx_ref())
.map_err(Into::<DatabaseError>::into)?;

View File

@ -66,6 +66,8 @@ impl DbTx for TxMock {
fn entries<T: Table>(&self) -> Result<usize, DatabaseError> {
Ok(self._table.len())
}
fn disable_backtrace_on_long_read_transaction(&mut self) {}
}
impl DbTxMut for TxMock {

View File

@ -24,6 +24,8 @@ pub trait DbTx: Send + Sync {
fn cursor_dup_read<T: DupSort>(&self) -> Result<Self::DupCursor<T>, DatabaseError>;
/// Returns number of entries in the table.
fn entries<T: Table>(&self) -> Result<usize, DatabaseError>;
/// Disables backtrace recording for this read transaction when it's open for too long.
fn disable_backtrace_on_long_read_transaction(&mut self);
}
/// Read write transaction that allows writing to database

View File

@ -156,6 +156,9 @@ struct MetricsHandler<K: TransactionKind> {
/// If `true`, the metric about transaction closing has already been recorded and we don't need
/// to do anything on [Drop::drop].
close_recorded: bool,
/// If `true`, the backtrace of transaction will be recorded and logged.
/// See [MetricsHandler::log_backtrace_on_long_read_transaction].
record_backtrace: bool,
/// If `true`, the backtrace of transaction has already been recorded and logged.
/// See [MetricsHandler::log_backtrace_on_long_read_transaction].
backtrace_recorded: AtomicBool,
@ -168,6 +171,7 @@ impl<K: TransactionKind> MetricsHandler<K> {
txn_id,
start: Instant::now(),
close_recorded: false,
record_backtrace: true,
backtrace_recorded: AtomicBool::new(false),
_marker: PhantomData,
}
@ -194,13 +198,14 @@ impl<K: TransactionKind> MetricsHandler<K> {
}
/// Logs the backtrace of current call if the duration that the read transaction has been open
/// is more than [LONG_TRANSACTION_DURATION].
/// is more than [LONG_TRANSACTION_DURATION] and `record_backtrace == true`.
/// The backtrace is recorded and logged just once, guaranteed by `backtrace_recorded` atomic.
///
/// NOTE: Backtrace is recorded using [Backtrace::force_capture], so `RUST_BACKTRACE` env var is
/// not needed.
fn log_backtrace_on_long_read_transaction(&self) {
if !self.backtrace_recorded.load(Ordering::Relaxed) &&
if self.record_backtrace &&
!self.backtrace_recorded.load(Ordering::Relaxed) &&
self.transaction_mode().is_read_only()
{
let open_duration = self.start.elapsed();
@ -283,6 +288,13 @@ impl<K: TransactionKind> DbTx for Tx<K> {
.map_err(|e| DatabaseError::Stats(e.into()))?
.entries())
}
/// Disables backtrace recording for this read transaction when it's open for too long.
fn disable_backtrace_on_long_read_transaction(&mut self) {
if let Some(metrics_handler) = self.metrics_handler.as_mut() {
metrics_handler.record_backtrace = false;
}
}
}
impl DbTxMut for Tx<RW> {

View File

@ -246,6 +246,17 @@ impl<TX: DbTx> DatabaseProvider<TX> {
.collect::<Result<Vec<_>, DatabaseError>>()
}
/// Disables backtrace recording for the underlying read database transaction when it's open for
/// too long.
///
/// CAUTION: In most of the cases, you want to keep backtraces on long read transactions
/// enabled. Use this only if you're sure that no write transaction is open in parallel, meaning
/// that Reth as a node is offline and not progressing.
pub fn disable_backtrace_on_long_read_transaction(mut self) -> Self {
self.tx.disable_backtrace_on_long_read_transaction();
self
}
/// Gets data within a specified range, potentially spanning different snapshots and database.
///
/// # Arguments