From dfc54cf89f6895bd7b542f6583dc75b68dcd246c Mon Sep 17 00:00:00 2001 From: YK Date: Thu, 5 Feb 2026 11:08:49 +0800 Subject: [PATCH] fix(prune): reth prune requires being run twice to actually prune (#21785) Co-authored-by: Georgios Konstantopoulos Co-authored-by: Amp --- crates/cli/commands/src/prune.rs | 7 +----- crates/prune/prune/src/pruner.rs | 4 +++- crates/prune/types/src/pruner.rs | 39 ++++++++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 7 deletions(-) diff --git a/crates/cli/commands/src/prune.rs b/crates/cli/commands/src/prune.rs index 6a7fe0e1d3..b1a761bce6 100644 --- a/crates/cli/commands/src/prune.rs +++ b/crates/cli/commands/src/prune.rs @@ -100,12 +100,7 @@ impl> PruneComma let batch_pruned: usize = output.segments.iter().map(|(_, seg)| seg.pruned).sum(); total_pruned = total_pruned.saturating_add(batch_pruned); - // Check if all segments are finished (not just the overall progress, - // since the pruner sets overall progress from the last segment only) - let all_segments_finished = - output.segments.iter().all(|(_, seg)| seg.progress.is_finished()); - - if all_segments_finished { + if output.progress.is_finished() { info!(target: "reth::cli", total_pruned, "Pruned data from database"); break; } diff --git a/crates/prune/prune/src/pruner.rs b/crates/prune/prune/src/pruner.rs index bb55f75cb0..3232a78532 100644 --- a/crates/prune/prune/src/pruner.rs +++ b/crates/prune/prune/src/pruner.rs @@ -177,6 +177,8 @@ where for segment in &self.segments { if limiter.is_limit_reached() { + output.progress = + output.progress.combine(PruneProgress::HasMoreData(limiter.interrupt_reason())); break } @@ -233,7 +235,7 @@ where .set(highest_pruned_block as f64); } - output.progress = segment_output.progress; + output.progress = output.progress.combine(segment_output.progress); output.segments.push((segment.segment(), segment_output)); debug!( diff --git a/crates/prune/types/src/pruner.rs b/crates/prune/types/src/pruner.rs index b3c8c5d5e8..c50c3b93c6 100644 --- a/crates/prune/types/src/pruner.rs +++ b/crates/prune/types/src/pruner.rs @@ -165,4 +165,43 @@ impl PruneProgress { pub const fn is_finished(&self) -> bool { matches!(self, Self::Finished) } + + /// Combines two progress values, keeping `HasMoreData` if either has it. + /// + /// Once any segment reports `HasMoreData`, the combined progress remains + /// `HasMoreData`. Only returns `Finished` if both are `Finished`. + #[must_use] + pub const fn combine(self, other: Self) -> Self { + match (self, other) { + (Self::HasMoreData(reason), _) => Self::HasMoreData(reason), + (Self::Finished, other) => other, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_prune_progress_combine() { + use PruneInterruptReason::*; + use PruneProgress::*; + + // HasMoreData dominates Finished + assert!(matches!(HasMoreData(Timeout).combine(Finished), HasMoreData(Timeout))); + + // First HasMoreData reason is preserved + assert!(matches!( + HasMoreData(Timeout).combine(HasMoreData(DeletedEntriesLimitReached)), + HasMoreData(Timeout) + )); + + // Finished adopts new progress + assert!(matches!(Finished.combine(Finished), Finished)); + assert!(matches!( + Finished.combine(HasMoreData(DeletedEntriesLimitReached)), + HasMoreData(DeletedEntriesLimitReached) + )); + } }