feat(stages): duration threshold for Execution stage (#6073)

This commit is contained in:
Alexey Shekhirin
2024-01-17 16:45:40 +00:00
committed by GitHub
parent f23250d88c
commit 43167eabda
11 changed files with 45 additions and 25 deletions

View File

@ -912,6 +912,7 @@ impl NodeConfig {
max_blocks: stage_config.execution.max_blocks, max_blocks: stage_config.execution.max_blocks,
max_changes: stage_config.execution.max_changes, max_changes: stage_config.execution.max_changes,
max_cumulative_gas: stage_config.execution.max_cumulative_gas, max_cumulative_gas: stage_config.execution.max_cumulative_gas,
max_duration: stage_config.execution.max_duration,
}, },
stage_config stage_config
.merkle .merkle

View File

@ -136,6 +136,7 @@ impl Command {
max_blocks: None, max_blocks: None,
max_changes: None, max_changes: None,
max_cumulative_gas: None, max_cumulative_gas: None,
max_duration: None,
}, },
stage_conf stage_conf
.merkle .merkle

View File

@ -205,6 +205,7 @@ impl Command {
max_blocks: Some(1), max_blocks: Some(1),
max_changes: None, max_changes: None,
max_cumulative_gas: None, max_cumulative_gas: None,
max_duration: None,
}, },
MERKLE_STAGE_DEFAULT_CLEAN_THRESHOLD, MERKLE_STAGE_DEFAULT_CLEAN_THRESHOLD,
PruneModes::all(), PruneModes::all(),

View File

@ -183,6 +183,7 @@ impl ImportCommand {
max_blocks: config.stages.execution.max_blocks, max_blocks: config.stages.execution.max_blocks,
max_changes: config.stages.execution.max_changes, max_changes: config.stages.execution.max_changes,
max_cumulative_gas: config.stages.execution.max_cumulative_gas, max_cumulative_gas: config.stages.execution.max_cumulative_gas,
max_duration: config.stages.execution.max_duration,
}, },
config config
.stages .stages

View File

@ -122,7 +122,7 @@ impl<DB> NodeState<DB> {
%target, %target,
%stage_progress, %stage_progress,
%stage_eta, %stage_eta,
message, "{message}",
) )
} else { } else {
info!( info!(
@ -131,7 +131,7 @@ impl<DB> NodeState<DB> {
checkpoint = %checkpoint.block_number, checkpoint = %checkpoint.block_number,
%target, %target,
%stage_progress, %stage_progress,
message, "{message}",
) )
} }
} }

View File

@ -73,6 +73,7 @@ async fn unwind_and_copy<DB: Database>(
max_blocks: Some(u64::MAX), max_blocks: Some(u64::MAX),
max_changes: None, max_changes: None,
max_cumulative_gas: None, max_cumulative_gas: None,
max_duration: None,
}, },
MERKLE_STAGE_DEFAULT_CLEAN_THRESHOLD, MERKLE_STAGE_DEFAULT_CLEAN_THRESHOLD,
PruneModes::all(), PruneModes::all(),

View File

@ -204,6 +204,7 @@ impl Command {
max_blocks: Some(batch_size), max_blocks: Some(batch_size),
max_changes: None, max_changes: None,
max_cumulative_gas: None, max_cumulative_gas: None,
max_duration: None,
}, },
config.stages.merkle.clean_threshold, config.stages.merkle.clean_threshold,
config.prune.map(|prune| prune.segments).unwrap_or_default(), config.prune.map(|prune| prune.segments).unwrap_or_default(),

View File

@ -131,22 +131,21 @@ The execution stage executes historical transactions. This stage is generally ve
Each executed transaction also generates a number of changesets, and mutates the current state of accounts and storage. Each executed transaction also generates a number of changesets, and mutates the current state of accounts and storage.
For this reason, there are two ways to control how much work to perform before the results are written to disk. For this reason, there are several ways to control how much work to perform before the results are written to disk.
```toml ```toml
[stages.execution] [stages.execution]
# The maximum amount of blocks to execute before writing the results to disk. # The maximum number of blocks to process before the execution stage commits.
max_blocks = 500000 max_blocks = 500000
# The maximum amount of account and storage changes to collect before writing # The maximum number of state changes to keep in memory before the execution stage commits.
# the results to disk.
max_changes = 5000000 max_changes = 5000000
# The maximum cumulative amount of gas to process before the execution stage commits.
max_cumulative_gas = 1500000000000 # 30_000_000 * 50_000_000
# The maximum time spent on blocks processing before the execution stage commits.
max_duration = '10m'
``` ```
Either one of `max_blocks` or `max_changes` must be specified, and both can also be specified at the same time: For all thresholds specified, the first to be hit will determine when the results are written to disk.
- If only `max_blocks` is specified, reth will execute (up to) that amount of blocks before writing to disk.
- If only `max_changes` is specified, reth will execute as many blocks as possible until the target amount of state transitions have occurred before writing to disk.
- If both are specified, then the first threshold to be hit will determine when the results are written to disk.
Lower values correspond to more frequent disk writes, but also lower memory consumption. A lower value also negatively impacts sync speed, since reth keeps a cache around for the entire duration of blocks executed in the same range. Lower values correspond to more frequent disk writes, but also lower memory consumption. A lower value also negatively impacts sync speed, since reth keeps a cache around for the entire duration of blocks executed in the same range.

