Skip to content

Commit 1d53e45

Browse files
committed
use state cache to avoid unnecessary block replay
1 parent 00cf5fc commit 1d53e45

File tree

1 file changed

+34
-9
lines changed

1 file changed

+34
-9
lines changed

beacon_node/store/src/hot_cold_store.rs

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,10 @@ use std::sync::Arc;
4040
use std::time::Duration;
4141
use 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

Comments
 (0)