Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: run StaticFileProvider::check_consistency on start up #8143

Merged
merged 74 commits into from
Jun 1, 2024
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
2070756
add StaticFileProvider::check_consistency
joshieDo May 7, 2024
a81ee6f
Merge remote-tracking branch 'origin/main' into joshie/boot-up-check
joshieDo May 8, 2024
65495fb
use PipelineTarget::Unwind
joshieDo May 8, 2024
d1ff55b
Merge remote-tracking branch 'origin/main' into joshie/boot-up-check
joshieDo May 11, 2024
d8a44dd
generalize check_invariants and add docs
joshieDo May 11, 2024
e06334a
add NoopBlockExecutorProvider
joshieDo May 11, 2024
c820410
check consistency on onLaunchContextWith::create_provider_factory
joshieDo May 11, 2024
175680d
Merge branch 'main' into joshie/boot-up-check
joshieDo May 12, 2024
5737636
pass config to unwind pipeline on create_provider_factory
joshieDo May 15, 2024
76c91f6
pass has_receipt_pruning to check_consistency
joshieDo May 15, 2024
3aba5cc
missing arg
joshieDo May 15, 2024
0d4a1ae
add docs
joshieDo May 15, 2024
7aea4a8
docs and variable names
joshieDo May 16, 2024
bd66959
more docs
joshieDo May 16, 2024
6a8aab8
added ensure_file_consistency
joshieDo May 16, 2024
1cb6009
add first test_check_consistency
joshieDo May 20, 2024
d32cecb
panic on unwind target to 0
joshieDo May 20, 2024
39a802a
add warning to check_consistency
joshieDo May 21, 2024
50cead0
add read_only to check_consistency
joshieDo May 21, 2024
aa865fa
make sure SegmentHeader is updated after file heal
joshieDo May 21, 2024
67719b1
add early prune tests
joshieDo May 21, 2024
f1502dd
nippyjar writer handle partial pruned row
joshieDo May 21, 2024
98f1381
rename to test_consistency_no_commit_prune
joshieDo May 21, 2024
a988e9e
fix usage of storage_kind on insert_blocks
joshieDo May 21, 2024
86946a6
add checkpoint and gap tests
joshieDo May 21, 2024
016deaa
smol refactor on test functions
joshieDo May 22, 2024
a0e3976
clippy
joshieDo May 22, 2024
ea0ef7f
nit
joshieDo May 22, 2024
d9fb353
add logs to node launch consistency check
joshieDo May 22, 2024
dfd2d8c
fmt
joshieDo May 22, 2024
9266ca4
nit
joshieDo May 22, 2024
2d142f1
update docs
joshieDo May 22, 2024
8ace1e5
const fns on segments
joshieDo May 27, 2024
50b19ba
noop executor
joshieDo May 27, 2024
0eaa9bd
swap order
joshieDo May 27, 2024
7541b52
update docs on create_provider_factory
joshieDo May 27, 2024
16071d7
Merge remote-tracking branch 'origin/main' into joshie/boot-up-check
joshieDo May 27, 2024
014c694
fix merge
joshieDo May 27, 2024
51c069e
feat: `StaticFileProvider` requires `StaticFileAccess` for initializa…
joshieDo May 28, 2024
d6eec77
Merge remote-tracking branch 'origin/main' into joshie/boot-up-check
joshieDo May 29, 2024
a5be748
bump Starting unwind msg to info
joshieDo May 29, 2024
c07a75f
compare tx_len and block_len against number of rows instead
joshieDo May 29, 2024
76156e2
point queries should return None if the static file cant be found for id
joshieDo May 29, 2024
42a3764
complete ensure_invariants
joshieDo May 29, 2024
1b8216f
ProviderFactor new no longer returns result
joshieDo May 29, 2024
14e2dbc
Merge remote-tracking branch 'origin/main' into joshie/boot-up-check
joshieDo May 29, 2024
25776f7
update_unwind_target takes BlockNumber instead
joshieDo May 29, 2024
78b92c0
fix test_consistency_checkpoints
joshieDo May 29, 2024
5cf7a07
Merge remote-tracking branch 'origin/main' into joshie/boot-up-check
joshieDo May 29, 2024
3243cfb
remove cfg from user_header
joshieDo May 29, 2024
5d190dd
only update SegmentHeader if it's not read_only, otherwise error out
joshieDo May 29, 2024
a142072
Merge remote-tracking branch 'origin/main' into joshie/boot-up-check
joshieDo May 29, 2024
4d3d9d0
replace unavailable errors on BlockExecutionError
joshieDo May 29, 2024
5c6ef86
clippy
joshieDo May 29, 2024
65232a9
access not env for StaticFileAccess var
joshieDo May 29, 2024
60bf5d5
Merge remote-tracking branch 'origin/main' into joshie/boot-up-check
joshieDo May 29, 2024
cc551cc
fix test import
joshieDo May 29, 2024
50dbeb8
Merge remote-tracking branch 'origin/main' into joshie/boot-up-check
joshieDo May 30, 2024
57b4d38
change arg to read_only on nippy writer
joshieDo May 30, 2024
4631781
add ConsistencyFailStrategy
joshieDo May 30, 2024
b3c660a
Merge remote-tracking branch 'origin/main' into joshie/boot-up-check
joshieDo May 30, 2024
8ff3736
use upsert on headers and txlookup
joshieDo May 30, 2024
6cd04b2
Revert "use upsert on headers and txlookup"
joshieDo May 30, 2024
aacbe7e
on unwinds: commit to db first then static files
joshieDo May 30, 2024
ea0627d
fix doc
joshieDo May 30, 2024
f69483c
fix docs
joshieDo May 30, 2024
8dcb970
make tests more complete
joshieDo May 31, 2024
b1651a5
add header as well to test
joshieDo May 31, 2024
fcdf452
add more docs
joshieDo May 31, 2024
8e9b89e
Merge remote-tracking branch 'origin/main' into joshie/boot-up-check
joshieDo May 31, 2024
938f2ca
make sure to update last receipt block if unwinding empty blocks
joshieDo May 31, 2024
e721388
fix edge case on unwinding empty blocks across files
joshieDo May 31, 2024
8f6bb24
rename back to num_rows
joshieDo May 31, 2024
477e027
Merge remote-tracking branch 'origin/main' into joshie/boot-up-check
joshieDo May 31, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 7 additions & 0 deletions crates/config/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,13 @@ impl Default for PruneConfig {
}
}

