mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-09 11:41:51 +00:00
Avoid duplicate committee cache loads (#3574)
## Issue Addressed NA ## Proposed Changes I have observed scenarios on Goerli where Lighthouse was receiving attestations which reference the same, un-cached shuffling on multiple threads at the same time. Lighthouse was then loading the same state from database and determining the shuffling on multiple threads at the same time. This is unnecessary load on the disk and RAM. This PR modifies the shuffling cache so that each entry can be either: - A committee - A promise for a committee (i.e., a `crossbeam_channel::Receiver`) Now, in the scenario where we have thread A and thread B simultaneously requesting the same un-cached shuffling, we will have the following: 1. Thread A will take the write-lock on the shuffling cache, find that there's no cached committee and then create a "promise" (a `crossbeam_channel::Sender`) for a committee before dropping the write-lock. 1. Thread B will then be allowed to take the write-lock for the shuffling cache and find the promise created by thread A. It will block the current thread waiting for thread A to fulfill that promise. 1. Thread A will load the state from disk, obtain the shuffling, send it down the channel, insert the entry into the cache and then continue to verify the attestation. 1. Thread B will then receive the shuffling from the receiver, be un-blocked and then continue to verify the attestation. In the case where thread A fails to generate the shuffling and drops the sender, the next time that specific shuffling is requested we will detect that the channel is disconnected and return a `None` entry for that shuffling. This will cause the shuffling to be re-calculated. ## Additional Info NA
This commit is contained in:
@@ -1498,6 +1498,26 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the cache for some `RelativeEpoch`, replacing the existing cache with an
|
||||
/// un-initialized cache. Returns an error if the existing cache has not been initialized.
|
||||
pub fn take_committee_cache(
|
||||
&mut self,
|
||||
relative_epoch: RelativeEpoch,
|
||||
) -> Result<CommitteeCache, Error> {
|
||||
let i = Self::committee_cache_index(relative_epoch);
|
||||
let current_epoch = self.current_epoch();
|
||||
let cache = self
|
||||
.committee_caches_mut()
|
||||
.get_mut(i)
|
||||
.ok_or(Error::CommitteeCachesOutOfBounds(i))?;
|
||||
|
||||
if cache.is_initialized_at(relative_epoch.into_epoch(current_epoch)) {
|
||||
Ok(mem::take(cache))
|
||||
} else {
|
||||
Err(Error::CommitteeCacheUninitialized(Some(relative_epoch)))
|
||||
}
|
||||
}
|
||||
|
||||
/// Drops the cache, leaving it in an uninitialized state.
|
||||
pub fn drop_committee_cache(&mut self, relative_epoch: RelativeEpoch) -> Result<(), Error> {
|
||||
*self.committee_cache_at_index_mut(Self::committee_cache_index(relative_epoch))? =
|
||||
|
||||
Reference in New Issue
Block a user