-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Validator: Safer pending attestation records flushing #8433
Merged
Merged
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
b509327
Add a debug log to show duration
prestonvanloon 9c4b419
merge from dev
prestonvanloon 43e9086
use safe pending attestation records struct
prestonvanloon 92d38db
Merge branch 'develop' of github.com:prysmaticlabs/prysm into check-i…
prestonvanloon 577b8c4
fix build, use atomic bool
prestonvanloon 80728a6
Merge branch 'develop' of github.com:prysmaticlabs/prysm into check-i…
prestonvanloon 2f6a789
Add deadline checks to CheckSlashableAttestation
prestonvanloon 46820f9
Merge branch 'develop' of github.com:prysmaticlabs/prysm into check-i…
prestonvanloon c4972b6
Go fmt
prestonvanloon ef97d60
Add test for in-progress log
prestonvanloon 1e6d680
GoDocs
prestonvanloon 01f3e33
Rename pending attestation records to queued attestation records
prestonvanloon 50cc460
rename and add commentary on log
prestonvanloon d7b818b
Merge refs/heads/develop into check-in-progress
prylabs-bulldozer[bot] 35f7492
Merge refs/heads/develop into check-in-progress
prylabs-bulldozer[bot] c57fadc
Merge refs/heads/develop into check-in-progress
prylabs-bulldozer[bot] File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,7 @@ package kv | |
import ( | ||
"context" | ||
"fmt" | ||
"sync" | ||
"time" | ||
|
||
"github.com/pkg/errors" | ||
|
@@ -26,6 +27,45 @@ type AttestationRecord struct { | |
SigningRoot [32]byte | ||
} | ||
|
||
// NewQueuedAttestationRecords constructor allocates the underlying slice and | ||
// required attributes for managing pending attestation records. | ||
func NewQueuedAttestationRecords() *QueuedAttestationRecords { | ||
return &QueuedAttestationRecords{ | ||
records: make([]*AttestationRecord, 0, attestationBatchCapacity), | ||
} | ||
} | ||
|
||
// QueuedAttestationRecords is a thread-safe struct for managing a queue of | ||
// attestation records to save to validator database. | ||
type QueuedAttestationRecords struct { | ||
records []*AttestationRecord | ||
lock sync.RWMutex | ||
} | ||
|
||
// Append a new attestation record to the queue. | ||
func (p *QueuedAttestationRecords) Append(ar *AttestationRecord) { | ||
p.lock.Lock() | ||
defer p.lock.Unlock() | ||
p.records = append(p.records, ar) | ||
} | ||
|
||
// Flush all records. This method returns the current pending records and resets | ||
// the pending records slice. | ||
func (p *QueuedAttestationRecords) Flush() []*AttestationRecord { | ||
p.lock.Lock() | ||
defer p.lock.Unlock() | ||
recs := p.records | ||
p.records = make([]*AttestationRecord, 0, attestationBatchCapacity) | ||
return recs | ||
} | ||
|
||
// Len returns the current length of records. | ||
func (p *QueuedAttestationRecords) Len() int { | ||
p.lock.RLock() | ||
defer p.lock.RUnlock() | ||
return len(p.records) | ||
} | ||
|
||
// A wrapper over an error received from a background routine | ||
// saving batched attestations for slashing protection. | ||
// This wrapper allows us to send this response over event feeds, | ||
|
@@ -97,6 +137,9 @@ func (s *Store) CheckSlashableAttestation( | |
defer span.End() | ||
var slashKind SlashingKind | ||
err := s.view(func(tx *bolt.Tx) error { | ||
if ctx.Err() != nil { | ||
return ctx.Err() | ||
} | ||
bucket := tx.Bucket(pubKeysBucket) | ||
pkBucket := bucket.Bucket(pubKey[:]) | ||
if pkBucket == nil { | ||
|
@@ -124,6 +167,10 @@ func (s *Store) CheckSlashableAttestation( | |
} | ||
// Check for surround votes. | ||
return sourceEpochsBucket.ForEach(func(sourceEpochBytes []byte, targetEpochsBytes []byte) error { | ||
if ctx.Err() != nil { | ||
return ctx.Err() | ||
} | ||
|
||
existingSourceEpoch := bytesutil.BytesToEpochBigEndian(sourceEpochBytes) | ||
|
||
// There can be multiple target epochs attested per source epoch. | ||
|
@@ -234,19 +281,23 @@ func (s *Store) batchAttestationWrites(ctx context.Context) { | |
for { | ||
select { | ||
case v := <-s.batchedAttestationsChan: | ||
s.batchedAttestations = append(s.batchedAttestations, v) | ||
if len(s.batchedAttestations) == attestationBatchCapacity { | ||
log.WithField("numRecords", attestationBatchCapacity).Debug( | ||
s.batchedAttestations.Append(v) | ||
if numRecords := s.batchedAttestations.Len(); numRecords >= attestationBatchCapacity { | ||
log.WithField("numRecords", numRecords).Debug( | ||
"Reached max capacity of batched attestation records, flushing to DB", | ||
) | ||
s.flushAttestationRecords(ctx) | ||
if s.batchedAttestationsFlushInProgress.IsNotSet() { | ||
s.flushAttestationRecords(ctx, s.batchedAttestations.Flush()) | ||
} | ||
} | ||
case <-ticker.C: | ||
if len(s.batchedAttestations) > 0 { | ||
log.WithField("numRecords", len(s.batchedAttestations)).Debug( | ||
if numRecords := s.batchedAttestations.Len(); numRecords > 0 { | ||
log.WithField("numRecords", numRecords).Debug( | ||
"Batched attestation records write interval reached, flushing to DB", | ||
) | ||
s.flushAttestationRecords(ctx) | ||
if s.batchedAttestationsFlushInProgress.IsNotSet() { | ||
s.flushAttestationRecords(ctx, s.batchedAttestations.Flush()) | ||
} | ||
} | ||
case <-ctx.Done(): | ||
return | ||
|
@@ -258,13 +309,27 @@ func (s *Store) batchAttestationWrites(ctx context.Context) { | |
// and resets the list of batched attestations for future writes. | ||
// This function notifies all subscribers for flushed attestations | ||
// of the result of the save operation. | ||
func (s *Store) flushAttestationRecords(ctx context.Context) { | ||
func (s *Store) flushAttestationRecords(ctx context.Context, records []*AttestationRecord) { | ||
if s.batchedAttestationsFlushInProgress.IsSet() { | ||
// This should never happen. This method should not be called when a flush is already in | ||
// progress. If you are seeing this log, check the atomic bool before calling this method. | ||
log.Error("Attempted to flush attestation records when already in progress") | ||
return | ||
} | ||
s.batchedAttestationsFlushInProgress.Set() | ||
defer s.batchedAttestationsFlushInProgress.UnSet() | ||
|
||
start := time.Now() | ||
err := s.saveAttestationRecords(ctx, s.batchedAttestations) | ||
// If there was no error, we reset the batched attestations slice. | ||
err := s.saveAttestationRecords(ctx, records) | ||
// If there was any error, retry the records since the TX would have been reverted. | ||
if err == nil { | ||
log.WithField("duration", time.Since(start)).Debug("Successfully flushed batched attestations to DB") | ||
s.batchedAttestations = make([]*AttestationRecord, 0, attestationBatchCapacity) | ||
} else { | ||
// This should never happen. | ||
log.WithError(err).Error("Failed to batch save attestation records, retrying in queue") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same ^ |
||
for _, ar := range records { | ||
s.batchedAttestations.Append(ar) | ||
} | ||
} | ||
// Forward the error, if any, to all subscribers via an event feed. | ||
// We use a struct wrapper around the error as the event feed | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be at error level? maybe debug is appropriate. We should only have error logs when they are actionable to the user, and this is opaque from a user's perspective
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should never ever happen. If it is happening, then it's a development bug that was recently introduced. I will add a comment to reflect the significance