impl PruneConfig {
/// Returns whether there is any kind of receipt pruning configuration.
pub fn has_receipts_pruning(&self) -> bool {
self.segments.receipts.is_some() || !self.segments.receipts_log_filter.is_empty()
}
}

/// Helper type to support older versions of Duration deserialization.
fn deserialize_duration<'de, D>(deserializer: D) -> Result<Option<Duration>, D::Error>
where
Expand Down
55 changes: 55 additions & 0 deletions crates/evm/src/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,61 @@ pub trait BlockExecutorProvider: Send + Sync + Clone + Unpin + 'static {
DB: Database<Error = ProviderError>;
}

/// A [BlockExecutorProvider] implementation that does nothing.
#[derive(Debug, Default, Clone)]
#[non_exhaustive]
pub struct NoopBlockExecutorProvider;
joshieDo marked this conversation as resolved.
Show resolved Hide resolved

impl BlockExecutorProvider for NoopBlockExecutorProvider {
type Executor<DB: Database<Error = ProviderError>> = Self;

type BatchExecutor<DB: Database<Error = ProviderError>> = Self;

fn executor<DB>(&self, _: DB) -> Self::Executor<DB>
where
DB: Database<Error = ProviderError>,
{
Self
}

fn batch_executor<DB>(&self, _: DB, _: PruneModes) -> Self::BatchExecutor<DB>
where
DB: Database<Error = ProviderError>,
{
Self
}
}

impl<DB> Executor<DB> for NoopBlockExecutorProvider {
type Input<'a> = BlockExecutionInput<'a, BlockWithSenders>;
type Output = BlockExecutionOutput<Receipt>;
type Error = BlockExecutionError;

fn execute(self, _: Self::Input<'_>) -> Result<Self::Output, Self::Error> {
Err(BlockExecutionError::UnavailableForNoop)
}
}

