@@ -22,6 +22,7 @@ type Pruner struct {
2222 store store.Store
2323 execPruner coreexecutor.ExecPruner
2424 cfg config.PruningConfig
25+ blockTime time.Duration
2526 logger zerolog.Logger
2627
2728 // Lifecycle
@@ -36,11 +37,13 @@ func New(
3637 store store.Store ,
3738 execPruner coreexecutor.ExecPruner ,
3839 cfg config.PruningConfig ,
40+ blockTime time.Duration ,
3941) * Pruner {
4042 return & Pruner {
4143 store : store ,
4244 execPruner : execPruner ,
4345 cfg : cfg ,
46+ blockTime : blockTime ,
4447 logger : logger .With ().Str ("component" , "prune" ).Logger (),
4548 }
4649}
@@ -124,19 +127,64 @@ func (p *Pruner) pruneBlocks() error {
124127
125128 targetHeight := upperBound - p .cfg .KeepRecent
126129
127- if err := p .store .PruneBlocks (p .ctx , targetHeight ); err != nil {
128- p .logger .Error ().Err (err ).Uint64 ("target_height" , targetHeight ).Msg ("failed to prune old block data" )
130+ // Get the last pruned height to determine batch size
131+ lastPruned , err := p .getLastPrunedBlockHeight (p .ctx )
132+ if err != nil {
133+ return fmt .Errorf ("failed to get last pruned block height: %w" , err )
134+ }
135+
136+ catchUpBatchSize , normalBatchSize := p .calculateBatchSizes ()
137+
138+ remainingToPrune := targetHeight - lastPruned
139+ batchSize := normalBatchSize
140+ if remainingToPrune > catchUpBatchSize {
141+ batchSize = catchUpBatchSize
142+ }
143+
144+ // prune in batches to avoid overwhelming the system
145+ batchEnd := min (lastPruned + batchSize , targetHeight )
146+
147+ if err := p .store .PruneBlocks (p .ctx , batchEnd ); err != nil {
148+ p .logger .Error ().Err (err ).Uint64 ("target_height" , batchEnd ).Msg ("failed to prune old block data" )
149+ return err
129150 }
130151
131152 if p .execPruner != nil {
132- if err := p .execPruner .PruneExec (p .ctx , targetHeight ); err != nil && ! errors .Is (err , ds .ErrNotFound ) {
153+ if err := p .execPruner .PruneExec (p .ctx , batchEnd ); err != nil && ! errors .Is (err , ds .ErrNotFound ) {
133154 return err
134155 }
135156 }
136157
137158 return nil
138159}
139160
161+ // calculateBatchSizes returns appropriate batch sizes for catch-up and normal pruning operations.
162+ // The batch sizes are based on the pruning interval and block time to ensure reasonable progress
163+ // without overwhelming the node.
164+ // Catch-up mode is usually triggered when pruning is enabled for the first time ever, and there is a large backlog of blocks to prune.
165+ func (p * Pruner ) calculateBatchSizes () (catchUpBatchSize , normalBatchSize uint64 ) {
166+ // Calculate batch size based on pruning interval and block time.
167+ // We use 2x the blocks produced during one pruning interval as the catch-up batch size,
168+ // and 4x for normal operation. This ensures we make steady progress during catch-up
169+ // without overwhelming the node.
170+ // Example: With 100ms blocks and 15min interval: 15*60/0.1 = 9000 blocks/interval
171+ // - Catch-up batch: 18,000 blocks
172+ // - Normal batch: 36,000 blocks
173+ blocksPerInterval := uint64 (p .cfg .Interval .Duration / p .blockTime )
174+ catchUpBatchSize = blocksPerInterval * 2
175+ normalBatchSize = blocksPerInterval * 4
176+
177+ // Ensure reasonable minimums
178+ if catchUpBatchSize < 1000 {
179+ catchUpBatchSize = 1000
180+ }
181+ if normalBatchSize < 10000 {
182+ normalBatchSize = 10000
183+ }
184+
185+ return catchUpBatchSize , normalBatchSize
186+ }
187+
140188// pruneMetadata prunes old state and execution metadata entries based on the configured retention depth.
141189// It does not prunes old blocks, as those are handled by the pruning logic.
142190// Pruning old state does not lose history but limit the ability to recover (replay or rollback) to the last HEAD-N blocks, where N is the retention depth.
@@ -164,19 +212,30 @@ func (p *Pruner) pruneMetadata() error {
164212 return nil
165213 }
166214
167- for h := lastPrunedState + 1 ; h <= target ; h ++ {
215+ catchUpBatchSize , normalBatchSize := p .calculateBatchSizes ()
216+
217+ remainingToPrune := target - lastPrunedState
218+ batchSize := normalBatchSize
219+ if remainingToPrune > catchUpBatchSize {
220+ batchSize = catchUpBatchSize
221+ }
222+
223+ // prune in batches to avoid overwhelming the system
224+ batchEnd := min (lastPrunedState + batchSize , target )
225+
226+ for h := lastPrunedState + 1 ; h <= batchEnd ; h ++ {
168227 if err := p .store .DeleteStateAtHeight (p .ctx , h ); err != nil && ! errors .Is (err , ds .ErrNotFound ) {
169228 return err
170229 }
171230 }
172231
173232 if p .execPruner != nil {
174- if err := p .execPruner .PruneExec (p .ctx , target ); err != nil && ! errors .Is (err , ds .ErrNotFound ) {
233+ if err := p .execPruner .PruneExec (p .ctx , batchEnd ); err != nil && ! errors .Is (err , ds .ErrNotFound ) {
175234 return err
176235 }
177236 }
178237
179- if err := p .setLastPrunedStateHeight (p .ctx , target ); err != nil {
238+ if err := p .setLastPrunedStateHeight (p .ctx , batchEnd ); err != nil {
180239 return fmt .Errorf ("failed to set last pruned block height: %w" , err )
181240 }
182241
0 commit comments