Skip to content

Commit

Permalink
Clone the head state
Browse files Browse the repository at this point in the history
  • Loading branch information
paulhauner committed Jul 28, 2021
1 parent 625a69e commit 8cb488e
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 19 deletions.
17 changes: 13 additions & 4 deletions beacon_node/beacon_chain/src/attester_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -278,10 +278,14 @@ impl AttesterCache {
Ok(())
}

/// Read the state identified by `state_root` from the database, advance it to the required
/// Read the state identified by `state_root` from the database*, advance it to the required
/// slot, use it to prime the cache and return the values for the provided `slot` and
/// `committee_index`.
///
/// *: The database read is avoided if `state_opt.is_some()`.
///
/// If `state_opt.is_some()`, the `state_root` *must* match that state.
///
/// ## Notes
///
/// This function takes a write-lock on the internal cache. Prefer attempting a `Self::get` call
Expand All @@ -290,6 +294,7 @@ impl AttesterCache {
pub fn load_and_cache_state<T: BeaconChainTypes>(
&self,
state_root: Hash256,
state_opt: Option<BeaconState<T::EthSpec>>,
key: AttesterCacheKey,
slot: Slot,
committee_index: CommitteeIndex,
Expand All @@ -316,9 +321,13 @@ impl AttesterCache {
return Ok(value);
}

let mut state: BeaconState<T::EthSpec> = chain
.get_state(&state_root, None)?
.ok_or(Error::MissingBeaconState(state_root))?;
let mut state = if let Some(state) = state_opt {
state
} else {
chain
.get_state(&state_root, None)?
.ok_or(Error::MissingBeaconState(state_root))?
};

if state.slot() > slot {
// This indicates an internal inconsistency.
Expand Down
52 changes: 37 additions & 15 deletions beacon_node/beacon_chain/src/beacon_chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1248,6 +1248,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
let beacon_state_root;
let target;
let current_epoch_attesting_info: Option<(Checkpoint, usize)>;
let head_state_clone: Option<Box<BeaconState<T::EthSpec>>>;
let attester_cache_key;
let head_timer = metrics::start_timer(&metrics::ATTESTATION_PRODUCTION_HEAD_SCRAPE_SECONDS);
if let Some(head) = self.canonical_head.try_read_for(HEAD_LOCK_TIMEOUT) {
Expand Down Expand Up @@ -1305,21 +1306,41 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
root: target_root,
};

current_epoch_attesting_info = if head_state.current_epoch() == request_epoch {
// When the head state is in the same epoch as the request, all the information
// required to attest is available on the head state.
Some((
head_state.current_justified_checkpoint(),
head_state
.get_beacon_committee(request_slot, request_index)?
.committee
.len(),
))
} else {
// If the head state is in a *different* epoch to the request, more work is required
// to determine the justified checkpoint and committee length.
None
};
match request_epoch.cmp(&head_state.current_epoch()) {
// The request is in the same epoch as the head state.
Ordering::Equal => {
// When the head state is in the same epoch as the request, all the information
// required to attest is available on the head state.
current_epoch_attesting_info = Some((
head_state.current_justified_checkpoint(),
head_state
.get_beacon_committee(request_slot, request_index)?
.committee
.len(),
));
// There is no need to clone the head state, all required information has
// already been obtained.
head_state_clone = None;
}
// The request is in a *later* epoch than the head state.
Ordering::Greater => {
// The justified checkpoint in the head state is not useful in this scenario.
current_epoch_attesting_info = None;
// The head state *is* useful in this this scenario because we can advance it
// into the required epoch.
head_state_clone = Some(Box::new(
head_state.clone_with(CloneConfig::committee_caches_only()),
));
}
// The request is in a *earlier* epoch than the head state.
Ordering::Less => {
// The justified checkpoint in the head state is not useful in this scenario.
current_epoch_attesting_info = None;
// The head state is not useful in this scenario, we must load an older one from
// disk.
head_state_clone = None;
}
}

// Determine the key for `self.attester_cache`, in case it is required later in this
// routine.
Expand Down Expand Up @@ -1369,6 +1390,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
metrics::start_timer(&metrics::ATTESTATION_PRODUCTION_CACHE_PRIME_SECONDS);
self.attester_cache.load_and_cache_state(
beacon_state_root,
head_state_clone.map(|boxed| *boxed),
attester_cache_key,
request_slot,
request_index,
Expand Down

0 comments on commit 8cb488e

Please sign in to comment.