impl<DB> BatchExecutor<DB> for NoopBlockExecutorProvider {
type Input<'a> = BlockExecutionInput<'a, BlockWithSenders>;
type Output = BatchBlockExecutionOutput;
type Error = BlockExecutionError;

fn execute_one(&mut self, _: Self::Input<'_>) -> Result<(), Self::Error> {
Err(BlockExecutionError::UnavailableForNoop)
}

fn finalize(self) -> Self::Output {
unreachable!()
}

fn set_tip(&mut self, _: BlockNumber) {}

fn size_hint(&self) -> Option<usize> {
None
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
3 changes: 2 additions & 1 deletion crates/interfaces/src/blockchain_tree/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,8 @@ impl InsertBlockErrorKind {
BlockExecutionError::CanonicalRevert { .. } |
BlockExecutionError::CanonicalCommit { .. } |
BlockExecutionError::AppendChainDoesntConnect { .. } |
BlockExecutionError::UnavailableForTest => false,
BlockExecutionError::UnavailableForTest |
BlockExecutionError::UnavailableForNoop => false,
BlockExecutionError::Other(_) => false,
}
}
Expand Down
3 changes: 3 additions & 0 deletions crates/interfaces/src/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ pub enum BlockExecutionError {
/// Note: this is not feature gated for convenience.
#[error("execution unavailable for tests")]
UnavailableForTest,
/// Only used for NoopBlockExecutorProvider
#[error("execution unavailable for noop")]
UnavailableForNoop,
joshieDo marked this conversation as resolved.
Show resolved Hide resolved
/// Error when fetching latest block state.
#[error(transparent)]
LatestBlock(#[from] ProviderError),
Expand Down
77 changes: 70 additions & 7 deletions crates/node/builder/src/launch/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,30 @@ use std::{cmp::max, sync::Arc, thread::available_parallelism};

use eyre::Context;
use rayon::ThreadPoolBuilder;
use tokio::sync::mpsc::Receiver;
use tokio::sync::{mpsc::Receiver, oneshot};

use reth_auto_seal_consensus::MiningMode;
use reth_beacon_consensus::EthBeaconConsensus;
use reth_config::{config::EtlConfig, PruneConfig};
use reth_db::{database::Database, database_metrics::DatabaseMetrics};
use reth_downloaders::{bodies::noop::NoopBodiesDownloader, headers::noop::NoopHeaderDownloader};
use reth_evm::execute::NoopBlockExecutorProvider;
use reth_interfaces::p2p::headers::client::HeadersClient;
use reth_node_core::{
cli::config::RethRpcConfig,
dirs::{ChainPath, DataDirPath},
init::{init_genesis, InitDatabaseError},
node_config::NodeConfig,
};
use reth_primitives::{BlockNumber, Chain, ChainSpec, Head, PruneModes, B256};
use reth_provider::{providers::StaticFileProvider, ProviderFactory, StaticFileProviderFactory};
use reth_primitives::{
stage::PipelineTarget, BlockNumber, Chain, ChainSpec, Head, PruneModes, B256,
};
use reth_provider::{
providers::StaticFileProvider, HeaderSyncMode, ProviderFactory, StaticFileProviderFactory,
};
use reth_prune::PrunerBuilder;
use reth_rpc_layer::JwtSecret;
use reth_stages::{sets::DefaultStages, Pipeline};
use reth_static_file::StaticFileProducer;
use reth_tasks::TaskExecutor;
use reth_tracing::tracing::{error, info, warn};
Expand Down Expand Up @@ -312,25 +320,80 @@ impl<R> LaunchContextWith<Attached<WithConfigs, R>> {

impl<DB> LaunchContextWith<Attached<WithConfigs, DB>>
where
DB: Clone,
DB: Database + Clone + 'static,
{
/// Returns the [ProviderFactory] for the attached database.
pub fn create_provider_factory(&self) -> eyre::Result<ProviderFactory<DB>> {
pub async fn create_provider_factory(&self) -> eyre::Result<ProviderFactory<DB>> {
joshieDo marked this conversation as resolved.
Show resolved Hide resolved
let factory = ProviderFactory::new(
self.right().clone(),
self.chain_spec(),
self.data_dir().static_files(),
)?
.with_static_files_metrics();

let has_receipt_pruning =
self.toml_config().prune.as_ref().map_or(false, |a| a.has_receipts_pruning());

info!(target: "reth::cli", "Verifying storage consistency.");

// Check for consistency between database and static files. If it fails, it unwinds to
// the first block that's consistent between database and static files.
if let Some(unwind_target) = factory.static_file_provider().check_consistency(
&factory.provider()?,
has_receipt_pruning,
false,
)? {
// Highly unlikely to happen, and given its destructive nature, it's better to panic
// instead.
if PipelineTarget::Unwind(0) == unwind_target {
panic!("A static file <> database inconsistency was found that would trigger an unwind to block 0.")
}

info!(target: "reth::cli", unwind_target = %unwind_target, "Executing an unwind after a failed storage consistency check.");

// Builds an unwind-only pipeline
let pipeline = Pipeline::builder()
.add_stages(DefaultStages::new(
factory.clone(),
HeaderSyncMode::Continuous,
Arc::new(EthBeaconConsensus::new(self.chain_spec())),
NoopHeaderDownloader::default(),
NoopBodiesDownloader::default(),
NoopBlockExecutorProvider::default(),
self.toml_config().stages.clone(),
self.prune_modes().unwrap_or_default(),
))
.build(
factory.clone(),
StaticFileProducer::new(
factory.clone(),
factory.static_file_provider(),
self.prune_modes().unwrap_or_default(),
),
);

// Unwinds to block
let (tx, rx) = oneshot::channel();

// Pipeline should be run as blocking and panic if it fails.
self.task_executor().spawn_critical_blocking(
joshieDo marked this conversation as resolved.
Show resolved Hide resolved
"pipeline task",
Box::pin(async move {
let (_, result) = pipeline.run_as_fut(Some(unwind_target)).await;
let _ = tx.send(result);
}),
);
rx.await??;
}

Ok(factory)
}

/// Creates a new [ProviderFactory] and attaches it to the launch context.
pub fn with_provider_factory(
pub async fn with_provider_factory(
self,
) -> eyre::Result<LaunchContextWith<Attached<WithConfigs, ProviderFactory<DB>>>> {
let factory = self.create_provider_factory()?;
let factory = self.create_provider_factory().await?;
let ctx = LaunchContextWith {
inner: self.inner,
attachment: self.attachment.map_right(|_| factory),
Expand Down
2 changes: 1 addition & 1 deletion crates/node/builder/src/launch/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ where
// ensure certain settings take effect
.with_adjusted_configs()
// Create the provider factory
.with_provider_factory()?
.with_provider_factory().await?
.inspect(|_| {
info!(target: "reth::cli", "Database opened");
})
Expand Down
13 changes: 12 additions & 1 deletion crates/primitives/src/stage/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ pub use checkpoints::{
};

/// Direction and target block for pipeline operations.
#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PipelineTarget {
/// Target for forward synchronization, indicating a block hash to sync to.
Sync(BlockHash),
Expand Down Expand Up @@ -53,3 +53,14 @@ impl From<BlockHash> for PipelineTarget {
Self::Sync(hash)
}
}

impl std::fmt::Display for PipelineTarget {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
PipelineTarget::Sync(block) => {
write!(f, "Sync({block})")
}
PipelineTarget::Unwind(block) => write!(f, "Unwind({block})"),
}
}
}
5 changes: 5 additions & 0 deletions crates/primitives/src/static_file/segment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,11 @@ impl StaticFileSegment {
pub fn is_headers(&self) -> bool {
matches!(self, StaticFileSegment::Headers)
}

/// Returns `true` if the segment is `StaticFileSegment::Receipts`.
pub fn is_receipts(&self) -> bool {
joshieDo marked this conversation as resolved.
Show resolved Hide resolved
matches!(self, StaticFileSegment::Receipts)
}
}

/// A segment header that contains information common to all segments. Used for storage.
Expand Down