--- /dev/null
+package database
+
+import (
+ "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 = 4096
+ maxCachedBlockTransactions = 1024
+ maxCachedVoteResults = 128
+)
+
+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 newCache(fillBlockHeader fillBlockHeaderFn, fillBlockTxs fillBlockTransactionsFn, fillVoteResult fillVoteResultFn) cache {
+ return cache{
+ lruBlockHeaders: common.NewCache(maxCachedBlockHeaders),
+ lruBlockTxs: common.NewCache(maxCachedBlockTransactions),
+ lruVoteResults: common.NewCache(maxCachedVoteResults),
+
+ fillBlockHeaderFn: fillBlockHeader,
+ fillBlockTransactionFn: fillBlockTxs,
+ fillVoteResultFn: fillVoteResult,
+ }
+}
+
+type cache 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)
+
+ sf singleflight.Group
+}
+
+func (c *cache) lookupBlockHeader(hash *bc.Hash, height uint64) (*types.BlockHeader, error) {
+ if data, ok := c.lruBlockHeaders.Get(*hash); ok {
+ return data.(*types.BlockHeader), nil
+ }
+
+ blockHeader, err := c.sf.Do("BlockHeader:"+hash.String(), func() (interface{}, error) {
+ blockHeader, err := c.fillBlockHeaderFn(hash, height)
+ if err != nil {
+ return nil, err
+ }
+
+ c.lruBlockHeaders.Add(blockHeader.Hash(), blockHeader)
+ return blockHeader, nil
+ })
+ if err != nil {
+ return nil, err
+ }
+ return blockHeader.(*types.BlockHeader), nil
+}
+
+func (c *cache) lookupBlockTxs(hash *bc.Hash) ([]*types.Tx, error) {
+ if data, ok := c.lruBlockTxs.Get(*hash); ok {
+ return data.([]*types.Tx), nil
+ }
+
+ blockTxs, err := c.sf.Do("BlockTxs:"+hash.String(), func() (interface{}, error) {
+ blockTxs, err := c.fillBlockTransactionFn(hash)
+ if err != nil {
+ return nil, err
+ }
+
+ c.lruBlockTxs.Add(hash, blockTxs)
+ return blockTxs, nil
+ })
+ if err != nil {
+ return nil, err
+ }
+ return blockTxs.([]*types.Tx), nil
+}
+
+func (c *cache) lookupVoteResult(seq uint64) (*state.VoteResult, error) {
+ if data, ok := c.lruVoteResults.Get(seq); ok {
+ return data.(*state.VoteResult).Fork(), nil
+ }
+
+ seqStr := strconv.FormatUint(seq, 10)
+ voteResult, err := c.sf.Do("VoteResult:"+seqStr, func() (interface{}, error) {
+ voteResult, err := c.fillVoteResultFn(seq)
+ if err != nil {
+ return nil, err
+ }
+
+ c.lruVoteResults.Add(voteResult.Seq, voteResult)
+ return voteResult, nil
+ })
+ if err != nil {
+ return nil, err
+ }
+ return voteResult.(*state.VoteResult).Fork(), nil
+}
+
+func (c *cache) removeBlockHeader(blockHeader *types.BlockHeader) {
+ c.lruBlockHeaders.Remove(blockHeader.Hash())
+}
+
+func (c *cache) removeVoteResult(voteResult *state.VoteResult) {
+ c.lruVoteResults.Remove(voteResult.Seq)
+}