@@ -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,
@@ -579,6 +586,9 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> HotColdDB<E, Hot, Cold>
579586 /// (which are frozen, and won't be deleted), or valid descendents of the finalized checkpoint
580587 /// (which will be deleted by this function but shouldn't be).
581588 pub fn delete_state ( & self , state_root : & Hash256 , slot : Slot ) -> Result < ( ) , Error > {
589+ // Delete the state from the cache.
590+ self . state_cache . lock ( ) . pop ( & slot) ;
591+
582592 // Delete the state summary.
583593 self . hot_db
584594 . key_delete ( DBColumn :: BeaconStateSummary . into ( ) , state_root. as_bytes ( ) ) ?;
@@ -977,40 +987,60 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> HotColdDB<E, Hot, Cold>
977987
978988 /// Load a frozen state that lies between restore points.
979989 fn load_cold_intermediate_state ( & self , slot : Slot ) -> Result < BeaconState < E > , Error > {
990+ if let Some ( state) = self . state_cache . lock ( ) . get ( & slot) {
991+ return Ok ( state. clone ( ) ) ;
992+ }
993+
980994 // 1. Load the restore points either side of the intermediate state.
981995 let low_restore_point_idx = slot. as_u64 ( ) / self . config . slots_per_restore_point ;
982996 let high_restore_point_idx = low_restore_point_idx + 1 ;
983997
998+ // Use low restore point as the base state.
999+ let mut low_slot: Slot = Slot :: new ( low_restore_point_idx * self . config . slots_per_restore_point ) ;
1000+ let mut low_state: BeaconState < E > = self . load_restore_point_by_index ( low_restore_point_idx) ?;
1001+
1002+ // Try to get a more recent state from the cache to avoid massive blocks replay.
1003+ for ( s, state) in self . state_cache . lock ( ) . iter ( ) {
1004+ if s. as_u64 ( ) / self . config . slots_per_restore_point == low_restore_point_idx && * s < slot && low_slot < * s {
1005+ low_slot = * s;
1006+ low_state = state. clone ( ) ;
1007+ }
1008+ }
1009+
9841010 // Acquire the read lock, so that the split can't change while this is happening.
9851011 let split = self . split . read_recursive ( ) ;
9861012
987- let low_restore_point = self . load_restore_point_by_index ( low_restore_point_idx) ?;
9881013 let high_restore_point = self . get_restore_point ( high_restore_point_idx, & split) ?;
9891014
990- // 2. Load the blocks from the high restore point back to the low restore point.
1015+ // 2. Load the blocks from the high restore point back to the low point.
9911016 let blocks = self . load_blocks_to_replay (
992- low_restore_point . slot ( ) ,
1017+ low_slot ,
9931018 slot,
9941019 self . get_high_restore_point_block_root ( & high_restore_point, slot) ?,
9951020 ) ?;
9961021
997- // 3. Replay the blocks on top of the low restore point.
1022+ // 3. Replay the blocks on top of the low point.
9981023 // Use a forwards state root iterator to avoid doing any tree hashing.
9991024 // The state root of the high restore point should never be used, so is safely set to 0.
10001025 let state_root_iter = self . forwards_state_roots_iterator_until (
1001- low_restore_point . slot ( ) ,
1026+ low_slot ,
10021027 slot,
10031028 || ( high_restore_point, Hash256 :: zero ( ) ) ,
10041029 & self . spec ,
10051030 ) ?;
10061031
1007- self . replay_blocks (
1008- low_restore_point ,
1032+ let state = self . replay_blocks (
1033+ low_state . clone ( ) ,
10091034 blocks,
10101035 slot,
10111036 Some ( state_root_iter) ,
10121037 StateRootStrategy :: Accurate ,
1013- )
1038+ ) ?;
1039+
1040+ // If state is not error, put it in the cache.
1041+ self . state_cache . lock ( ) . put ( slot, state. clone ( ) ) ;
1042+
1043+ Ok ( state)
10141044 }
10151045
10161046 /// Get the restore point with the given index, or if it is out of bounds, the split state.
0 commit comments