Impl oneshot_broadcast for committee promises (#3595)

## Issue Addressed

NA

## Proposed Changes

Fixes an issue introduced in #3574 where I erroneously assumed that a `crossbeam_channel` multiple receiver queue was a *broadcast* queue. This is incorrect, each message will be received by *only one* receiver. The effect of this mistake is these logs:

```
Sep 20 06:56:17.001 INFO Synced                                  slot: 4736079, block: 0xaa8a…180d, epoch: 148002, finalized_epoch: 148000, finalized_root: 0x2775…47f2, exec_hash: 0x2ca5…ffde (verified), peers: 6, service: slot_notifier
Sep 20 06:56:23.237 ERRO Unable to validate attestation          error: CommitteeCacheWait(RecvError), peer_id: 16Uiu2HAm2Jnnj8868tb7hCta1rmkXUf5YjqUH1YPj35DCwNyeEzs, type: "aggregated", slot: Slot(4736047), beacon_block_root: 0x88d318534b1010e0ebd79aed60b6b6da1d70357d72b271c01adf55c2b46206c1
```

## Additional Info

NA
This commit is contained in:
Paul Hauner
2022-09-21 01:01:50 +00:00
parent a95bcba2ab
commit 96692b8e43
8 changed files with 218 additions and 19 deletions

View File

@@ -4609,13 +4609,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
metrics::stop_timer(committee_building_timer);
if let Err(e) = sender.send(committee_cache.clone()) {
debug!(
self.log,
"Did not fulfil committee promise";
"error" => %e
)
}
sender.send(committee_cache.clone());
map_fn(&committee_cache, shuffling_decision_block)
}

View File

@@ -202,7 +202,7 @@ pub enum BeaconChainError {
},
AttestationHeadNotInForkChoice(Hash256),
MissingPersistedForkChoice,
CommitteeCacheWait(crossbeam_channel::RecvError),
CommitteePromiseFailed(oneshot_broadcast::Error),
MaxCommitteePromises(usize),
}

View File

@@ -1,6 +1,6 @@
use crate::{metrics, BeaconChainError};
use crossbeam_channel::{bounded, Receiver, Sender, TryRecvError};
use lru::LruCache;
use oneshot_broadcast::{oneshot, Receiver, Sender};
use std::sync::Arc;
use types::{beacon_state::CommitteeCache, AttestationShufflingId, Epoch, Hash256};
@@ -40,7 +40,7 @@ impl CacheItem {
CacheItem::Committee(cache) => Ok(cache),
CacheItem::Promise(receiver) => receiver
.recv()
.map_err(BeaconChainError::CommitteeCacheWait),
.map_err(BeaconChainError::CommitteePromiseFailed),
}
}
}
@@ -72,7 +72,7 @@ impl ShufflingCache {
item @ Some(CacheItem::Promise(receiver)) => match receiver.try_recv() {
// The promise has already been resolved. Replace the entry in the cache with a
// `Committee` entry and then return the committee.
Ok(committee) => {
Ok(Some(committee)) => {
metrics::inc_counter(&metrics::SHUFFLING_CACHE_PROMISE_HITS);
metrics::inc_counter(&metrics::SHUFFLING_CACHE_HITS);
let ready = CacheItem::Committee(committee);
@@ -81,7 +81,7 @@ impl ShufflingCache {
}
// The promise has not yet been resolved. Return the promise so the caller can await
// it.
Err(TryRecvError::Empty) => {
Ok(None) => {
metrics::inc_counter(&metrics::SHUFFLING_CACHE_PROMISE_HITS);
metrics::inc_counter(&metrics::SHUFFLING_CACHE_HITS);
item.cloned()
@@ -96,7 +96,7 @@ impl ShufflingCache {
// memory and the nature of the LRU cache means that future, relevant entries will
// still be added to the cache. We expect that *all* promises should be resolved,
// unless there is a programming or database error.
Err(TryRecvError::Disconnected) => {
Err(oneshot_broadcast::Error::SenderDropped) => {
metrics::inc_counter(&metrics::SHUFFLING_CACHE_PROMISE_FAILS);
metrics::inc_counter(&metrics::SHUFFLING_CACHE_MISSES);
self.cache.pop(key);
@@ -147,7 +147,7 @@ impl ShufflingCache {
return Err(BeaconChainError::MaxCommitteePromises(num_active_promises));
}
let (sender, receiver) = bounded(1);
let (sender, receiver) = oneshot();
self.cache.put(key, CacheItem::Promise(receiver));
Ok(sender)
}
@@ -262,7 +262,7 @@ mod test {
);
// Resolve the promise.
sender.send(committee_a.clone()).unwrap();
sender.send(committee_a.clone());
// Ensure the promise has been resolved.
let item = cache.get(&id_a).unwrap();
@@ -324,7 +324,7 @@ mod test {
);
// Resolve promise A.
sender_a.send(committee_a.clone()).unwrap();
sender_a.send(committee_a.clone());
// Ensure promise A has been resolved.
let item = cache.get(&id_a).unwrap();
assert!(
@@ -333,7 +333,7 @@ mod test {
);
// Resolve promise B.
sender_b.send(committee_b.clone()).unwrap();
sender_b.send(committee_b.clone());
// Ensure promise B has been resolved.
let item = cache.get(&id_b).unwrap();
assert!(