View File

@ -5,7 +5,7 @@ use reth_network::{NetworkConfigBuilder, PeersConfig, SessionsConfig};
use reth_primitives::PruneModes; use reth_primitives::PruneModes;
use secp256k1::SecretKey; use secp256k1::SecretKey;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::path::PathBuf; use std::{path::PathBuf, time::Duration};
/// Configuration for the reth node. /// Configuration for the reth node.
#[derive(Debug, Clone, Default, Deserialize, PartialEq, Serialize)] #[derive(Debug, Clone, Default, Deserialize, PartialEq, Serialize)]
@ -181,10 +181,12 @@ impl Default for SenderRecoveryConfig {
pub struct ExecutionConfig { pub struct ExecutionConfig {
/// The maximum number of blocks to process before the execution stage commits. /// The maximum number of blocks to process before the execution stage commits.
pub max_blocks: Option<u64>, pub max_blocks: Option<u64>,
/// The maximum amount of state changes to keep in memory before the execution stage commits. /// The maximum number of state changes to keep in memory before the execution stage commits.
pub max_changes: Option<u64>, pub max_changes: Option<u64>,
/// The maximum gas to process before the execution stage commits. /// The maximum cumulative amount of gas to process before the execution stage commits.
pub max_cumulative_gas: Option<u64>, pub max_cumulative_gas: Option<u64>,
/// The maximum time spent on blocks processing before the execution stage commits.
pub max_duration: Option<Duration>,
} }
impl Default for ExecutionConfig { impl Default for ExecutionConfig {
@ -194,6 +196,8 @@ impl Default for ExecutionConfig {
max_changes: Some(5_000_000), max_changes: Some(5_000_000),
// 50k full blocks of 30M gas // 50k full blocks of 30M gas
max_cumulative_gas: Some(30_000_000 * 50_000), max_cumulative_gas: Some(30_000_000 * 50_000),
// 10 minutes
max_duration: Some(Duration::from_secs(10 * 60)),
} }
} }
} }

View File

@ -135,12 +135,15 @@ impl<EF: ExecutorFactory> ExecutionStage<EF> {
let mut fetch_block_duration = Duration::default(); let mut fetch_block_duration = Duration::default();
let mut execution_duration = Duration::default(); let mut execution_duration = Duration::default();
debug!(target: "sync::stages::execution", start = start_block, end = max_block, "Executing range"); debug!(target: "sync::stages::execution", start = start_block, end = max_block, "Executing range");
// Execute block range
// Execute block range
let mut cumulative_gas = 0; let mut cumulative_gas = 0;
let batch_start = Instant::now();
for block_number in start_block..=max_block { for block_number in start_block..=max_block {
let time = Instant::now(); // Fetch the block
let fetch_block_start = Instant::now();
let td = provider let td = provider
.header_td_by_number(block_number)? .header_td_by_number(block_number)?
.ok_or_else(|| ProviderError::HeaderNotFound(block_number.into()))?; .ok_or_else(|| ProviderError::HeaderNotFound(block_number.into()))?;
@ -150,21 +153,20 @@ impl<EF: ExecutorFactory> ExecutionStage<EF> {
.block_with_senders(block_number.into(), TransactionVariant::NoHash)? .block_with_senders(block_number.into(), TransactionVariant::NoHash)?
.ok_or_else(|| ProviderError::BlockNotFound(block_number.into()))?; .ok_or_else(|| ProviderError::BlockNotFound(block_number.into()))?;
fetch_block_duration += time.elapsed(); fetch_block_duration += fetch_block_start.elapsed();
cumulative_gas += block.gas_used; cumulative_gas += block.gas_used;
// Configure the executor to use the current state. // Configure the executor to use the current state.
trace!(target: "sync::stages::execution", number = block_number, txs = block.body.len(), "Executing block"); trace!(target: "sync::stages::execution", number = block_number, txs = block.body.len(), "Executing block");
let time = Instant::now();
// Execute the block // Execute the block
let execute_start = Instant::now();
executor.execute_and_verify_receipt(&block, td).map_err(|error| StageError::Block { executor.execute_and_verify_receipt(&block, td).map_err(|error| StageError::Block {
block: Box::new(block.header.clone().seal_slow()), block: Box::new(block.header.clone().seal_slow()),
error: BlockErrorKind::Execution(error), error: BlockErrorKind::Execution(error),
})?; })?;
execution_duration += execute_start.elapsed();
execution_duration += time.elapsed();
// Gas metrics // Gas metrics
if let Some(metrics_tx) = &mut self.metrics_tx { if let Some(metrics_tx) = &mut self.metrics_tx {
@ -182,6 +184,7 @@ impl<EF: ExecutorFactory> ExecutionStage<EF> {
block_number - start_block, block_number - start_block,
bundle_size_hint, bundle_size_hint,
cumulative_gas, cumulative_gas,
batch_start.elapsed(),
) { ) {
break break
} }
@ -451,12 +454,14 @@ impl<EF: ExecutorFactory, DB: Database> Stage<DB> for ExecutionStage<EF> {
/// current database transaction, which frees up memory. /// current database transaction, which frees up memory.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ExecutionStageThresholds { pub struct ExecutionStageThresholds {
/// The maximum number of blocks to process before the execution stage commits. /// The maximum number of blocks to execute before the execution stage commits.
pub max_blocks: Option<u64>, pub max_blocks: Option<u64>,
/// The maximum amount of state changes to keep in memory before the execution stage commits. /// The maximum number of state changes to keep in memory before the execution stage commits.
pub max_changes: Option<u64>, pub max_changes: Option<u64>,
/// The maximum amount of cumultive gas used in the batch. /// The maximum cumulative amount of gas to process before the execution stage commits.
pub max_cumulative_gas: Option<u64>, pub max_cumulative_gas: Option<u64>,
/// The maximum spent on blocks processing before the execution stage commits.
pub max_duration: Option<Duration>,
} }
impl Default for ExecutionStageThresholds { impl Default for ExecutionStageThresholds {
@ -464,8 +469,10 @@ impl Default for ExecutionStageThresholds {
Self { Self {
max_blocks: Some(500_000), max_blocks: Some(500_000),
max_changes: Some(5_000_000), max_changes: Some(5_000_000),
// 30M gas per block on 50k blocks // 50k full blocks of 30M gas
max_cumulative_gas: Some(30_000_000 * 50_000), max_cumulative_gas: Some(30_000_000 * 50_000),
// 10 minutes
max_duration: Some(Duration::from_secs(10 * 60)),
} }
} }
} }
@ -478,10 +485,12 @@ impl ExecutionStageThresholds {
blocks_processed: u64, blocks_processed: u64,
changes_processed: u64, changes_processed: u64,
cumulative_gas_used: u64, cumulative_gas_used: u64,
elapsed: Duration,
) -> bool { ) -> bool {
blocks_processed >= self.max_blocks.unwrap_or(u64::MAX) || blocks_processed >= self.max_blocks.unwrap_or(u64::MAX) ||
changes_processed >= self.max_changes.unwrap_or(u64::MAX) || changes_processed >= self.max_changes.unwrap_or(u64::MAX) ||
cumulative_gas_used >= self.max_cumulative_gas.unwrap_or(u64::MAX) cumulative_gas_used >= self.max_cumulative_gas.unwrap_or(u64::MAX) ||
elapsed >= self.max_duration.unwrap_or(Duration::MAX)
} }
} }
@ -511,6 +520,7 @@ mod tests {
max_blocks: Some(100), max_blocks: Some(100),
max_changes: None, max_changes: None,
max_cumulative_gas: None, max_cumulative_gas: None,
max_duration: None,
}, },
MERKLE_STAGE_DEFAULT_CLEAN_THRESHOLD, MERKLE_STAGE_DEFAULT_CLEAN_THRESHOLD,
PruneModes::none(), PruneModes::none(),

View File

@ -135,6 +135,7 @@ mod tests {
max_blocks: Some(100), max_blocks: Some(100),
max_changes: None, max_changes: None,
max_cumulative_gas: None, max_cumulative_gas: None,
max_duration: None,
}, },
MERKLE_STAGE_DEFAULT_CLEAN_THRESHOLD, MERKLE_STAGE_DEFAULT_CLEAN_THRESHOLD,
prune_modes.clone(), prune_modes.clone(),