feat: add TreeConfig (#9833)

This commit is contained in:
Federico Gimenez
2024-07-26 17:16:51 +02:00
committed by GitHub
parent 41c34608b0
commit 77f5ed1a7b
6 changed files with 106 additions and 16 deletions

1
Cargo.lock generated
View File

@ -7765,6 +7765,7 @@ dependencies = [
"reth-consensus",
"reth-db",
"reth-e2e-test-utils",
"reth-engine-tree",
"reth-ethereum-engine",
"reth-ethereum-engine-primitives",
"reth-ethereum-payload-builder",

View File

@ -0,0 +1,74 @@
//! Engine tree configuration.
const DEFAULT_PERSISTENCE_THRESHOLD: u64 = 256;
const DEFAULT_BLOCK_BUFFER_LIMIT: u32 = 256;
const DEFAULT_MAX_INVALID_HEADER_CACHE_LENGTH: u32 = 256;
/// The configuration of the engine tree.
#[derive(Debug)]
pub struct TreeConfig {
/// Maximum number of blocks to be kept only in memory without triggering persistence.
persistence_threshold: u64,
/// Number of pending blocks that cannot be executed due to missing parent and
/// are kept in cache.
block_buffer_limit: u32,
/// Number of invalid headers to keep in cache.
max_invalid_header_cache_length: u32,
}
impl Default for TreeConfig {
fn default() -> Self {
Self {
persistence_threshold: DEFAULT_PERSISTENCE_THRESHOLD,
block_buffer_limit: DEFAULT_BLOCK_BUFFER_LIMIT,
max_invalid_header_cache_length: DEFAULT_MAX_INVALID_HEADER_CACHE_LENGTH,
}
}
}
impl TreeConfig {
/// Create engine tree configuration.
pub const fn new(
persistence_threshold: u64,
block_buffer_limit: u32,
max_invalid_header_cache_length: u32,
) -> Self {
Self { persistence_threshold, block_buffer_limit, max_invalid_header_cache_length }
}
/// Return the persistence threshold.
pub const fn persistence_threshold(&self) -> u64 {
self.persistence_threshold
}
/// Return the block buffer limit.
pub const fn block_buffer_limit(&self) -> u32 {
self.block_buffer_limit
}
/// Return the maximum invalid cache header length.
pub const fn max_invalid_header_cache_length(&self) -> u32 {
self.max_invalid_header_cache_length
}
/// Setter for persistence threshold.
pub const fn with_persistence_threshold(mut self, persistence_threshold: u64) -> Self {
self.persistence_threshold = persistence_threshold;
self
}
/// Setter for block buffer limit.
pub const fn with_block_buffer_limit(mut self, block_buffer_limit: u32) -> Self {
self.block_buffer_limit = block_buffer_limit;
self
}
/// Setter for maximum invalid header cache length.
pub const fn with_max_invalid_header_cache_length(
mut self,
max_invalid_header_cache_length: u32,
) -> Self {
self.max_invalid_header_cache_length = max_invalid_header_cache_length;
self
}
}

View File

@ -49,13 +49,8 @@ use tokio::sync::{
};
use tracing::*;
/// Maximum number of blocks to be kept only in memory without triggering persistence.
const PERSISTENCE_THRESHOLD: u64 = 256;
/// Number of pending blocks that cannot be executed due to missing parent and
/// are kept in cache.
const DEFAULT_BLOCK_BUFFER_LIMIT: u32 = 256;
/// Number of invalid headers to keep in cache.
const DEFAULT_MAX_INVALID_HEADER_CACHE_LENGTH: u32 = 256;
mod config;
pub use config::TreeConfig;
/// Keeps track of the state of the tree.
///
@ -387,6 +382,8 @@ pub struct EngineApiTreeHandlerImpl<P, E, T: EngineTypes> {
/// Handle to the payload builder that will receive payload attributes for valid forkchoice
/// updates
payload_builder: PayloadBuilderHandle<T>,
/// Configuration settings.
config: TreeConfig,
}
impl<P, E, T> EngineApiTreeHandlerImpl<P, E, T>
@ -407,6 +404,7 @@ where
canonical_in_memory_state: CanonicalInMemoryState,
persistence: PersistenceHandle,
payload_builder: PayloadBuilderHandle<T>,
config: TreeConfig,
) -> Self {
Self {
provider,
@ -421,6 +419,7 @@ where
state,
canonical_in_memory_state,
payload_builder,
config,
}
}
@ -437,14 +436,15 @@ where
persistence: PersistenceHandle,
payload_builder: PayloadBuilderHandle<T>,
canonical_in_memory_state: CanonicalInMemoryState,
config: TreeConfig,
) -> UnboundedReceiver<EngineApiEvent> {
let best_block_number = provider.best_block_number().unwrap_or(0);
let header = provider.sealed_header(best_block_number).ok().flatten().unwrap_or_default();
let (tx, outgoing) = tokio::sync::mpsc::unbounded_channel();
let state = EngineApiTreeState::new(
DEFAULT_BLOCK_BUFFER_LIMIT,
DEFAULT_MAX_INVALID_HEADER_CACHE_LENGTH,
config.block_buffer_limit(),
config.max_invalid_header_cache_length(),
header.num_hash(),
);
@ -459,6 +459,7 @@ where
canonical_in_memory_state,
persistence,
payload_builder,
config,
);
std::thread::Builder::new().name("Tree Task".to_string()).spawn(|| task.run()).unwrap();
outgoing
@ -657,12 +658,12 @@ where
fn should_persist(&self) -> bool {
self.state.tree_state.max_block_number() -
self.persistence_state.last_persisted_block_number >=
PERSISTENCE_THRESHOLD
self.config.persistence_threshold()
}
fn get_blocks_to_persist(&self) -> Vec<ExecutedBlock> {
let start = self.persistence_state.last_persisted_block_number;
let end = start + PERSISTENCE_THRESHOLD;
let end = start + self.config.persistence_threshold();
// NOTE: this is an exclusive range, to try to include exactly PERSISTENCE_THRESHOLD blocks
self.state
@ -1607,6 +1608,7 @@ mod tests {
canonical_in_memory_state,
persistence_handle,
payload_builder,
TreeConfig::default(),
);
Self { tree, to_tree_tx, blocks: vec![], action_rx, payload_command_rx }
@ -1675,6 +1677,7 @@ mod tests {
canonical_in_memory_state,
persistence_handle,
payload_builder,
TreeConfig::default(),
);
let last_executed_block = blocks.last().unwrap().clone();
let pending = Some(BlockState::new(last_executed_block));
@ -1688,8 +1691,10 @@ mod tests {
async fn test_tree_persist_blocks() {
// we need more than PERSISTENCE_THRESHOLD blocks to trigger the
// persistence task.
let tree_config = TreeConfig::default();
let TestHarness { tree, to_tree_tx, action_rx, mut blocks, payload_command_rx } =
get_default_test_harness(PERSISTENCE_THRESHOLD + 1);
get_default_test_harness(tree_config.persistence_threshold() + 1);
std::thread::Builder::new().name("Tree Task".to_string()).spawn(|| tree.run()).unwrap();
// send a message to the tree to enter the main loop.
@ -1699,7 +1704,7 @@ mod tests {
if let PersistenceAction::SaveBlocks((saved_blocks, _)) = received_action {
// only PERSISTENCE_THRESHOLD will be persisted
blocks.pop();
assert_eq!(saved_blocks.len() as u64, PERSISTENCE_THRESHOLD);
assert_eq!(saved_blocks.len() as u64, tree_config.persistence_threshold());
assert_eq!(saved_blocks, blocks);
} else {
panic!("unexpected action received {received_action:?}");
@ -1731,8 +1736,10 @@ mod tests {
#[tokio::test]
async fn test_engine_request_during_backfill() {
let tree_config = TreeConfig::default();
let TestHarness { mut tree, to_tree_tx, action_rx, blocks, payload_command_rx } =
get_default_test_harness(PERSISTENCE_THRESHOLD);
get_default_test_harness(tree_config.persistence_threshold());
// set backfill active
tree.backfill_sync_state = BackfillSyncState::Active;
@ -1754,7 +1761,7 @@ mod tests {
#[tokio::test]
async fn test_holesky_payload() {
let s = include_str!("../test-data/holesky/1.rlp");
let s = include_str!("../../test-data/holesky/1.rlp");
let data = Bytes::from_str(s).unwrap();
let block = Block::decode(&mut data.as_ref()).unwrap();
let sealed = block.seal_slow();

View File

@ -8,7 +8,7 @@ use reth_engine_tree::{
download::BasicBlockDownloader,
engine::{EngineApiRequestHandler, EngineHandler},
persistence::PersistenceHandle,
tree::EngineApiTreeHandlerImpl,
tree::{EngineApiTreeHandlerImpl, TreeConfig},
};
pub use reth_engine_tree::{
chain::{ChainEvent, ChainOrchestrator},
@ -68,6 +68,7 @@ where
blockchain_db: BlockchainProvider2<DB>,
pruner: Pruner<DB, ProviderFactory<DB>>,
payload_builder: PayloadBuilderHandle<EthEngineTypes>,
tree_config: TreeConfig,
) -> Self {
let consensus = Arc::new(EthBeaconConsensus::new(chain_spec.clone()));
let downloader = BasicBlockDownloader::new(client, consensus.clone());
@ -89,6 +90,7 @@ where
persistence_handle,
payload_builder,
canonical_in_memory_state,
tree_config,
);
let engine_handler = EngineApiRequestHandler::new(to_tree_tx, from_tree);
@ -174,6 +176,7 @@ mod tests {
blockchain_db,
pruner,
PayloadBuilderHandle::new(tx),
TreeConfig::default(),
);
}
}

View File

@ -36,6 +36,7 @@ reth-node-events.workspace = true
reth-node-core.workspace = true
reth-exex.workspace = true
reth-blockchain-tree.workspace = true
reth-engine-tree.workspace = true
# misc
eyre.workspace = true

View File

@ -6,6 +6,7 @@ use reth_beacon_consensus::{
BeaconConsensusEngineHandle,
};
use reth_blockchain_tree::BlockchainTreeConfig;
use reth_engine_tree::tree::TreeConfig;
use reth_ethereum_engine::service::{ChainEvent, EthService};
use reth_ethereum_engine_primitives::EthEngineTypes;
use reth_exex::ExExManagerHandle;
@ -171,6 +172,8 @@ where
let pruner_events = pruner.events();
info!(target: "reth::cli", prune_config=?ctx.prune_config().unwrap_or_default(), "Pruner initialized");
let tree_config = TreeConfig::default().with_persistence_threshold(120);
// Configure the consensus engine
let mut eth_service = EthService::new(
ctx.chain_spec(),
@ -182,6 +185,7 @@ where
ctx.blockchain_db().clone(),
pruner,
ctx.components().payload_builder().clone(),
tree_config,
);
let event_sender = EventSender::default();