1use crate::{PruneCheckpoint, PruneMode, PruneSegment};
2use alloc::{format, string::ToString, vec::Vec};
3use alloy_primitives::{BlockNumber, TxNumber};
4use core::time::Duration;
5use derive_more::Display;
6use tracing::debug;
7
8#[derive(Debug)]
10pub struct PrunerOutput {
11 pub progress: PruneProgress,
13 pub segments: Vec<(PruneSegment, SegmentOutput)>,
15}
16
17impl From<PruneProgress> for PrunerOutput {
18 fn from(progress: PruneProgress) -> Self {
19 Self { progress, segments: Vec::new() }
20 }
21}
22
23impl PrunerOutput {
24 #[inline]
29 pub fn debug_log(
30 &self,
31 tip_block_number: BlockNumber,
32 deleted_entries: usize,
33 elapsed: Duration,
34 ) {
35 let message = match self.progress {
36 PruneProgress::HasMoreData(_) => "Pruner interrupted, has more data",
37 PruneProgress::Finished => "Pruner finished",
38 };
39
40 let segments: Vec<_> = self
41 .segments
42 .iter()
43 .filter(|(_, seg)| seg.pruned > 0)
44 .map(|(segment, seg)| {
45 let block = seg
46 .checkpoint
47 .and_then(|c| c.block_number)
48 .map(|b| b.to_string())
49 .unwrap_or_else(|| "?".to_string());
50 let status = if seg.progress.is_finished() { "done" } else { "in_progress" };
51 format!("{segment}[{block}, {status}]")
52 })
53 .collect();
54
55 debug!(
56 target: "pruner",
57 %tip_block_number,
58 deleted_entries,
59 ?elapsed,
60 segments = %segments.join(" "),
61 "{message}",
62 );
63 }
64}
65
66#[derive(Debug, Clone, PartialEq, Eq, Display)]
68#[display("(table={segment}, pruned={pruned}, status={progress})")]
69pub struct PrunedSegmentInfo {
70 pub segment: PruneSegment,
72 pub pruned: usize,
74 pub progress: PruneProgress,
76}
77
78#[derive(Debug, Clone, Copy, Eq, PartialEq)]
80pub struct SegmentOutput {
81 pub progress: PruneProgress,
83 pub pruned: usize,
85 pub checkpoint: Option<SegmentOutputCheckpoint>,
87}
88
89impl SegmentOutput {
90 pub const fn done() -> Self {
93 Self { progress: PruneProgress::Finished, pruned: 0, checkpoint: None }
94 }
95
96 pub const fn not_done(
99 reason: PruneInterruptReason,
100 checkpoint: Option<SegmentOutputCheckpoint>,
101 ) -> Self {
102 Self { progress: PruneProgress::HasMoreData(reason), pruned: 0, checkpoint }
103 }
104}
105
106#[derive(Debug, Clone, Copy, Default, Eq, PartialEq)]
108pub struct SegmentOutputCheckpoint {
109 pub block_number: Option<BlockNumber>,
111 pub tx_number: Option<TxNumber>,
113}
114
115impl SegmentOutputCheckpoint {
116 pub const fn from_prune_checkpoint(checkpoint: PruneCheckpoint) -> Self {
118 Self { block_number: checkpoint.block_number, tx_number: checkpoint.tx_number }
119 }
120
121 pub const fn as_prune_checkpoint(&self, prune_mode: PruneMode) -> PruneCheckpoint {
123 PruneCheckpoint { block_number: self.block_number, tx_number: self.tx_number, prune_mode }
124 }
125}
126
127#[derive(Debug, PartialEq, Eq, Clone, Copy, Display)]
129pub enum PruneProgress {
130 #[display("HasMoreData({_0})")]
132 HasMoreData(PruneInterruptReason),
133 #[display("Finished")]
135 Finished,
136}
137
138#[derive(Debug, PartialEq, Eq, Clone, Copy, Display)]
140pub enum PruneInterruptReason {
141 Timeout,
143 DeletedEntriesLimitReached,
145 WaitingOnSegment(PruneSegment),
147 Unknown,
149}
150
151impl PruneInterruptReason {
152 pub const fn is_timeout(&self) -> bool {
154 matches!(self, Self::Timeout)
155 }
156
157 pub const fn is_entries_limit_reached(&self) -> bool {
159 matches!(self, Self::DeletedEntriesLimitReached)
160 }
161}
162
163impl PruneProgress {
164 pub const fn is_finished(&self) -> bool {
166 matches!(self, Self::Finished)
167 }
168
169 #[must_use]
174 pub const fn combine(self, other: Self) -> Self {
175 match (self, other) {
176 (Self::HasMoreData(reason), _) => Self::HasMoreData(reason),
177 (Self::Finished, other) => other,
178 }
179 }
180}
181
182#[cfg(test)]
183mod tests {
184 use super::*;
185
186 #[test]
187 fn test_prune_progress_combine() {
188 use PruneInterruptReason::*;
189 use PruneProgress::*;
190
191 assert!(matches!(HasMoreData(Timeout).combine(Finished), HasMoreData(Timeout)));
193
194 assert!(matches!(
196 HasMoreData(Timeout).combine(HasMoreData(DeletedEntriesLimitReached)),
197 HasMoreData(Timeout)
198 ));
199
200 assert!(matches!(Finished.combine(Finished), Finished));
202 assert!(matches!(
203 Finished.combine(HasMoreData(DeletedEntriesLimitReached)),
204 HasMoreData(DeletedEntriesLimitReached)
205 ));
206 }
207}