@@ -62,6 +62,8 @@ pub struct HotColdDB<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> {
6262 pub hot_db : Hot ,
6363 /// LRU cache of deserialized blocks. Updated whenever a block is loaded.
6464 block_cache : Mutex < LruCache < Hash256 , SignedBeaconBlock < E > > > ,
65+ /// LRU cache of replayed states.
66+ state_cache : Mutex < LruCache < Slot , BeaconState < E > > > ,
6567 /// Chain spec.
6668 pub ( crate ) spec : ChainSpec ,
6769 /// Logger.
@@ -129,6 +131,7 @@ impl<E: EthSpec> HotColdDB<E, MemoryStore<E>, MemoryStore<E>> {
129131 cold_db : MemoryStore :: open ( ) ,
130132 hot_db : MemoryStore :: open ( ) ,
131133 block_cache : Mutex :: new ( LruCache :: new ( config. block_cache_size ) ) ,
134+ state_cache : Mutex :: new ( LruCache :: new ( config. historic_state_cache_size ) ) ,
132135 config,
133136 spec,
134137 log,
@@ -162,6 +165,7 @@ impl<E: EthSpec> HotColdDB<E, LevelDB<E>, LevelDB<E>> {
162165 cold_db : LevelDB :: open ( cold_path) ?,
163166 hot_db : LevelDB :: open ( hot_path) ?,
164167 block_cache : Mutex :: new ( LruCache :: new ( config. block_cache_size ) ) ,
168+ state_cache : Mutex :: new ( LruCache :: new ( config. historic_state_cache_size ) ) ,
165169 config,
166170 spec,
167171 log,
@@ -977,40 +981,70 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> HotColdDB<E, Hot, Cold>
977981
978982 /// Load a frozen state that lies between restore points.
979983 fn load_cold_intermediate_state ( & self , slot : Slot ) -> Result < BeaconState < E > , Error > {
984+ if let Some ( state) = self . state_cache . lock ( ) . get ( & slot) {
985+ return Ok ( state. clone ( ) ) ;
986+ }
987+
980988 // 1. Load the restore points either side of the intermediate state.
981989 let low_restore_point_idx = slot. as_u64 ( ) / self . config . slots_per_restore_point ;
982990 let high_restore_point_idx = low_restore_point_idx + 1 ;
983991
992+ // Use low restore point as the base state.
993+ let mut low_slot: Slot =
994+ Slot :: new ( low_restore_point_idx * self . config . slots_per_restore_point ) ;
995+ let mut low_state: Option < BeaconState < E > > = None ;
996+
997+ // Try to get a more recent state from the cache to avoid massive blocks replay.
998+ for ( s, state) in self . state_cache . lock ( ) . iter ( ) {
999+ if s. as_u64 ( ) / self . config . slots_per_restore_point == low_restore_point_idx
1000+ && * s < slot
1001+ && low_slot < * s
1002+ {
1003+ low_slot = * s;
1004+ low_state = Some ( state. clone ( ) ) ;
1005+ }
1006+ }
1007+
1008+ // If low_state is still None, use load_restore_point_by_index to load the state.
1009+ let low_state = match low_state {
1010+ Some ( state) => state,
1011+ None => self . load_restore_point_by_index ( low_restore_point_idx) ?,
1012+ } ;
1013+
9841014 // Acquire the read lock, so that the split can't change while this is happening.
9851015 let split = self . split . read_recursive ( ) ;
9861016
987- let low_restore_point = self . load_restore_point_by_index ( low_restore_point_idx) ?;
9881017 let high_restore_point = self . get_restore_point ( high_restore_point_idx, & split) ?;
9891018
990- // 2. Load the blocks from the high restore point back to the low restore point.
1019+ // 2. Load the blocks from the high restore point back to the low point.
9911020 let blocks = self . load_blocks_to_replay (
992- low_restore_point . slot ( ) ,
1021+ low_slot ,
9931022 slot,
9941023 self . get_high_restore_point_block_root ( & high_restore_point, slot) ?,
9951024 ) ?;
9961025
997- // 3. Replay the blocks on top of the low restore point.
1026+ // 3. Replay the blocks on top of the low point.
9981027 // Use a forwards state root iterator to avoid doing any tree hashing.
9991028 // The state root of the high restore point should never be used, so is safely set to 0.
10001029 let state_root_iter = self . forwards_state_roots_iterator_until (
1001- low_restore_point . slot ( ) ,
1030+ low_slot ,
10021031 slot,
10031032 || ( high_restore_point, Hash256 :: zero ( ) ) ,
10041033 & self . spec ,
10051034 ) ?;
10061035
1007- self . replay_blocks (
1008- low_restore_point ,
1036+ let state = self . replay_blocks (
1037+ low_state ,
10091038 blocks,
10101039 slot,
10111040 Some ( state_root_iter) ,
10121041 StateRootStrategy :: Accurate ,
1013- )
1042+ ) ?;
1043+
1044+ // If state is not error, put it in the cache.
1045+ self . state_cache . lock ( ) . put ( slot, state. clone ( ) ) ;
1046+
1047+ Ok ( state)
10141048 }
10151049
10161050 /// Get the restore point with the given index, or if it is out of bounds, the split state.
0 commit comments