Skip to content
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

Use a cache of one entry to build attestation #13300

Merged
merged 11 commits into from
Dec 18, 2023
Merged

Conversation

terencechain
Copy link
Member

@terencechain terencechain commented Dec 8, 2023

Alternate Implementation to #13286 and #13256

This proposal introduces a modified caching strategy for single attestation entries, implementing stricter controls on the RPC behavior.

  • Check if the current slot matches the request slot. If not, the service is not provided (New behavior).
  • If a cached entry exists for the same slot, the committee index is appended to it.
  • If no relevant cached entry is present, retrieve the necessary data from the fork choice once, and cache it, replacing the existing cached attestation.

@terencechain terencechain requested a review from a team as a code owner December 8, 2023 01:11
minDelay = float64(10) // 10 nanoseconds
maxDelay = float64(100000000) // 0.1 second
delayFactor = 1.1
const maxSize = 4
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was from the previous cache I think

cache *cache.FIFO
lock sync.RWMutex
inProgress map[string]bool
a *ethpb.AttestationData
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why would you keep the full attestation data with the given committee index ?

attestationCacheHit.Inc()
return ethpb.CopyAttestationData(item.(*attestationReqResWrapper).res), nil
return ethpb.CopyAttestationData(c.a), nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will return the wrong committee index for some validators.

isCurrentOrPastSlot := s.HeadFetcher.HeadSlot() == req.Slot || currentUnixTime >= timeThreshold

if isCurrentOrPastSlot {
err := s.AttestationCache.Put(ctx, res)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If someone requests an attestation for the previous slot you will overwrite the cache, why is this? Say you hold 1000 keys, and one of them for some reason (for example is run on a different VC on a different network and that one lags) requests for the previous slot. If you overwrite the cache then you'll fail on any of the other 1000 keys. I think we shouldn't even serve these attestations.

Also notice that there is another bug here: Suppose that the requested slot is from 5 slots ago, the first branch of || will be false but the second will be true. The attestation here will be for the wrong headBlock!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've updated the design to ensure RPC only serves current slot attestation, it should address most of the concerns. Let's wait a few days and gather more feedback from the team

Comment on lines 15 to 16
TargetCheckpoint *ethpb.Checkpoint
SourceCheckpoint *ethpb.Checkpoint
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are these pointers?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't want to copy the whole protobuf object; there are many irrelevant fields.
Instead of Checkpoint, I'll switch to the root and epoch

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use forkchoicetypes.Checkpoint for this

// ErrAlreadyInProgress appears when attempting to mark a cache as in progress while it is
// already in progress. The client should handle this error and wait for the in progress
// data to resolve via Get.
ErrAlreadyInProgress = errors.New("already in progress")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this still used? ah perhaps in another cache?

targetRoot = headRoot
}
justifiedCheckpoint := s.FinalizedFetcher.CurrentJustifiedCheckpt()
if err = s.AttestationCache.Put(ctx, &cache.AttestationConsensusData{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could possibly be contention here with 100+ routines trying to put this in the cache if this beacon node hosts many validators.

@terencechain terencechain force-pushed the attester-use-fc-2 branch 2 times, most recently from 37518b2 to 30afc71 Compare December 13, 2023 16:55
if err != nil {
return nil, &RpcError{Reason: Internal, Err: errors.Errorf("could not retrieve head state: %v", err)}
return nil, &RpcError{Reason: Internal, Err: errors.Errorf("could not retrieve data from attestation cache: %v", err)}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there is an error, we need to unlock the attestation cache, otherwise it remains permanently locked.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That function never errors, we should change the signature.

nisdas
nisdas previously approved these changes Dec 14, 2023
attestationCacheMiss.Inc()
return nil, nil
// Get retrieves cached attestation data, recording a cache hit or miss. This method is lock free.
func (c *AttestationCache) Get(ctx context.Context) (*AttestationConsensusData, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the error signature here?

lock sync.RWMutex
inProgress map[string]bool
a *AttestationConsensusData
lock sync.RWMutex
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to have this named, see also below.

}
return reqToKey(w.req)
// Unlock unlocks the cache for writing.
func (c *AttestationCache) Unlock() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is not needed if you not name the lock

func reqToKey(req *ethpb.AttestationDataRequest) (string, error) {
return fmt.Sprintf("%d", req.Slot), nil
// RLock locks the cache for reading.
func (c *AttestationCache) RLock() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is not needed if you don' t name the lock.

return res, nil
}
return nil, &RpcError{Reason: Internal, Err: errors.Errorf("could not mark attestation as in-progress: %v", err)}
if req.Slot != s.GenesisTimeFetcher.CurrentSlot() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please move this before the call to ValidateAttestationTime that does a bunch of silly computations.

if err != nil {
return nil, &RpcError{Reason: Internal, Err: errors.Errorf("could not retrieve head state: %v", err)}
return nil, &RpcError{Reason: Internal, Err: errors.Errorf("could not retrieve data from attestation cache: %v", err)}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That function never errors, we should change the signature.

@terencechain terencechain force-pushed the attester-use-fc-2 branch 4 times, most recently from 575aacf to e6e73fe Compare December 18, 2023 08:29
potuz
potuz previously approved these changes Dec 18, 2023
beacon-chain/rpc/prysm/v1alpha1/validator/attester_test.go Outdated Show resolved Hide resolved
Co-authored-by: Potuz <potuz@prysmaticlabs.com>
@terencechain terencechain added this pull request to the merge queue Dec 18, 2023
Merged via the queue into develop with commit 0eff83c Dec 18, 2023
17 checks passed
@terencechain terencechain deleted the attester-use-fc-2 branch December 18, 2023 16:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants