Skip to content

Commit

Permalink
Add cache to Eth1 RPC functions (prysmaticlabs#5347)
Browse files Browse the repository at this point in the history
* ADd cache to other function and add comment

* Add cache for eth1data

* Fix test

* Reset cache before test

* Move cache to beacon node end

* Add metrics

* Disable blocktimebyheight cache

* Fix time
  • Loading branch information
0xKiwi committed Apr 9, 2020
1 parent 06db5f6 commit 159ef3d
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 21 deletions.
70 changes: 50 additions & 20 deletions beacon-chain/powchain/block_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,26 @@ import (

"github.com/ethereum/go-ethereum/common"
"github.com/pkg/errors"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"go.opencensus.io/trace"
)

var cachedEth1VotingStartTime uint64
var cachedEth1DataBlockHeight *big.Int

var (
// Metrics
votingBlockHeightCacheMiss = promauto.NewCounter(prometheus.CounterOpts{
Name: "powchain_voting_height_cache_miss",
Help: "The number of voting block height requests that aren't present in the cache.",
})
votingBlockHeightCacheHit = promauto.NewCounter(prometheus.CounterOpts{
Name: "powchain_voting_height_cache_hit",
Help: "The number of voting block height requests that are present in the cache.",
})
)

// BlockExists returns true if the block exists, it's height and any possible error encountered.
func (s *Service) BlockExists(ctx context.Context, hash common.Hash) (bool, *big.Int, error) {
ctx, span := trace.StartSpan(ctx, "beacon-chain.web3service.BlockExists")
Expand Down Expand Up @@ -61,6 +78,14 @@ func (s *Service) BlockHashByHeight(ctx context.Context, height *big.Int) (commo
func (s *Service) BlockTimeByHeight(ctx context.Context, height *big.Int) (uint64, error) {
ctx, span := trace.StartSpan(ctx, "beacon-chain.web3service.BlockTimeByHeight")
defer span.End()
if exists, blkInfo, err := s.blockCache.BlockInfoByHeight(height); exists || err != nil {
if err != nil {
return 0, err
}
span.AddAttributes(trace.BoolAttribute("blockCacheHit", true))
return blkInfo.Time, nil
}
span.AddAttributes(trace.BoolAttribute("blockCacheHit", false))
block, err := s.blockFetcher.BlockByNumber(ctx, height)
if err != nil {
return 0, errors.Wrap(err, "could not query block with given height")
Expand All @@ -73,37 +98,42 @@ func (s *Service) BlockTimeByHeight(ctx context.Context, height *big.Int) (uint6
// or ETH1. This is called for multiple times but only changes every
// SlotsPerEth1VotingPeriod (1024 slots) so the whole method should be cached.
func (s *Service) BlockNumberByTimestamp(ctx context.Context, time uint64) (*big.Int, error) {
ctx, span := trace.StartSpan(ctx, "beacon-chain.web3service.BlockByTimestamp")
ctx, span := trace.StartSpan(ctx, "beacon-chain.web3service.BlockNumberByTimestamp")
defer span.End()

head, err := s.blockFetcher.BlockByNumber(ctx, nil)
if err != nil {
return nil, err
}

for bn := head.Number(); ; bn = big.NewInt(0).Sub(bn, big.NewInt(1)) {
if ctx.Err() != nil {
return nil, ctx.Err()
}

exists, info, err := s.blockCache.BlockInfoByHeight(bn)
if time != cachedEth1VotingStartTime || cachedEth1DataBlockHeight == nil {
votingBlockHeightCacheMiss.Inc()
head, err := s.blockFetcher.BlockByNumber(ctx, nil)
if err != nil {
return nil, err
}

if !exists {
blk, err := s.blockFetcher.BlockByNumber(ctx, bn)
for bn := head.Number(); ; bn = big.NewInt(0).Sub(bn, big.NewInt(1)) {
if ctx.Err() != nil {
return nil, ctx.Err()
}

exists, info, err := s.blockCache.BlockInfoByHeight(bn)
if err != nil {
return nil, err
}
if err := s.blockCache.AddBlock(blk); err != nil {
return nil, err

if !exists {
blk, err := s.blockFetcher.BlockByNumber(ctx, bn)
if err != nil {
return nil, err
}
info = blockToBlockInfo(blk)
}
info = blockToBlockInfo(blk)
}

if info.Time <= time {
return info.Number, nil
if info.Time <= time {
cachedEth1VotingStartTime = time
cachedEth1DataBlockHeight = info.Number
return cachedEth1DataBlockHeight, nil
}
}
} else {
votingBlockHeightCacheHit.Inc()
}
return cachedEth1DataBlockHeight, nil
}
11 changes: 11 additions & 0 deletions beacon-chain/powchain/block_reader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,9 @@ func TestBlockNumberByTimestamp(t *testing.T) {
web3Service = setDefaultMocks(web3Service)
web3Service.client = nil

if cachedEth1VotingStartTime != 0 {
t.Fatalf("Expected cached result to be 0 initially, received %d", cachedEth1VotingStartTime)
}
ctx := context.Background()
bn, err := web3Service.BlockNumberByTimestamp(ctx, 150000 /* time */)
if err != nil {
Expand All @@ -258,4 +261,12 @@ func TestBlockNumberByTimestamp(t *testing.T) {
if bn.Cmp(big.NewInt(0)) == 0 {
t.Error("Returned a block with zero number, expected to be non zero")
}

if cachedEth1VotingStartTime != 150000 {
t.Errorf("Expected cached voting start time to be equal to request, expected: %d, received: %d", 150000, cachedEth1VotingStartTime)
}

if bn.Cmp(cachedEth1DataBlockHeight) != 0 {
t.Errorf("Expected cached block height to be equal to result, expected: %d, received: %d", bn, cachedEth1DataBlockHeight)
}
}
6 changes: 5 additions & 1 deletion beacon-chain/rpc/validator/proposer.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,12 @@ func (vs *Server) eth1Data(ctx context.Context, slot uint64) (*ethpb.Eth1Data, e
if err != nil {
return nil, errors.Wrap(err, "could not get block number from timestamp")
}
eth1Data, err := vs.defaultEth1DataResponse(ctx, blockNumber)
if err != nil {
return nil, errors.Wrap(err, "could not get eth1 data from block number")
}

return vs.defaultEth1DataResponse(ctx, blockNumber)
return eth1Data, nil
}

func (vs *Server) mockETH1DataVote(ctx context.Context, slot uint64) (*ethpb.Eth1Data, error) {
Expand Down

0 comments on commit 159ef3d

Please sign in to comment.