import (
"fmt"
+ "strconv"
"github.com/golang/groupcache/singleflight"
"github.com/vapor/common"
"github.com/vapor/protocol/bc"
"github.com/vapor/protocol/bc/types"
+ "github.com/vapor/protocol/state"
)
const (
maxCachedBlockHeaders = 1000
maxCachedBlockTransactions = 1000
+ maxCachedVoteResults = 144 // int(60 * 60 * 24 * 1000 / consensus.BlockTimeInterval / consensus.RoundVoteBlockNums)
)
type fillBlockHeaderFn func(hash *bc.Hash, height uint64) (*types.BlockHeader, error)
type fillBlockTransactionsFn func(hash *bc.Hash) ([]*types.Tx, error)
+type fillVoteResultFn func(seq uint64) (*state.VoteResult, error)
-func newBlockCache(fillBlockHeader fillBlockHeaderFn, fillBlockTxs fillBlockTransactionsFn) blockCache {
+func newBlockCache(fillBlockHeader fillBlockHeaderFn, fillBlockTxs fillBlockTransactionsFn, fillVoteResult fillVoteResultFn) blockCache {
return blockCache{
lruBlockHeaders: common.NewCache(maxCachedBlockHeaders),
lruBlockTxs: common.NewCache(maxCachedBlockTransactions),
+ lruVoteResults: common.NewCache(maxCachedVoteResults),
fillBlockHeaderFn: fillBlockHeader,
fillBlockTransactionFn: fillBlockTxs,
+ fillVoteResultFn: fillVoteResult,
}
}
type blockCache struct {
lruBlockHeaders *common.Cache
lruBlockTxs *common.Cache
+ lruVoteResults *common.Cache
fillBlockHeaderFn func(hash *bc.Hash, height uint64) (*types.BlockHeader, error)
fillBlockTransactionFn func(hash *bc.Hash) ([]*types.Tx, error)
+ fillVoteResultFn func(seq uint64) (*state.VoteResult, error)
singleBlockHeader singleflight.Group
singleBlockTxs singleflight.Group
+ singleVoteResult singleflight.Group
}
func (c *blockCache) lookupBlockHeader(hash *bc.Hash, height uint64) (*types.BlockHeader, error) {
return blockTransactions.([]*types.Tx), nil
}
+func (c *blockCache) lookupVoteResult(seq uint64) (*state.VoteResult, error) {
+ if vr, ok := c.getVoteResult(seq); ok {
+ return vr, nil
+ }
+
+ seqStr := strconv.FormatUint(seq, 10)
+ voteResult, err := c.singleVoteResult.Do(seqStr, func() (interface{}, error) {
+ v, err := c.fillVoteResultFn(seq)
+ if err != nil {
+ return nil, err
+ }
+
+ if v == nil {
+ return nil, fmt.Errorf("There are no vote result with given seq %s", seqStr)
+ }
+
+ c.addVoteResult(v)
+ return v, nil
+ })
+ if err != nil {
+ return nil, err
+ }
+ return voteResult.(*state.VoteResult), nil
+}
+
func (c *blockCache) getBlockHeader(hash *bc.Hash) (*types.BlockHeader, bool) {
blockHeader, ok := c.lruBlockHeaders.Get(*hash)
if blockHeader == nil {
return txs.([]*types.Tx), ok
}
+func (c *blockCache) getVoteResult(seq uint64) (*state.VoteResult, bool) {
+ voteResult, ok := c.lruVoteResults.Get(seq)
+ if voteResult == nil {
+ return nil, ok
+ }
+ return voteResult.(*state.VoteResult), ok
+}
+
func (c *blockCache) addBlockHeader(blockHeader *types.BlockHeader) {
c.lruBlockHeaders.Add(blockHeader.Hash(), blockHeader)
}
func (c *blockCache) addBlockTxs(hash bc.Hash, txs []*types.Tx) {
c.lruBlockTxs.Add(hash, txs)
}
+
+func (c *blockCache) addVoteResult(voteResult *state.VoteResult) {
+ c.lruVoteResults.Add(voteResult.Seq, voteResult)
+}
"github.com/vapor/protocol/bc"
"github.com/vapor/protocol/bc/types"
+ "github.com/vapor/protocol/state"
)
func TestBlockCache(t *testing.T) {
},
}
}
+ newVoteResult := func(seq uint64) *state.VoteResult {
+ return &state.VoteResult{
+ Seq: seq,
+ }
+ }
blocks := make(map[bc.Hash]*types.Block)
for i := 0; i < maxCachedBlockHeaders+10; i++ {
block := newBlock(uint64(i))
blocks[block.Hash()] = block
}
+ voteResults := make(map[uint64]*state.VoteResult)
+ for i := 0; i < maxCachedVoteResults+10; i++ {
+ voteResult := newVoteResult(uint64(i))
+ voteResults[voteResult.Seq] = voteResult
+ }
fillBlockHeaderFn := func(hash *bc.Hash, height uint64) (*types.BlockHeader, error) {
return &blocks[*hash].BlockHeader, nil
return blocks[*hash].Transactions, nil
}
- cache := newBlockCache(fillBlockHeaderFn, fillBlockTxsFn)
+ fillVoteResultFn := func(seq uint64) (*state.VoteResult, error) {
+ return voteResults[seq], nil
+ }
+
+ cache := newBlockCache(fillBlockHeaderFn, fillBlockTxsFn, fillVoteResultFn)
for i := 0; i < maxCachedBlockHeaders+10; i++ {
block := newBlock(uint64(i))
t.Fatalf("can't find new block")
}
}
+
+ for i := 0; i < maxCachedVoteResults+10; i++ {
+ voteResult := newVoteResult(uint64(i))
+ cache.lookupVoteResult(voteResult.Seq)
+ }
+
+ for i := 0; i < 10; i++ {
+ voteResult := newVoteResult(uint64(i))
+ if v, _ := cache.getVoteResult(voteResult.Seq); v != nil {
+ t.Fatalf("find old vote result")
+ }
+ }
+
+ for i := 10; i < maxCachedVoteResults+10; i++ {
+ voteResult := newVoteResult(uint64(i))
+ if v, _ := cache.getVoteResult(voteResult.Seq); v == nil {
+ t.Fatalf("can't find new vote result")
+ }
+ }
}
return block.Transactions, nil
}
+// GetVoteResult return the vote result by given sequence
+func GetVoteResult(db dbm.DB, seq uint64) (*state.VoteResult, error) {
+ data := db.Get(calcVoteResultKey(seq))
+ if data == nil {
+ return nil, protocol.ErrNotFoundVoteResult
+ }
+
+ voteResult := new(state.VoteResult)
+ if err := json.Unmarshal(data, voteResult); err != nil {
+ return nil, errors.Wrap(err, "unmarshaling vote result")
+ }
+ return voteResult, nil
+}
+
// NewStore creates and returns a new Store object.
func NewStore(db dbm.DB) *Store {
fillBlockHeaderFn := func(hash *bc.Hash, height uint64) (*types.BlockHeader, error) {
return GetBlockHeader(db, hash, height)
}
-
fillBlockTxsFn := func(hash *bc.Hash) ([]*types.Tx, error) {
return GetBlockTransactions(db, hash)
}
-
- cache := newBlockCache(fillBlockHeaderFn, fillBlockTxsFn)
+ fillVoteResultFn := func(seq uint64) (*state.VoteResult, error) {
+ return GetVoteResult(db, seq)
+ }
+ bc := newBlockCache(fillBlockHeaderFn, fillBlockTxsFn, fillVoteResultFn)
return &Store{
db: db,
- cache: cache,
+ cache: bc,
}
}
// GetVoteResult retrive the voting result in specified vote sequence
func (s *Store) GetVoteResult(seq uint64) (*state.VoteResult, error) {
- data := s.db.Get(calcVoteResultKey(seq))
- if data == nil {
- return nil, protocol.ErrNotFoundVoteResult
- }
-
- vr := &state.VoteResult{}
- if err := json.Unmarshal(data, vr); err != nil {
- return nil, errors.Wrap(err, "unmarshaling vote result")
- }
- return vr, nil
+ return s.cache.lookupVoteResult(seq)
}
func (s *Store) LoadBlockIndex(stateBestHeight uint64) (*state.BlockIndex, error) {