perf(prune): use bulk table clear for PruneMode::Full (#21302)

This commit is contained in:
Georgios Konstantopoulos
2026-01-22 05:01:17 -08:00
committed by GitHub
parent 4b1c341ced
commit f692ac7d1e
3 changed files with 51 additions and 2 deletions

View File

@@ -9,6 +9,16 @@ use std::{fmt::Debug, ops::RangeBounds};
use tracing::debug;
pub(crate) trait DbTxPruneExt: DbTxMut + DbTx {
/// Clear the entire table in a single operation.
///
/// This is much faster than iterating entry-by-entry for `PruneMode::Full`.
/// Returns the number of entries that were in the table.
fn clear_table<T: Table>(&self) -> Result<usize, DatabaseError> {
let count = self.entries::<T>()?;
<Self as DbTxMut>::clear::<T>(self)?;
Ok(count)
}
/// Prune the table for the specified pre-sorted key iterator.
///
/// Returns number of rows pruned.

View File

@@ -6,7 +6,7 @@ use crate::{
use reth_db_api::{tables, transaction::DbTxMut};
use reth_provider::{BlockReader, DBProvider, TransactionsProvider};
use reth_prune_types::{
PruneMode, PrunePurpose, PruneSegment, SegmentOutput, SegmentOutputCheckpoint,
PruneMode, PruneProgress, PrunePurpose, PruneSegment, SegmentOutput, SegmentOutputCheckpoint,
};
use tracing::{instrument, trace};
@@ -48,6 +48,25 @@ where
};
let tx_range_end = *tx_range.end();
// For PruneMode::Full, clear the entire table in one operation
if self.mode.is_full() {
let pruned = provider.tx_ref().clear_table::<tables::TransactionSenders>()?;
trace!(target: "pruner", %pruned, "Cleared transaction senders table");
let last_pruned_block = provider
.block_by_transaction_id(tx_range_end)?
.ok_or(PrunerError::InconsistentData("Block for transaction is not found"))?;
return Ok(SegmentOutput {
progress: PruneProgress::Finished,
pruned,
checkpoint: Some(SegmentOutputCheckpoint {
block_number: Some(last_pruned_block),
tx_number: Some(tx_range_end),
}),
});
}
let mut limiter = input.limiter;
let mut last_pruned_transaction = tx_range_end;

View File

@@ -8,7 +8,7 @@ use rayon::prelude::*;
use reth_db_api::{tables, transaction::DbTxMut};
use reth_provider::{BlockReader, DBProvider, PruneCheckpointReader, StaticFileProviderFactory};
use reth_prune_types::{
PruneCheckpoint, PruneMode, PrunePurpose, PruneSegment, SegmentOutputCheckpoint,
PruneCheckpoint, PruneMode, PruneProgress, PrunePurpose, PruneSegment, SegmentOutputCheckpoint,
};
use reth_static_file_types::StaticFileSegment;
use tracing::{debug, instrument, trace};
@@ -82,6 +82,26 @@ where
}
}
.into_inner();
// For PruneMode::Full, clear the entire table in one operation
if self.mode.is_full() {
let pruned = provider.tx_ref().clear_table::<tables::TransactionHashNumbers>()?;
trace!(target: "pruner", %pruned, "Cleared transaction lookup table");
let last_pruned_block = provider
.block_by_transaction_id(end)?
.ok_or(PrunerError::InconsistentData("Block for transaction is not found"))?;
return Ok(SegmentOutput {
progress: PruneProgress::Finished,
pruned,
checkpoint: Some(SegmentOutputCheckpoint {
block_number: Some(last_pruned_block),
tx_number: Some(end),
}),
});
}
let tx_range = start..=
Some(end)
.min(