@@ -10,9 +10,9 @@ use crate::iter::{BlockRootsIterator, ParentRootBlockIterator, RootsIterator};
10
10
use crate :: leveldb_store:: { BytesKey , LevelDB } ;
11
11
use crate :: memory_store:: MemoryStore ;
12
12
use crate :: metadata:: {
13
- AnchorInfo , BlobInfo , CompactionTimestamp , PruningCheckpoint , SchemaVersion , ANCHOR_INFO_KEY ,
14
- BLOB_INFO_KEY , COMPACTION_TIMESTAMP_KEY , CONFIG_KEY , CURRENT_SCHEMA_VERSION ,
15
- PRUNING_CHECKPOINT_KEY , SCHEMA_VERSION_KEY , SPLIT_KEY , STATE_UPPER_LIMIT_NO_RETAIN ,
13
+ AnchorInfo , BlobInfo , CompactionTimestamp , SchemaVersion , ANCHOR_INFO_KEY , BLOB_INFO_KEY ,
14
+ COMPACTION_TIMESTAMP_KEY , CONFIG_KEY , CURRENT_SCHEMA_VERSION , SCHEMA_VERSION_KEY , SPLIT_KEY ,
15
+ STATE_UPPER_LIMIT_NO_RETAIN ,
16
16
} ;
17
17
use crate :: metrics;
18
18
use crate :: state_cache:: { PutStateOutcome , StateCache } ;
@@ -77,6 +77,8 @@ pub struct HotColdDB<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> {
77
77
/// LRU cache of deserialized blocks and blobs. Updated whenever a block or blob is loaded.
78
78
block_cache : Mutex < BlockCache < E > > ,
79
79
/// Cache of beacon states.
80
+ ///
81
+ /// LOCK ORDERING: this lock must always be locked *after* the `split` if both are required.
80
82
state_cache : Mutex < StateCache < E > > ,
81
83
/// Immutable validator cache.
82
84
pub immutable_validators : Arc < RwLock < ValidatorPubkeyCache < E , Hot , Cold > > > ,
@@ -2385,26 +2387,17 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> HotColdDB<E, Hot, Cold>
2385
2387
self . config . compact_on_prune
2386
2388
}
2387
2389
2388
- /// Load the checkpoint to begin pruning from (the "old finalized checkpoint").
2389
- pub fn load_pruning_checkpoint ( & self ) -> Result < Option < Checkpoint > , Error > {
2390
- Ok ( self
2391
- . hot_db
2392
- . get ( & PRUNING_CHECKPOINT_KEY ) ?
2393
- . map ( |pc : PruningCheckpoint | pc. checkpoint ) )
2394
- }
2395
-
2396
- /// Store the checkpoint to begin pruning from (the "old finalized checkpoint").
2397
- pub fn store_pruning_checkpoint ( & self , checkpoint : Checkpoint ) -> Result < ( ) , Error > {
2398
- self . hot_db
2399
- . do_atomically ( vec ! [ self . pruning_checkpoint_store_op( checkpoint) ?] )
2400
- }
2401
-
2402
- /// Create a staged store for the pruning checkpoint.
2403
- pub fn pruning_checkpoint_store_op (
2404
- & self ,
2405
- checkpoint : Checkpoint ,
2406
- ) -> Result < KeyValueStoreOp , Error > {
2407
- PruningCheckpoint { checkpoint } . as_kv_store_op ( PRUNING_CHECKPOINT_KEY )
2390
+ /// Get the checkpoint to begin pruning from (the "old finalized checkpoint").
2391
+ pub fn get_pruning_checkpoint ( & self ) -> Checkpoint {
2392
+ // Since tree-states we infer the pruning checkpoint from the split, as this is simpler &
2393
+ // safer in the presence of crashes that occur after pruning but before the split is
2394
+ // updated.
2395
+ // FIXME(sproul): ensure delete PRUNING_CHECKPOINT_KEY is deleted in DB migration
2396
+ let split = self . get_split_info ( ) ;
2397
+ Checkpoint {
2398
+ epoch : split. slot . epoch ( E :: slots_per_epoch ( ) ) ,
2399
+ root : split. block_root ,
2400
+ }
2408
2401
}
2409
2402
2410
2403
/// Load the timestamp of the last compaction as a `Duration` since the UNIX epoch.
@@ -2917,8 +2910,8 @@ pub fn migrate_database<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>>(
2917
2910
store. store_cold_state ( & state_root, & state, & mut cold_db_ops) ?;
2918
2911
}
2919
2912
2920
- // There are data dependencies between calls to `store_cold_state()` that prevent us from
2921
- // doing one big call to `store.cold_db.do_atomically()` at end of the loop .
2913
+ // Cold states are diffed with respect to each other, so we need to finish writing previous
2914
+ // states before storing new ones .
2922
2915
store. cold_db . do_atomically ( cold_db_ops) ?;
2923
2916
}
2924
2917
@@ -2927,15 +2920,20 @@ pub fn migrate_database<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>>(
2927
2920
// procedure.
2928
2921
//
2929
2922
// Since it is pretty much impossible to be atomic across more than one database, we trade
2930
- // losing track of states to delete, for consistency. In other words: We should be safe to die
2931
- // at any point below but it may happen that some states won't be deleted from the hot database
2932
- // and will remain there forever. Since dying in these particular few lines should be an
2933
- // exceedingly rare event, this should be an acceptable tradeoff .
2923
+ // temporarily losing track of blocks to delete, for consistency. In other words: We should be
2924
+ // safe to die at any point below but it may happen that some blocks won't be deleted from the
2925
+ // hot database and will remain there forever. We may also temporarily abandon states, but
2926
+ // they will get picked up by the state pruning that iterates over the whole column .
2934
2927
2935
2928
// Flush to disk all the states that have just been migrated to the cold store.
2936
2929
store. cold_db . do_atomically ( cold_db_block_ops) ?;
2937
2930
store. cold_db . sync ( ) ?;
2938
2931
2932
+ // Update the split.
2933
+ //
2934
+ // NOTE(sproul): We do this in its own fsync'd transaction mostly for historical reasons, but
2935
+ // I'm scared to change it, because doing an fsync with *more data* while holding the split
2936
+ // write lock might have terrible performance implications (jamming the split for 100-500ms+).
2939
2937
{
2940
2938
let mut split_guard = store. split . write ( ) ;
2941
2939
let latest_split_slot = split_guard. slot ;
@@ -2966,13 +2964,13 @@ pub fn migrate_database<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>>(
2966
2964
} ;
2967
2965
store. hot_db . put_sync ( & SPLIT_KEY , & split) ?;
2968
2966
2969
- // Split point is now persisted in the hot database on disk. The in-memory split point
2970
- // hasn't been modified elsewhere since we keep a write lock on it. It's safe to update
2967
+ // Split point is now persisted in the hot database on disk. The in-memory split point
2968
+ // hasn't been modified elsewhere since we keep a write lock on it. It's safe to update
2971
2969
// the in-memory split point now.
2972
2970
* split_guard = split;
2973
2971
}
2974
2972
2975
- // Delete the states from the hot database if we got this far.
2973
+ // Delete the blocks and states from the hot database if we got this far.
2976
2974
store. do_atomically_with_block_and_blobs_cache ( hot_db_ops) ?;
2977
2975
2978
2976
// Update the cache's view of the finalized state.
0 commit comments