@@ -15,9 +15,7 @@ use std::time::{Duration, SystemTime, UNIX_EPOCH};
15
15
use store:: hot_cold_store:: { migrate_database, HotColdDBError } ;
16
16
use store:: { DBColumn , Error , HotStateSummary , ItemStore , StoreItem , StoreOp } ;
17
17
pub use store:: { HotColdDB , MemoryStore } ;
18
- use types:: {
19
- BeaconState , BeaconStateHash , Checkpoint , Epoch , EthSpec , FixedBytesExtended , Hash256 , Slot ,
20
- } ;
18
+ use types:: { BeaconState , BeaconStateHash , Checkpoint , Epoch , EthSpec , Hash256 , Slot } ;
21
19
22
20
/// Compact at least this frequently, finalization permitting (7 days).
23
21
const MAX_COMPACTION_PERIOD_SECONDS : u64 = 604800 ;
@@ -89,7 +87,7 @@ pub struct PrevMigration {
89
87
pub enum PruningOutcome {
90
88
/// The pruning succeeded and updated the pruning checkpoint from `old_finalized_checkpoint`.
91
89
Successful {
92
- old_finalized_checkpoint : Checkpoint ,
90
+ old_finalized_checkpoint_epoch : Epoch ,
93
91
} ,
94
92
/// The run was aborted because the new finalized checkpoint is older than the previous one.
95
93
OutOfOrderFinalization {
@@ -119,6 +117,8 @@ pub enum PruningError {
119
117
MissingSummaryForFinalizedCheckpoint ( Hash256 ) ,
120
118
MissingBlindedBlock ( Hash256 ) ,
121
119
SummariesDagError ( summaries_dag:: Error ) ,
120
+ EmptyFinalizedStates ,
121
+ EmptyFinalizedBlocks ,
122
122
}
123
123
124
124
/// Message sent to the migration thread containing the information it needs to run.
@@ -356,7 +356,7 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> BackgroundMigrator<E, Ho
356
356
}
357
357
} ;
358
358
359
- let old_finalized_checkpoint = match Self :: prune_hot_db (
359
+ let old_finalized_checkpoint_epoch = match Self :: prune_hot_db (
360
360
db. clone ( ) ,
361
361
finalized_state_root. into ( ) ,
362
362
& finalized_state,
@@ -365,8 +365,8 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> BackgroundMigrator<E, Ho
365
365
log,
366
366
) {
367
367
Ok ( PruningOutcome :: Successful {
368
- old_finalized_checkpoint ,
369
- } ) => old_finalized_checkpoint ,
368
+ old_finalized_checkpoint_epoch ,
369
+ } ) => old_finalized_checkpoint_epoch ,
370
370
Ok ( PruningOutcome :: DeferredConcurrentHeadTrackerMutation ) => {
371
371
warn ! (
372
372
log,
@@ -397,7 +397,7 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> BackgroundMigrator<E, Ho
397
397
// Finally, compact the database so that new free space is properly reclaimed.
398
398
if let Err ( e) = Self :: run_compaction (
399
399
db,
400
- old_finalized_checkpoint . epoch ,
400
+ old_finalized_checkpoint_epoch ,
401
401
notif. finalized_checkpoint . epoch ,
402
402
log,
403
403
) {
@@ -475,17 +475,6 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> BackgroundMigrator<E, Ho
475
475
genesis_block_root : Hash256 ,
476
476
log : & Logger ,
477
477
) -> Result < PruningOutcome , BeaconChainError > {
478
- let old_finalized_checkpoint =
479
- store
480
- . load_pruning_checkpoint ( ) ?
481
- . unwrap_or_else ( || Checkpoint {
482
- epoch : Epoch :: new ( 0 ) ,
483
- root : Hash256 :: zero ( ) ,
484
- } ) ;
485
-
486
- let old_finalized_slot = old_finalized_checkpoint
487
- . epoch
488
- . start_slot ( E :: slots_per_epoch ( ) ) ;
489
478
let new_finalized_slot = new_finalized_checkpoint
490
479
. epoch
491
480
. start_slot ( E :: slots_per_epoch ( ) ) ;
@@ -500,21 +489,16 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> BackgroundMigrator<E, Ho
500
489
. into ( ) ) ;
501
490
}
502
491
503
- // The new finalized state must be newer than the previous finalized state.
504
- // I think this can happen sometimes currently due to `fork_choice` running in parallel
505
- // with itself and sending us notifications out of order.
506
- if old_finalized_slot > new_finalized_slot {
507
- return Ok ( PruningOutcome :: OutOfOrderFinalization {
508
- old_finalized_checkpoint,
509
- new_finalized_checkpoint,
510
- } ) ;
511
- }
492
+ // TODO(hdiff): if we remove the check of `old_finalized_slot > new_finalized_slot` can we
493
+ // ensure that a single pruning operation is running at once? If a pruning run is triggered
494
+ // with an old finalized checkpoint it can derive a stale hdiff set of slots and delete
495
+ // future ones that are necessary breaking the DB.
512
496
513
497
debug ! (
514
498
log,
515
499
"Starting database pruning" ;
516
- "old_finalized_checkpoint" => ?old_finalized_checkpoint,
517
500
"new_finalized_checkpoint" => ?new_finalized_checkpoint,
501
+ "new_finalized_state_hash" => ?new_finalized_state_hash,
518
502
) ;
519
503
520
504
let ( state_summaries_dag, block_summaries_dag) = {
@@ -580,9 +564,18 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> BackgroundMigrator<E, Ho
580
564
) ;
581
565
582
566
// Note: ancestors_of includes the finalized state root
583
- let newly_finalized_state_roots = state_summaries_dag
567
+ let newly_finalized_state_summaries = state_summaries_dag
584
568
. ancestors_of ( new_finalized_state_hash)
585
569
. map_err ( PruningError :: SummariesDagError ) ?;
570
+ let newly_finalized_state_roots = newly_finalized_state_summaries
571
+ . iter ( )
572
+ . map ( |( root, _) | * root)
573
+ . collect :: < HashSet < Hash256 > > ( ) ;
574
+ let newly_finalized_states_min_slot = * newly_finalized_state_summaries
575
+ . iter ( )
576
+ . map ( |( _, slot) | slot)
577
+ . min ( )
578
+ . ok_or ( PruningError :: EmptyFinalizedStates ) ?;
586
579
587
580
// Note: ancestors_of includes the finalized block
588
581
let newly_finalized_blocks = block_summaries_dag
@@ -592,6 +585,11 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> BackgroundMigrator<E, Ho
592
585
. iter ( )
593
586
. map ( |( root, _) | * root)
594
587
. collect :: < HashSet < Hash256 > > ( ) ;
588
+ let newly_finalized_blocks_min_slot = * newly_finalized_blocks
589
+ . iter ( )
590
+ . map ( |( _, slot) | slot)
591
+ . min ( )
592
+ . ok_or ( PruningError :: EmptyFinalizedBlocks ) ?;
595
593
596
594
// Compute the set of finalized state roots that we must keep to make the dynamic HDiff system
597
595
// work.
@@ -622,11 +620,12 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> BackgroundMigrator<E, Ho
622
620
// In the diagram below, `o` are diffs by slot that we must keep. In the prior
623
621
// finalized section there's only one chain so we preserve them unconditionally.
624
622
// For the newly finalized chain, we check which of is canonical and only keep
625
- // those.
623
+ // those. Slots below `min_finalized_state_slot` we don't have canonical
624
+ // information so we assume they are part of the finalized pruned chain.
626
625
//
627
626
// /-----o----
628
627
// o-------o------/-------o----
629
- if slot < old_finalized_slot
628
+ if slot < newly_finalized_states_min_slot
630
629
|| newly_finalized_state_roots. contains ( & state_root)
631
630
{
632
631
// Track kept summaries to debug hdiff inconsistencies with "Extra pruning information"
@@ -660,9 +659,13 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> BackgroundMigrator<E, Ho
660
659
} else if newly_finalized_block_roots. contains ( & block_root) {
661
660
// Keep recently finalized blocks
662
661
false
663
- } else if slot <= old_finalized_slot {
664
- // Keep blocks that are prior to the last finalized slot.
665
- // newly_finalized_block_roots only contains blocks over `old_finalized_slot`.
662
+ } else if slot < newly_finalized_blocks_min_slot
663
+ || newly_finalized_block_roots. contains ( & block_root)
664
+ {
665
+ // Keep recently finalized blocks that we know are canonical. Blocks with slots <
666
+ // that `newly_finalized_blocks_min_slot` we don't have canonical information so we
667
+ // assume they are part of the finalized pruned chain
668
+ //
666
669
// Pruning those risks breaking the DB by deleting canonical blocks once the HDiff
667
670
// grid advances. If the pruning routine is correct this condition should never hit.
668
671
false
@@ -679,10 +682,11 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> BackgroundMigrator<E, Ho
679
682
debug ! (
680
683
log,
681
684
"Extra pruning information" ;
682
- "old_finalized_checkpoint" => ?old_finalized_checkpoint,
683
685
"new_finalized_checkpoint" => ?new_finalized_checkpoint,
684
686
"newly_finalized_blocks" => newly_finalized_blocks. len( ) ,
687
+ "newly_finalized_blocks_min_slot" => newly_finalized_blocks_min_slot,
685
688
"newly_finalized_state_roots" => newly_finalized_state_roots. len( ) ,
689
+ "newly_finalized_states_min_slot" => newly_finalized_states_min_slot,
686
690
"required_finalized_diff_state_slots" => ?required_finalized_diff_state_slots,
687
691
"kept_summaries_for_hdiff" => ?kept_summaries_for_hdiff,
688
692
"state_summaries_count" => state_summaries_dag. summaries_count( ) ,
@@ -719,11 +723,6 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> BackgroundMigrator<E, Ho
719
723
persisted_head. as_kv_store_op ( BEACON_CHAIN_DB_KEY ) ,
720
724
) ) ;
721
725
722
- // Persist the new finalized checkpoint as the pruning checkpoint.
723
- batch. push ( StoreOp :: KeyValueOp (
724
- store. pruning_checkpoint_store_op ( new_finalized_checkpoint) ,
725
- ) ) ;
726
-
727
726
// Prune sync committee branches of non-checkpoint canonical finalized blocks
728
727
Self :: prune_non_checkpoint_sync_committee_branches ( & newly_finalized_blocks, & mut batch) ;
729
728
// Prune all payloads of the canonical finalized blocks
@@ -736,7 +735,10 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> BackgroundMigrator<E, Ho
736
735
debug ! ( log, "Database pruning complete" ) ;
737
736
738
737
Ok ( PruningOutcome :: Successful {
739
- old_finalized_checkpoint,
738
+ // TODO(hdiff): approximation of the previous finalized checkpoint. Only used in the
739
+ // compaction to compute time, can we use something else?
740
+ old_finalized_checkpoint_epoch : newly_finalized_blocks_min_slot
741
+ . epoch ( E :: slots_per_epoch ( ) ) ,
740
742
} )
741
743
}
742
744
0 commit comments