|
// Service is responsible for handling all run time p2p related operations as the |
|
// main entry point for network messages. |
|
type Service struct { |
|
cfg *config |
|
ctx context.Context |
|
cancel context.CancelFunc |
|
slotToPendingBlocks *gcache.Cache |
|
seenPendingBlocks map[[32]byte]bool |
|
blkRootToPendingAtts map[[32]byte][]any |
|
subHandler *subTopicHandler |
|
pendingAttsLock sync.RWMutex |
|
pendingQueueLock sync.RWMutex |
|
chainStarted *atomic.Bool |
|
validateBlockLock sync.RWMutex |
|
rateLimiter *limiter |
|
seenBlockLock sync.RWMutex |
|
seenBlockCache *lru.Cache |
|
seenPayloadEnvelopeCache *lru.Cache |
|
seenExecutionPayloadBidCache *slotAwareCache |
|
highestExecutionPayloadBidCache *cache.HighestExecutionPayloadBidCache |
|
seenBlobLock sync.RWMutex |
|
seenBlobCache *lru.Cache |
|
seenDataColumnCache *slotAwareCache |
|
pendingGloasColumnsLock sync.RWMutex |
|
pendingGloasColumns map[[32]byte]*pendingGloasEntry |
|
seenAggregatedAttestationLock sync.RWMutex |
|
seenAggregatedAttestationCache *lru.Cache |
|
seenUnAggregatedAttestationLock sync.RWMutex |
|
seenUnAggregatedAttestationCache *lru.Cache |
|
seenExitLock sync.RWMutex |
|
seenExitCache *lru.Cache |
|
seenProposerSlashingLock sync.RWMutex |
|
seenProposerSlashingCache *lru.Cache |
|
seenAttesterSlashingLock sync.RWMutex |
|
seenAttesterSlashingCache map[uint64]bool |
|
seenSyncMessageLock sync.RWMutex |
|
seenSyncMessageCache *lru.Cache |
|
seenSyncContributionLock sync.RWMutex |
|
seenSyncContributionCache *lru.Cache |
|
badBlockCache *lru.Cache |
|
badBlockLock sync.RWMutex |
|
badPayloadCache *lru.Cache |
|
badPayloadLock sync.RWMutex |
|
syncContributionBitsOverlapLock sync.RWMutex |
|
syncContributionBitsOverlapCache *lru.Cache |
|
signatureChan chan *signatureVerifier |
|
clockWaiter startup.ClockWaiter |
|
initialSyncComplete chan struct{} |
|
verifierWaiter *verification.InitializerWaiter |
|
newBlobVerifier verification.NewBlobVerifier |
|
newColumnsVerifier verification.NewDataColumnsVerifier |
|
newPayloadAttestationVerifier verification.NewPayloadAttestationMsgVerifier |
|
newSignedProposerPreferencesVerifier verification.NewSignedProposerPreferencesVerifier |
|
newExecutionPayloadBidVerifier verification.NewExecutionPayloadBidVerifier |
|
columnSidecarsExecSingleFlight singleflight.Group |
|
reconstructionSingleFlight singleflight.Group |
|
payloadEnvelopeRequestSingleFlight singleflight.Group |
|
availableBlocker coverage.AvailableBlocker |
|
reconstructionRandGen *rand.Rand |
|
ctxMap ContextByteVersions |
|
slasherEnabled bool |
|
lcStore *lightClient.Store |
|
dataColumnLogCh chan dataColumnLogEntry |
|
payloadAttestationCache *cache.PayloadAttestationCache |
|
proposerPreferencesCache *cache.ProposerPreferencesCache |
|
subscribedValidatorsCache *cache.SubscribedValidatorsCache |
|
digestActions perDigestSet |
|
subscriptionSpawner func(func()) // see Service.spawn for details |
|
newExecutionPayloadEnvelopeVerifier verification.NewExecutionPayloadEnvelopeVerifier |
|
pendingPayloadEnvelopes map[[32]byte]map[uint64]*ethpb.SignedExecutionPayloadEnvelope |
|
pendingEnvelopeLock sync.RWMutex |
|
selfBuildSigFailures int |
|
selfBuildSigFailSlot primitives.Slot |
|
pendingPayloadAttestations map[[32]byte][]*ethpb.PayloadAttestationMessage |
|
pendingPayloadAttestationLock sync.RWMutex |
|
} |
prysm/beacon-chain/sync/service.go
Lines 141 to 216 in ae9e443
Currently, we have bunch of patterns to handle pending messages that heavily relies on mutex + shared data pattern. I'd say this architecture can be improved a lot by using channel pattern, which is considered more Go-idiomatic (Share Memory By Communicating). Mutex only protects the data while it cannot enforce orderings across critical sections.
pendingAttsQueuein favor of using Go channels #17251This PR is a trial to improve our pending attestation queue design. It not only moves the logic from mutex to channel-based approach but resolves pre-existing race: attestation is checked in -> block imports meanwhile -> attestation saved into a map will not be looked by anyone.
Ideally, for each pending queue, the service starts goroutines for processing the queue. The goroutine owns the pending map inside the goroutine which we can now remove mutex that protects the map. Communications are taking place via channels. Producers never block and owner never locks.
We can apply this refactoring in: