mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-20 21:34:46 +00:00
Clone state ahead of block production (#4925)
* Clone state ahead of block production * Add pruning and fix logging * Don't hold 2 states in mem
This commit is contained in:
@@ -45,6 +45,9 @@ const MAX_ADVANCE_DISTANCE: u64 = 4;
|
||||
/// impact whilst having 8 epochs without a block is a comfortable grace period.
|
||||
const MAX_FORK_CHOICE_DISTANCE: u64 = 256;
|
||||
|
||||
/// Drop any unused block production state cache after this many slots.
|
||||
const MAX_BLOCK_PRODUCTION_CACHE_DISTANCE: u64 = 4;
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Error {
|
||||
BeaconChain(BeaconChainError),
|
||||
@@ -227,19 +230,73 @@ async fn state_advance_timer<T: BeaconChainTypes>(
|
||||
|
||||
// Prepare proposers so that the node can send payload attributes in the case where
|
||||
// it decides to abandon a proposer boost re-org.
|
||||
if let Err(e) = beacon_chain.prepare_beacon_proposer(current_slot).await {
|
||||
warn!(
|
||||
log,
|
||||
"Unable to prepare proposer with lookahead";
|
||||
"error" => ?e,
|
||||
"slot" => next_slot,
|
||||
);
|
||||
}
|
||||
let proposer_head = beacon_chain
|
||||
.prepare_beacon_proposer(current_slot)
|
||||
.await
|
||||
.unwrap_or_else(|e| {
|
||||
warn!(
|
||||
log,
|
||||
"Unable to prepare proposer with lookahead";
|
||||
"error" => ?e,
|
||||
"slot" => next_slot,
|
||||
);
|
||||
None
|
||||
});
|
||||
|
||||
// Use a blocking task to avoid blocking the core executor whilst waiting for locks
|
||||
// in `ForkChoiceSignalTx`.
|
||||
beacon_chain.task_executor.clone().spawn_blocking(
|
||||
move || {
|
||||
// If we're proposing, clone the head state preemptively so that it isn't on
|
||||
// the hot path of proposing. We can delete this once we have tree-states.
|
||||
if let Some(proposer_head) = proposer_head {
|
||||
let mut cache = beacon_chain.block_production_state.lock();
|
||||
|
||||
// Avoid holding two states in memory. It's OK to hold the lock because
|
||||
// we always lock the block production cache before the snapshot cache
|
||||
// and we prefer for block production to wait for the block production
|
||||
// cache if a clone is in-progress.
|
||||
if cache
|
||||
.as_ref()
|
||||
.map_or(false, |(cached_head, _)| *cached_head != proposer_head)
|
||||
{
|
||||
drop(cache.take());
|
||||
}
|
||||
if let Some(proposer_state) = beacon_chain
|
||||
.snapshot_cache
|
||||
.try_read_for(BLOCK_PROCESSING_CACHE_LOCK_TIMEOUT)
|
||||
.and_then(|snapshot_cache| {
|
||||
snapshot_cache.get_state_for_block_production(proposer_head)
|
||||
})
|
||||
{
|
||||
*cache = Some((proposer_head, proposer_state));
|
||||
debug!(
|
||||
log,
|
||||
"Cloned state ready for block production";
|
||||
"head_block_root" => ?proposer_head,
|
||||
"slot" => next_slot
|
||||
);
|
||||
} else {
|
||||
warn!(
|
||||
log,
|
||||
"Block production state missing from snapshot cache";
|
||||
"head_block_root" => ?proposer_head,
|
||||
"slot" => next_slot
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// If we aren't proposing, drop any old block production cache to save
|
||||
// memory.
|
||||
let mut cache = beacon_chain.block_production_state.lock();
|
||||
if let Some((_, state)) = &*cache {
|
||||
if state.pre_state.slot() + MAX_BLOCK_PRODUCTION_CACHE_DISTANCE
|
||||
<= current_slot
|
||||
{
|
||||
drop(cache.take());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Signal block proposal for the next slot (if it happens to be waiting).
|
||||
if let Some(tx) = &beacon_chain.fork_choice_signal_tx {
|
||||
if let Err(e) = tx.notify_fork_choice_complete(next_slot) {
|
||||
|
||||
Reference in New Issue
Block a user