@@ -40,7 +40,10 @@ use std::sync::Arc;
4040use std:: time:: Duration ;
4141use types:: * ;
4242
43- /// On-disk database that stores finalized states efficiently.
43+ /// The number of replayed states to be cached.
44+ const STATE_CACHE_SIZE : usize = 32 ;
45+
46+ /// On-disk database that stores fnalized states efficiently.
4447///
4548/// Stores vector fields like the `block_roots` and `state_roots` separately, and only stores
4649/// intermittent "restore point" states pre-finalization.
@@ -62,6 +65,8 @@ pub struct HotColdDB<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> {
6265 pub hot_db : Hot ,
6366 /// LRU cache of deserialized blocks. Updated whenever a block is loaded.
6467 block_cache : Mutex < LruCache < Hash256 , SignedBeaconBlock < E > > > ,
68+ /// LRU cache of replayed states.
69+ state_cache : Mutex < LruCache < Slot , BeaconState < E > > > ,
6570 /// Chain spec.
6671 pub ( crate ) spec : ChainSpec ,
6772 /// Logger.
@@ -129,6 +134,7 @@ impl<E: EthSpec> HotColdDB<E, MemoryStore<E>, MemoryStore<E>> {
129134 cold_db : MemoryStore :: open ( ) ,
130135 hot_db : MemoryStore :: open ( ) ,
131136 block_cache : Mutex :: new ( LruCache :: new ( config. block_cache_size ) ) ,
137+ state_cache : Mutex :: new ( LruCache :: new ( STATE_CACHE_SIZE ) ) ,
132138 config,
133139 spec,
134140 log,
@@ -162,6 +168,7 @@ impl<E: EthSpec> HotColdDB<E, LevelDB<E>, LevelDB<E>> {
162168 cold_db : LevelDB :: open ( cold_path) ?,
163169 hot_db : LevelDB :: open ( hot_path) ?,
164170 block_cache : Mutex :: new ( LruCache :: new ( config. block_cache_size ) ) ,
171+ state_cache : Mutex :: new ( LruCache :: new ( STATE_CACHE_SIZE ) ) ,
165172 config,
166173 spec,
167174 log,
@@ -977,40 +984,58 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> HotColdDB<E, Hot, Cold>
977984
978985 /// Load a frozen state that lies between restore points.
979986 fn load_cold_intermediate_state ( & self , slot : Slot ) -> Result < BeaconState < E > , Error > {
987+ if let Some ( state) = self . state_cache . lock ( ) . get ( & slot) {
988+ return Ok ( state. clone ( ) ) ;
989+ }
990+
980991 // 1. Load the restore points either side of the intermediate state.
981992 let low_restore_point_idx = slot. as_u64 ( ) / self . config . slots_per_restore_point ;
982993 let high_restore_point_idx = low_restore_point_idx + 1 ;
983994
995+ let mut low_slot: Slot = Slot :: new ( low_restore_point_idx) ;
996+ let mut low_point: BeaconState < E > = self . load_restore_point_by_index ( low_restore_point_idx) ?;
997+
998+ // Try to get cached state if it exists.
999+ for ( s, state) in self . state_cache . lock ( ) . iter ( ) {
1000+ if s. as_u64 ( ) / self . config . slots_per_restore_point == low_restore_point_idx && * s < slot && low_slot < * s {
1001+ low_slot = * s;
1002+ low_point = state. clone ( ) ;
1003+ }
1004+ }
1005+
9841006 // Acquire the read lock, so that the split can't change while this is happening.
9851007 let split = self . split . read_recursive ( ) ;
9861008
987- let low_restore_point = self . load_restore_point_by_index ( low_restore_point_idx) ?;
9881009 let high_restore_point = self . get_restore_point ( high_restore_point_idx, & split) ?;
9891010
990- // 2. Load the blocks from the high restore point back to the low restore point.
1011+ // 2. Load the blocks from the high restore point back to the low point.
9911012 let blocks = self . load_blocks_to_replay (
992- low_restore_point . slot ( ) ,
1013+ low_slot ,
9931014 slot,
9941015 self . get_high_restore_point_block_root ( & high_restore_point, slot) ?,
9951016 ) ?;
9961017
997- // 3. Replay the blocks on top of the low restore point.
1018+ // 3. Replay the blocks on top of the low point.
9981019 // Use a forwards state root iterator to avoid doing any tree hashing.
9991020 // The state root of the high restore point should never be used, so is safely set to 0.
10001021 let state_root_iter = self . forwards_state_roots_iterator_until (
1001- low_restore_point . slot ( ) ,
1022+ low_slot ,
10021023 slot,
10031024 || ( high_restore_point, Hash256 :: zero ( ) ) ,
10041025 & self . spec ,
10051026 ) ?;
10061027
1007- self . replay_blocks (
1008- low_restore_point ,
1028+ let state = self . replay_blocks (
1029+ low_point . clone ( ) ,
10091030 blocks,
10101031 slot,
10111032 Some ( state_root_iter) ,
10121033 StateRootStrategy :: Accurate ,
1013- )
1034+ ) ?;
1035+
1036+ self . state_cache . lock ( ) . put ( slot, low_point) ;
1037+
1038+ Ok ( state)
10141039 }
10151040
10161041 /// Get the restore point with the given index, or if it is out of bounds, the split state.
0 commit comments