diff --git a/crates/prune/prune/src/builder.rs b/crates/prune/prune/src/builder.rs index 45118c78ac..af426a8986 100644 --- a/crates/prune/prune/src/builder.rs +++ b/crates/prune/prune/src/builder.rs @@ -3,7 +3,7 @@ use reth_chainspec::MAINNET; use reth_config::PruneConfig; use reth_db_api::database::Database; use reth_exex_types::FinishedExExHeight; -use reth_provider::ProviderFactory; +use reth_provider::{providers::StaticFileProvider, ProviderFactory, StaticFileProviderFactory}; use reth_prune_types::PruneModes; use std::time::Duration; use tokio::sync::watch; @@ -75,7 +75,10 @@ impl PrunerBuilder { self, provider_factory: ProviderFactory, ) -> Pruner> { - let segments = SegmentSet::::from_prune_modes(self.segments); + let segments = SegmentSet::::from_components( + provider_factory.static_file_provider(), + self.segments, + ); Pruner::<_, ProviderFactory>::new( provider_factory, @@ -87,9 +90,9 @@ impl PrunerBuilder { ) } - /// Builds a [Pruner] from the current configuration. - pub fn build(self) -> Pruner { - let segments = SegmentSet::::from_prune_modes(self.segments); + /// Builds a [Pruner] from the current configuration with the given static file provider. + pub fn build(self, static_file_provider: StaticFileProvider) -> Pruner { + let segments = SegmentSet::::from_components(static_file_provider, self.segments); Pruner::<_, ()>::new( segments.into_vec(), diff --git a/crates/prune/prune/src/pruner.rs b/crates/prune/prune/src/pruner.rs index c3c6ff1e26..479022eb10 100644 --- a/crates/prune/prune/src/pruner.rs +++ b/crates/prune/prune/src/pruner.rs @@ -1,7 +1,6 @@ //! Support for pruning. use crate::{ - segments, segments::{PruneInput, Segment}, Metrics, PrunerError, PrunerEvent, }; @@ -9,8 +8,7 @@ use alloy_primitives::BlockNumber; use reth_db_api::database::Database; use reth_exex_types::FinishedExExHeight; use reth_provider::{DatabaseProviderRW, ProviderFactory, PruneCheckpointReader}; -use reth_prune_types::{PruneLimiter, PruneMode, PruneProgress, PrunePurpose, PruneSegment}; -use reth_static_file_types::StaticFileSegment; +use reth_prune_types::{PruneLimiter, PruneProgress, PruneSegment}; use reth_tokio_util::{EventSender, EventStream}; use std::time::{Duration, Instant}; use tokio::sync::watch; @@ -168,31 +166,27 @@ impl Pruner { tip_block_number: BlockNumber, limiter: &mut PruneLimiter, ) -> Result<(PrunerStats, usize, PruneProgress), PrunerError> { - let static_file_segments = self.static_file_segments(provider); - let segments = static_file_segments - .iter() - .map(|segment| (segment, PrunePurpose::StaticFile)) - .chain(self.segments.iter().map(|segment| (segment, PrunePurpose::User))); - let mut stats = PrunerStats::new(); let mut pruned = 0; let mut progress = PruneProgress::Finished; - for (segment, purpose) in segments { + for segment in &self.segments { if limiter.is_limit_reached() { break } if let Some((to_block, prune_mode)) = segment .mode() - .map(|mode| mode.prune_target_block(tip_block_number, segment.segment(), purpose)) + .map(|mode| { + mode.prune_target_block(tip_block_number, segment.segment(), segment.purpose()) + }) .transpose()? .flatten() { debug!( target: "pruner", segment = ?segment.segment(), - ?purpose, + purpose = ?segment.purpose(), %to_block, ?prune_mode, "Segment pruning started" @@ -226,7 +220,7 @@ impl Pruner { debug!( target: "pruner", segment = ?segment.segment(), - ?purpose, + purpose = ?segment.purpose(), %to_block, ?prune_mode, %output.pruned, @@ -239,43 +233,13 @@ impl Pruner { stats.push((segment.segment(), output.pruned, output.progress)); } } else { - debug!(target: "pruner", segment = ?segment.segment(), ?purpose, "Nothing to prune for the segment"); + debug!(target: "pruner", segment = ?segment.segment(), purpose = ?segment.purpose(), "Nothing to prune for the segment"); } } Ok((stats, pruned, progress)) } - /// Returns pre-configured segments that needs to be pruned according to the highest - /// `static_files` for [`PruneSegment::Transactions`], [`PruneSegment::Headers`] and - /// [`PruneSegment::Receipts`]. - fn static_file_segments(&self, provider: &DatabaseProviderRW) -> Vec>> { - let mut segments = Vec::>>::new(); - - let static_file_provider = provider.static_file_provider(); - - if let Some(to_block) = - static_file_provider.get_highest_static_file_block(StaticFileSegment::Transactions) - { - segments - .push(Box::new(segments::Transactions::new(PruneMode::before_inclusive(to_block)))) - } - - if let Some(to_block) = - static_file_provider.get_highest_static_file_block(StaticFileSegment::Headers) - { - segments.push(Box::new(segments::Headers::new(PruneMode::before_inclusive(to_block)))) - } - - if let Some(to_block) = - static_file_provider.get_highest_static_file_block(StaticFileSegment::Receipts) - { - segments.push(Box::new(segments::Receipts::new(PruneMode::before_inclusive(to_block)))) - } - - segments - } - /// Returns `true` if the pruning is needed at the provided tip block number. /// This determined by the check against minimum pruning interval and last pruned block number. pub fn is_pruning_needed(&self, tip_block_number: BlockNumber) -> bool { diff --git a/crates/prune/prune/src/segments/mod.rs b/crates/prune/prune/src/segments/mod.rs index 9dc6892658..8700ac8a0d 100644 --- a/crates/prune/prune/src/segments/mod.rs +++ b/crates/prune/prune/src/segments/mod.rs @@ -1,34 +1,29 @@ -mod account_history; -mod headers; -pub(super) mod history; mod receipts; -mod receipts_by_logs; -mod sender_recovery; mod set; -mod storage_history; -mod transaction_lookup; -mod transactions; +mod static_file; +mod user; use crate::PrunerError; -pub use account_history::AccountHistory; use alloy_primitives::{BlockNumber, TxNumber}; -pub use headers::Headers; -pub use receipts::Receipts; -pub use receipts_by_logs::ReceiptsByLogs; use reth_db_api::database::Database; use reth_provider::{ errors::provider::ProviderResult, BlockReader, DatabaseProviderRW, PruneCheckpointWriter, }; use reth_prune_types::{ - PruneCheckpoint, PruneInterruptReason, PruneLimiter, PruneMode, PruneProgress, PruneSegment, + PruneCheckpoint, PruneInterruptReason, PruneLimiter, PruneMode, PruneProgress, PrunePurpose, + PruneSegment, }; -pub use sender_recovery::SenderRecovery; pub use set::SegmentSet; +pub use static_file::{ + Headers as StaticFileHeaders, Receipts as StaticFileReceipts, + Transactions as StaticFileTransactions, +}; use std::{fmt::Debug, ops::RangeInclusive}; -pub use storage_history::StorageHistory; use tracing::error; -pub use transaction_lookup::TransactionLookup; -pub use transactions::Transactions; +pub use user::{ + AccountHistory, Receipts as UserReceipts, ReceiptsByLogs, SenderRecovery, StorageHistory, + TransactionLookup, +}; /// A segment represents a pruning of some portion of the data. /// @@ -41,9 +36,12 @@ pub trait Segment: Debug + Send + Sync { /// Segment of data that's pruned. fn segment(&self) -> PruneSegment; - /// Prune mode with which the segment was initialized + /// Prune mode with which the segment was initialized. fn mode(&self) -> Option; + /// Purpose of the segment. + fn purpose(&self) -> PrunePurpose; + /// Prune data for [`Self::segment`] using the provided input. fn prune( &self, diff --git a/crates/prune/prune/src/segments/receipts.rs b/crates/prune/prune/src/segments/receipts.rs index 5b81187788..3483d9e1c2 100644 --- a/crates/prune/prune/src/segments/receipts.rs +++ b/crates/prune/prune/src/segments/receipts.rs @@ -1,5 +1,12 @@ +//! Common receipts pruning logic shared between user and static file pruning segments. +//! +//! - [`crate::segments::user::Receipts`] is responsible for pruning receipts according to the +//! user-configured settings (for example, on a full node or with a custom prune config) +//! - [`crate::segments::static_file::Receipts`] is responsible for pruning receipts on an archive +//! node after static file producer has finished + use crate::{ - segments::{PruneInput, PruneOutput, PruneOutputCheckpoint, Segment}, + segments::{PruneInput, PruneOutput, PruneOutputCheckpoint}, PrunerError, }; use reth_db::tables; @@ -8,92 +15,68 @@ use reth_provider::{ errors::provider::ProviderResult, DatabaseProviderRW, PruneCheckpointWriter, TransactionsProvider, }; -use reth_prune_types::{PruneCheckpoint, PruneMode, PruneProgress, PruneSegment}; -use tracing::{instrument, trace}; +use reth_prune_types::{PruneCheckpoint, PruneProgress, PruneSegment}; +use tracing::trace; -#[derive(Debug)] -pub struct Receipts { - mode: PruneMode, +pub(crate) fn prune( + provider: &DatabaseProviderRW, + input: PruneInput, +) -> Result { + let tx_range = match input.get_next_tx_num_range(provider)? { + Some(range) => range, + None => { + trace!(target: "pruner", "No receipts to prune"); + return Ok(PruneOutput::done()) + } + }; + let tx_range_end = *tx_range.end(); + + let mut limiter = input.limiter; + + let mut last_pruned_transaction = tx_range_end; + let (pruned, done) = provider.prune_table_with_range::( + tx_range, + &mut limiter, + |_| false, + |row| last_pruned_transaction = row.0, + )?; + trace!(target: "pruner", %pruned, %done, "Pruned receipts"); + + let last_pruned_block = provider + .transaction_block(last_pruned_transaction)? + .ok_or(PrunerError::InconsistentData("Block for transaction is not found"))? + // If there's more receipts to prune, set the checkpoint block number to previous, + // so we could finish pruning its receipts on the next run. + .checked_sub(if done { 0 } else { 1 }); + + let progress = PruneProgress::new(done, &limiter); + + Ok(PruneOutput { + progress, + pruned, + checkpoint: Some(PruneOutputCheckpoint { + block_number: last_pruned_block, + tx_number: Some(last_pruned_transaction), + }), + }) } -impl Receipts { - pub const fn new(mode: PruneMode) -> Self { - Self { mode } - } -} +pub(crate) fn save_checkpoint( + provider: &DatabaseProviderRW, + checkpoint: PruneCheckpoint, +) -> ProviderResult<()> { + provider.save_prune_checkpoint(PruneSegment::Receipts, checkpoint)?; -impl Segment for Receipts { - fn segment(&self) -> PruneSegment { - PruneSegment::Receipts - } + // `PruneSegment::Receipts` overrides `PruneSegment::ContractLogs`, so we can preemptively + // limit their pruning start point. + provider.save_prune_checkpoint(PruneSegment::ContractLogs, checkpoint)?; - fn mode(&self) -> Option { - Some(self.mode) - } - - #[instrument(level = "trace", target = "pruner", skip(self, provider), ret)] - fn prune( - &self, - provider: &DatabaseProviderRW, - input: PruneInput, - ) -> Result { - let tx_range = match input.get_next_tx_num_range(provider)? { - Some(range) => range, - None => { - trace!(target: "pruner", "No receipts to prune"); - return Ok(PruneOutput::done()) - } - }; - let tx_range_end = *tx_range.end(); - - let mut limiter = input.limiter; - - let mut last_pruned_transaction = tx_range_end; - let (pruned, done) = provider.prune_table_with_range::( - tx_range, - &mut limiter, - |_| false, - |row| last_pruned_transaction = row.0, - )?; - trace!(target: "pruner", %pruned, %done, "Pruned receipts"); - - let last_pruned_block = provider - .transaction_block(last_pruned_transaction)? - .ok_or(PrunerError::InconsistentData("Block for transaction is not found"))? - // If there's more receipts to prune, set the checkpoint block number to previous, - // so we could finish pruning its receipts on the next run. - .checked_sub(if done { 0 } else { 1 }); - - let progress = PruneProgress::new(done, &limiter); - - Ok(PruneOutput { - progress, - pruned, - checkpoint: Some(PruneOutputCheckpoint { - block_number: last_pruned_block, - tx_number: Some(last_pruned_transaction), - }), - }) - } - - fn save_checkpoint( - &self, - provider: &DatabaseProviderRW, - checkpoint: PruneCheckpoint, - ) -> ProviderResult<()> { - provider.save_prune_checkpoint(PruneSegment::Receipts, checkpoint)?; - - // `PruneSegment::Receipts` overrides `PruneSegment::ContractLogs`, so we can preemptively - // limit their pruning start point. - provider.save_prune_checkpoint(PruneSegment::ContractLogs, checkpoint)?; - - Ok(()) - } + Ok(()) } #[cfg(test)] mod tests { - use crate::segments::{PruneInput, PruneOutput, Receipts, Segment}; + use crate::segments::{PruneInput, PruneOutput}; use alloy_primitives::{BlockNumber, TxNumber, B256}; use assert_matches::assert_matches; use itertools::{ @@ -140,7 +123,6 @@ mod tests { let test_prune = |to_block: BlockNumber, expected_result: (PruneProgress, usize)| { let prune_mode = PruneMode::Before(to_block); - let segment = Receipts::new(prune_mode); let mut limiter = PruneLimiter::default().set_deleted_entries_limit(10); let input = PruneInput { previous_checkpoint: db @@ -175,7 +157,7 @@ mod tests { .sub(1); let provider = db.factory.provider_rw().unwrap(); - let result = segment.prune(&provider, input).unwrap(); + let result = super::prune(&provider, input).unwrap(); limiter.increment_deleted_entries_count_by(result.pruned); assert_matches!( @@ -184,12 +166,11 @@ mod tests { if (progress, pruned) == expected_result ); - segment - .save_checkpoint( - &provider, - result.checkpoint.unwrap().as_prune_checkpoint(prune_mode), - ) - .unwrap(); + super::save_checkpoint( + &provider, + result.checkpoint.unwrap().as_prune_checkpoint(prune_mode), + ) + .unwrap(); provider.commit().expect("commit"); let last_pruned_block_number = blocks diff --git a/crates/prune/prune/src/segments/set.rs b/crates/prune/prune/src/segments/set.rs index f5b60f2695..d152bfe262 100644 --- a/crates/prune/prune/src/segments/set.rs +++ b/crates/prune/prune/src/segments/set.rs @@ -1,10 +1,13 @@ use crate::segments::{ - AccountHistory, Receipts, ReceiptsByLogs, Segment, SenderRecovery, StorageHistory, - TransactionLookup, + AccountHistory, ReceiptsByLogs, Segment, SenderRecovery, StorageHistory, TransactionLookup, + UserReceipts, }; use reth_db_api::database::Database; +use reth_provider::providers::StaticFileProvider; use reth_prune_types::PruneModes; +use super::{StaticFileHeaders, StaticFileReceipts, StaticFileTransactions}; + /// Collection of [Segment]. Thread-safe, allocated on the heap. #[derive(Debug)] pub struct SegmentSet { @@ -36,8 +39,12 @@ impl SegmentSet { self.inner } - /// Creates a [`SegmentSet`] from an existing [`PruneModes`]. - pub fn from_prune_modes(prune_modes: PruneModes) -> Self { + /// Creates a [`SegmentSet`] from an existing components, such as [`StaticFileProvider`] and + /// [`PruneModes`]. + pub fn from_components( + static_file_provider: StaticFileProvider, + prune_modes: PruneModes, + ) -> Self { let PruneModes { sender_recovery, transaction_lookup, @@ -48,12 +55,18 @@ impl SegmentSet { } = prune_modes; Self::default() + // Static file headers + .segment(StaticFileHeaders::new(static_file_provider.clone())) + // Static file transactions + .segment(StaticFileTransactions::new(static_file_provider.clone())) + // Static file receipts + .segment(StaticFileReceipts::new(static_file_provider)) // Account history .segment_opt(account_history.map(AccountHistory::new)) // Storage history .segment_opt(storage_history.map(StorageHistory::new)) - // Receipts - .segment_opt(receipts.map(Receipts::new)) + // User receipts + .segment_opt(receipts.map(UserReceipts::new)) // Receipts by logs .segment_opt( (!receipts_log_filter.is_empty()) diff --git a/crates/prune/prune/src/segments/headers.rs b/crates/prune/prune/src/segments/static_file/headers.rs similarity index 90% rename from crates/prune/prune/src/segments/headers.rs rename to crates/prune/prune/src/segments/static_file/headers.rs index 1c49fe8c10..a9c26a97ca 100644 --- a/crates/prune/prune/src/segments/headers.rs +++ b/crates/prune/prune/src/segments/static_file/headers.rs @@ -4,30 +4,30 @@ use crate::{ segments::{PruneInput, PruneOutput, PruneOutputCheckpoint, Segment}, PrunerError, }; +use alloy_primitives::BlockNumber; use itertools::Itertools; -use reth_db::tables; -use reth_db_api::{ +use reth_db::{ cursor::{DbCursorRO, RangeWalker}, database::Database, + tables, transaction::DbTxMut, }; - -use alloy_primitives::BlockNumber; -use reth_provider::DatabaseProviderRW; -use reth_prune_types::{PruneLimiter, PruneMode, PruneProgress, PruneSegment}; -use tracing::{instrument, trace}; +use reth_provider::{providers::StaticFileProvider, DatabaseProviderRW}; +use reth_prune_types::{PruneLimiter, PruneMode, PruneProgress, PrunePurpose, PruneSegment}; +use reth_static_file_types::StaticFileSegment; +use tracing::trace; /// Number of header tables to prune in one step const HEADER_TABLES_TO_PRUNE: usize = 3; #[derive(Debug)] pub struct Headers { - mode: PruneMode, + static_file_provider: StaticFileProvider, } impl Headers { - pub const fn new(mode: PruneMode) -> Self { - Self { mode } + pub const fn new(static_file_provider: StaticFileProvider) -> Self { + Self { static_file_provider } } } @@ -37,10 +37,15 @@ impl Segment for Headers { } fn mode(&self) -> Option { - Some(self.mode) + self.static_file_provider + .get_highest_static_file_block(StaticFileSegment::Headers) + .map(PruneMode::before_inclusive) + } + + fn purpose(&self) -> PrunePurpose { + PrunePurpose::StaticFile } - #[instrument(level = "trace", target = "pruner", skip(self, provider), ret)] fn prune( &self, provider: &DatabaseProviderRW, @@ -98,7 +103,6 @@ impl Segment for Headers { }) } } - type Walker<'a, DB, T> = RangeWalker<'a, T, <::TXMut as DbTxMut>::CursorMut>; #[allow(missing_debug_implementations)] @@ -188,11 +192,15 @@ where #[cfg(test)] mod tests { + use crate::segments::{ + static_file::headers::HEADER_TABLES_TO_PRUNE, PruneInput, PruneOutput, + PruneOutputCheckpoint, Segment, + }; use alloy_primitives::{BlockNumber, B256, U256}; use assert_matches::assert_matches; use reth_db::tables; use reth_db_api::transaction::DbTx; - use reth_provider::PruneCheckpointReader; + use reth_provider::{PruneCheckpointReader, PruneCheckpointWriter, StaticFileProviderFactory}; use reth_prune_types::{ PruneCheckpoint, PruneInterruptReason, PruneLimiter, PruneMode, PruneProgress, PruneSegment, }; @@ -200,11 +208,6 @@ mod tests { use reth_testing_utils::{generators, generators::random_header_range}; use tracing::trace; - use crate::segments::{ - headers::HEADER_TABLES_TO_PRUNE, Headers, PruneInput, PruneOutput, PruneOutputCheckpoint, - Segment, - }; - #[test] fn prune() { reth_tracing::init_test_tracing(); @@ -224,8 +227,8 @@ mod tests { assert_eq!(db.table::().unwrap().len(), headers.len()); let test_prune = |to_block: BlockNumber, expected_result: (PruneProgress, usize)| { + let segment = super::Headers::new(db.factory.static_file_provider()); let prune_mode = PruneMode::Before(to_block); - let segment = Headers::new(prune_mode); let mut limiter = PruneLimiter::default().set_deleted_entries_limit(10); let input = PruneInput { previous_checkpoint: db @@ -263,9 +266,9 @@ mod tests { PruneOutput {progress, pruned, checkpoint: Some(_)} if (progress, pruned) == expected_result ); - segment - .save_checkpoint( - &provider, + provider + .save_prune_checkpoint( + PruneSegment::Headers, result.checkpoint.unwrap().as_prune_checkpoint(prune_mode), ) .unwrap(); @@ -310,7 +313,6 @@ mod tests { fn prune_cannot_be_done() { let db = TestStageDB::default(); - let segment = Headers::new(PruneMode::Full); let limiter = PruneLimiter::default().set_deleted_entries_limit(0); let input = PruneInput { @@ -321,6 +323,7 @@ mod tests { }; let provider = db.factory.provider_rw().unwrap(); + let segment = super::Headers::new(db.factory.static_file_provider()); let result = segment.prune(&provider, input).unwrap(); assert_eq!( result, diff --git a/crates/prune/prune/src/segments/static_file/mod.rs b/crates/prune/prune/src/segments/static_file/mod.rs new file mode 100644 index 0000000000..cb9dc79c6c --- /dev/null +++ b/crates/prune/prune/src/segments/static_file/mod.rs @@ -0,0 +1,7 @@ +mod headers; +mod receipts; +mod transactions; + +pub use headers::Headers; +pub use receipts::Receipts; +pub use transactions::Transactions; diff --git a/crates/prune/prune/src/segments/static_file/receipts.rs b/crates/prune/prune/src/segments/static_file/receipts.rs new file mode 100644 index 0000000000..ea616c6ab0 --- /dev/null +++ b/crates/prune/prune/src/segments/static_file/receipts.rs @@ -0,0 +1,53 @@ +use crate::{ + segments::{PruneInput, PruneOutput, Segment}, + PrunerError, +}; +use reth_db_api::database::Database; +use reth_provider::{ + errors::provider::ProviderResult, providers::StaticFileProvider, DatabaseProviderRW, +}; +use reth_prune_types::{PruneCheckpoint, PruneMode, PrunePurpose, PruneSegment}; +use reth_static_file_types::StaticFileSegment; + +#[derive(Debug)] +pub struct Receipts { + static_file_provider: StaticFileProvider, +} + +impl Receipts { + pub const fn new(static_file_provider: StaticFileProvider) -> Self { + Self { static_file_provider } + } +} + +impl Segment for Receipts { + fn segment(&self) -> PruneSegment { + PruneSegment::Receipts + } + + fn mode(&self) -> Option { + self.static_file_provider + .get_highest_static_file_block(StaticFileSegment::Receipts) + .map(PruneMode::before_inclusive) + } + + fn purpose(&self) -> PrunePurpose { + PrunePurpose::StaticFile + } + + fn prune( + &self, + provider: &DatabaseProviderRW, + input: PruneInput, + ) -> Result { + crate::segments::receipts::prune(provider, input) + } + + fn save_checkpoint( + &self, + provider: &DatabaseProviderRW, + checkpoint: PruneCheckpoint, + ) -> ProviderResult<()> { + crate::segments::receipts::save_checkpoint(provider, checkpoint) + } +} diff --git a/crates/prune/prune/src/segments/transactions.rs b/crates/prune/prune/src/segments/static_file/transactions.rs similarity index 86% rename from crates/prune/prune/src/segments/transactions.rs rename to crates/prune/prune/src/segments/static_file/transactions.rs index fd24a380de..1da08806f0 100644 --- a/crates/prune/prune/src/segments/transactions.rs +++ b/crates/prune/prune/src/segments/static_file/transactions.rs @@ -4,18 +4,19 @@ use crate::{ }; use reth_db::tables; use reth_db_api::database::Database; -use reth_provider::{DatabaseProviderRW, TransactionsProvider}; -use reth_prune_types::{PruneMode, PruneProgress, PruneSegment}; -use tracing::{instrument, trace}; +use reth_provider::{providers::StaticFileProvider, DatabaseProviderRW, TransactionsProvider}; +use reth_prune_types::{PruneMode, PruneProgress, PrunePurpose, PruneSegment}; +use reth_static_file_types::StaticFileSegment; +use tracing::trace; #[derive(Debug)] pub struct Transactions { - mode: PruneMode, + static_file_provider: StaticFileProvider, } impl Transactions { - pub const fn new(mode: PruneMode) -> Self { - Self { mode } + pub const fn new(static_file_provider: StaticFileProvider) -> Self { + Self { static_file_provider } } } @@ -25,10 +26,15 @@ impl Segment for Transactions { } fn mode(&self) -> Option { - Some(self.mode) + self.static_file_provider + .get_highest_static_file_block(StaticFileSegment::Transactions) + .map(PruneMode::before_inclusive) + } + + fn purpose(&self) -> PrunePurpose { + PrunePurpose::StaticFile } - #[instrument(level = "trace", target = "pruner", skip(self, provider), ret)] fn prune( &self, provider: &DatabaseProviderRW, @@ -75,7 +81,7 @@ impl Segment for Transactions { #[cfg(test)] mod tests { - use crate::segments::{PruneInput, PruneOutput, Segment, Transactions}; + use crate::segments::{PruneInput, PruneOutput, Segment}; use alloy_primitives::{BlockNumber, TxNumber, B256}; use assert_matches::assert_matches; use itertools::{ @@ -83,7 +89,7 @@ mod tests { Itertools, }; use reth_db::tables; - use reth_provider::PruneCheckpointReader; + use reth_provider::{PruneCheckpointReader, PruneCheckpointWriter, StaticFileProviderFactory}; use reth_prune_types::{ PruneCheckpoint, PruneInterruptReason, PruneLimiter, PruneMode, PruneProgress, PruneSegment, }; @@ -104,8 +110,8 @@ mod tests { assert_eq!(db.table::().unwrap().len(), transactions.len()); let test_prune = |to_block: BlockNumber, expected_result: (PruneProgress, usize)| { + let segment = super::Transactions::new(db.factory.static_file_provider()); let prune_mode = PruneMode::Before(to_block); - let segment = Transactions::new(prune_mode); let mut limiter = PruneLimiter::default().set_deleted_entries_limit(10); let input = PruneInput { previous_checkpoint: db @@ -138,9 +144,9 @@ mod tests { if (progress, pruned) == expected_result ); - segment - .save_checkpoint( - &provider, + provider + .save_prune_checkpoint( + PruneSegment::Transactions, result.checkpoint.unwrap().as_prune_checkpoint(prune_mode), ) .unwrap(); diff --git a/crates/prune/prune/src/segments/account_history.rs b/crates/prune/prune/src/segments/user/account_history.rs similarity index 96% rename from crates/prune/prune/src/segments/account_history.rs rename to crates/prune/prune/src/segments/user/account_history.rs index 28e448560b..bbf2a155eb 100644 --- a/crates/prune/prune/src/segments/account_history.rs +++ b/crates/prune/prune/src/segments/user/account_history.rs @@ -1,6 +1,7 @@ use crate::{ segments::{ - history::prune_history_indices, PruneInput, PruneOutput, PruneOutputCheckpoint, Segment, + user::history::prune_history_indices, PruneInput, PruneOutput, PruneOutputCheckpoint, + Segment, }, PrunerError, }; @@ -8,7 +9,9 @@ use itertools::Itertools; use reth_db::tables; use reth_db_api::{database::Database, models::ShardedKey}; use reth_provider::DatabaseProviderRW; -use reth_prune_types::{PruneInterruptReason, PruneMode, PruneProgress, PruneSegment}; +use reth_prune_types::{ + PruneInterruptReason, PruneMode, PruneProgress, PrunePurpose, PruneSegment, +}; use rustc_hash::FxHashMap; use tracing::{instrument, trace}; @@ -38,6 +41,10 @@ impl Segment for AccountHistory { Some(self.mode) } + fn purpose(&self) -> PrunePurpose { + PrunePurpose::User + } + #[instrument(level = "trace", target = "pruner", skip(self, provider), ret)] fn prune( &self, @@ -124,8 +131,8 @@ impl Segment for AccountHistory { #[cfg(test)] mod tests { use crate::segments::{ - account_history::ACCOUNT_HISTORY_TABLES_TO_PRUNE, AccountHistory, PruneInput, PruneOutput, - Segment, + user::account_history::ACCOUNT_HISTORY_TABLES_TO_PRUNE, AccountHistory, PruneInput, + PruneOutput, Segment, }; use alloy_primitives::{BlockNumber, B256}; use assert_matches::assert_matches; diff --git a/crates/prune/prune/src/segments/history.rs b/crates/prune/prune/src/segments/user/history.rs similarity index 100% rename from crates/prune/prune/src/segments/history.rs rename to crates/prune/prune/src/segments/user/history.rs diff --git a/crates/prune/prune/src/segments/user/mod.rs b/crates/prune/prune/src/segments/user/mod.rs new file mode 100644 index 0000000000..0b787d14da --- /dev/null +++ b/crates/prune/prune/src/segments/user/mod.rs @@ -0,0 +1,14 @@ +mod account_history; +mod history; +mod receipts; +mod receipts_by_logs; +mod sender_recovery; +mod storage_history; +mod transaction_lookup; + +pub use account_history::AccountHistory; +pub use receipts::Receipts; +pub use receipts_by_logs::ReceiptsByLogs; +pub use sender_recovery::SenderRecovery; +pub use storage_history::StorageHistory; +pub use transaction_lookup::TransactionLookup; diff --git a/crates/prune/prune/src/segments/user/receipts.rs b/crates/prune/prune/src/segments/user/receipts.rs new file mode 100644 index 0000000000..0105e0c2fd --- /dev/null +++ b/crates/prune/prune/src/segments/user/receipts.rs @@ -0,0 +1,50 @@ +use crate::{ + segments::{PruneInput, PruneOutput, Segment}, + PrunerError, +}; +use reth_db_api::database::Database; +use reth_provider::{errors::provider::ProviderResult, DatabaseProviderRW}; +use reth_prune_types::{PruneCheckpoint, PruneMode, PrunePurpose, PruneSegment}; +use tracing::instrument; + +#[derive(Debug)] +pub struct Receipts { + mode: PruneMode, +} + +impl Receipts { + pub const fn new(mode: PruneMode) -> Self { + Self { mode } + } +} + +impl Segment for Receipts { + fn segment(&self) -> PruneSegment { + PruneSegment::Receipts + } + + fn mode(&self) -> Option { + Some(self.mode) + } + + fn purpose(&self) -> PrunePurpose { + PrunePurpose::User + } + + #[instrument(level = "trace", target = "pruner", skip(self, provider), ret)] + fn prune( + &self, + provider: &DatabaseProviderRW, + input: PruneInput, + ) -> Result { + crate::segments::receipts::prune(provider, input) + } + + fn save_checkpoint( + &self, + provider: &DatabaseProviderRW, + checkpoint: PruneCheckpoint, + ) -> ProviderResult<()> { + crate::segments::receipts::save_checkpoint(provider, checkpoint) + } +} diff --git a/crates/prune/prune/src/segments/receipts_by_logs.rs b/crates/prune/prune/src/segments/user/receipts_by_logs.rs similarity index 98% rename from crates/prune/prune/src/segments/receipts_by_logs.rs rename to crates/prune/prune/src/segments/user/receipts_by_logs.rs index 63b5941c34..24940d1510 100644 --- a/crates/prune/prune/src/segments/receipts_by_logs.rs +++ b/crates/prune/prune/src/segments/user/receipts_by_logs.rs @@ -31,6 +31,10 @@ impl Segment for ReceiptsByLogs { None } + fn purpose(&self) -> PrunePurpose { + PrunePurpose::User + } + #[instrument(level = "trace", target = "pruner", skip(self, provider), ret)] fn prune( &self, @@ -215,7 +219,7 @@ impl Segment for ReceiptsByLogs { #[cfg(test)] mod tests { - use crate::segments::{receipts_by_logs::ReceiptsByLogs, PruneInput, Segment}; + use crate::segments::{PruneInput, ReceiptsByLogs, Segment}; use alloy_primitives::B256; use assert_matches::assert_matches; use reth_db::tables; diff --git a/crates/prune/prune/src/segments/sender_recovery.rs b/crates/prune/prune/src/segments/user/sender_recovery.rs similarity index 98% rename from crates/prune/prune/src/segments/sender_recovery.rs rename to crates/prune/prune/src/segments/user/sender_recovery.rs index 94ef6ffb7c..695147efa8 100644 --- a/crates/prune/prune/src/segments/sender_recovery.rs +++ b/crates/prune/prune/src/segments/user/sender_recovery.rs @@ -5,7 +5,7 @@ use crate::{ use reth_db::tables; use reth_db_api::database::Database; use reth_provider::{DatabaseProviderRW, TransactionsProvider}; -use reth_prune_types::{PruneMode, PruneProgress, PruneSegment}; +use reth_prune_types::{PruneMode, PruneProgress, PrunePurpose, PruneSegment}; use tracing::{instrument, trace}; #[derive(Debug)] @@ -28,6 +28,10 @@ impl Segment for SenderRecovery { Some(self.mode) } + fn purpose(&self) -> PrunePurpose { + PrunePurpose::User + } + #[instrument(level = "trace", target = "pruner", skip(self, provider), ret)] fn prune( &self, diff --git a/crates/prune/prune/src/segments/storage_history.rs b/crates/prune/prune/src/segments/user/storage_history.rs similarity index 96% rename from crates/prune/prune/src/segments/storage_history.rs rename to crates/prune/prune/src/segments/user/storage_history.rs index 95e9afa0a5..a400499c6f 100644 --- a/crates/prune/prune/src/segments/storage_history.rs +++ b/crates/prune/prune/src/segments/user/storage_history.rs @@ -1,6 +1,7 @@ use crate::{ segments::{ - history::prune_history_indices, PruneInput, PruneOutput, PruneOutputCheckpoint, Segment, + user::history::prune_history_indices, PruneInput, PruneOutput, PruneOutputCheckpoint, + Segment, }, PrunerError, }; @@ -11,7 +12,9 @@ use reth_db_api::{ models::{storage_sharded_key::StorageShardedKey, BlockNumberAddress}, }; use reth_provider::DatabaseProviderRW; -use reth_prune_types::{PruneInterruptReason, PruneMode, PruneProgress, PruneSegment}; +use reth_prune_types::{ + PruneInterruptReason, PruneMode, PruneProgress, PrunePurpose, PruneSegment, +}; use rustc_hash::FxHashMap; use tracing::{instrument, trace}; @@ -41,6 +44,10 @@ impl Segment for StorageHistory { Some(self.mode) } + fn purpose(&self) -> PrunePurpose { + PrunePurpose::User + } + #[instrument(level = "trace", target = "pruner", skip(self, provider), ret)] fn prune( &self, @@ -132,7 +139,7 @@ impl Segment for StorageHistory { #[cfg(test)] mod tests { use crate::segments::{ - storage_history::STORAGE_HISTORY_TABLES_TO_PRUNE, PruneInput, PruneOutput, Segment, + user::storage_history::STORAGE_HISTORY_TABLES_TO_PRUNE, PruneInput, PruneOutput, Segment, StorageHistory, }; use alloy_primitives::{BlockNumber, B256}; diff --git a/crates/prune/prune/src/segments/transaction_lookup.rs b/crates/prune/prune/src/segments/user/transaction_lookup.rs similarity index 98% rename from crates/prune/prune/src/segments/transaction_lookup.rs rename to crates/prune/prune/src/segments/user/transaction_lookup.rs index 457f551c16..b7889cfc64 100644 --- a/crates/prune/prune/src/segments/transaction_lookup.rs +++ b/crates/prune/prune/src/segments/user/transaction_lookup.rs @@ -6,7 +6,7 @@ use rayon::prelude::*; use reth_db::tables; use reth_db_api::database::Database; use reth_provider::{DatabaseProviderRW, TransactionsProvider}; -use reth_prune_types::{PruneMode, PruneProgress, PruneSegment}; +use reth_prune_types::{PruneMode, PruneProgress, PrunePurpose, PruneSegment}; use tracing::{instrument, trace}; #[derive(Debug)] @@ -29,6 +29,10 @@ impl Segment for TransactionLookup { Some(self.mode) } + fn purpose(&self) -> PrunePurpose { + PrunePurpose::User + } + #[instrument(level = "trace", target = "pruner", skip(self, provider), ret)] fn prune( &self, diff --git a/crates/stages/stages/src/stages/prune.rs b/crates/stages/stages/src/stages/prune.rs index 72ee685d35..f6bc8c8443 100644 --- a/crates/stages/stages/src/stages/prune.rs +++ b/crates/stages/stages/src/stages/prune.rs @@ -43,7 +43,7 @@ impl Stage for PruneStage { let mut pruner = PrunerBuilder::default() .segments(self.prune_modes.clone()) .delete_limit(self.commit_threshold) - .build(); + .build(provider.static_file_provider().clone()); let result = pruner.run(provider, input.target())?; if result.is_finished() {