import (
"fmt"
- "sync"
- "github.com/golang/groupcache/lru"
"github.com/golang/groupcache/singleflight"
+ "github.com/vapor/common"
"github.com/vapor/protocol/bc"
"github.com/vapor/protocol/bc/types"
)
-const maxCachedBlocks = 30
+const (
+ maxCachedBlockHeaders = 1000
+ maxCachedBlockTransactions = 1000
+)
+
+type fillBlockHeaderFn func(hash *bc.Hash, height uint64) (*types.BlockHeader, error)
+type fillBlockTransactionsFn func(hash *bc.Hash) ([]*types.Tx, error)
-func newBlockCache(fillFn func(hash *bc.Hash) (*types.Block, error)) blockCache {
+func newBlockCache(fillBlockHeader fillBlockHeaderFn, fillBlockTxs fillBlockTransactionsFn) blockCache {
return blockCache{
- lru: lru.New(maxCachedBlocks),
- fillFn: fillFn,
+ lruBlockHeaders: common.NewCache(maxCachedBlockHeaders),
+ lruBlockTxs: common.NewCache(maxCachedBlockTransactions),
+
+ fillBlockHeaderFn: fillBlockHeader,
+ fillBlockTransactionFn: fillBlockTxs,
}
}
type blockCache struct {
- mu sync.Mutex
- lru *lru.Cache
- fillFn func(hash *bc.Hash) (*types.Block, error)
- single singleflight.Group
+ lruBlockHeaders *common.Cache
+ lruBlockTxs *common.Cache
+
+ fillBlockHeaderFn func(hash *bc.Hash, height uint64) (*types.BlockHeader, error)
+ fillBlockTransactionFn func(hash *bc.Hash) ([]*types.Tx, error)
+
+ singleBlockHeader singleflight.Group
+ singleBlockTxs singleflight.Group
}
-func (c *blockCache) lookup(hash *bc.Hash) (*types.Block, error) {
- if b, ok := c.get(hash); ok {
- return b, nil
+func (c *blockCache) lookupBlockHeader(hash *bc.Hash, height uint64) (*types.BlockHeader, error) {
+ if bH, ok := c.getBlockHeader(hash); ok {
+ return bH, nil
}
- block, err := c.single.Do(hash.String(), func() (interface{}, error) {
- b, err := c.fillFn(hash)
+ blockHeader, err := c.singleBlockHeader.Do(hash.String(), func() (interface{}, error) {
+ bH, err := c.fillBlockHeaderFn(hash, height)
if err != nil {
return nil, err
}
- if b == nil {
- return nil, fmt.Errorf("There are no block with given hash %s", hash.String())
+ if bH == nil {
+ return nil, fmt.Errorf("There are no blockHeader with given hash %s", hash.String())
}
- c.add(b)
- return b, nil
+ c.addBlockHeader(bH)
+ return bH, nil
})
if err != nil {
return nil, err
}
- return block.(*types.Block), nil
+ return blockHeader.(*types.BlockHeader), nil
}
-func (c *blockCache) get(hash *bc.Hash) (*types.Block, bool) {
- c.mu.Lock()
- block, ok := c.lru.Get(*hash)
- c.mu.Unlock()
- if block == nil {
+func (c *blockCache) lookupBlockTxs(hash *bc.Hash) ([]*types.Tx, error) {
+ if bTxs, ok := c.getBlockTransactions(hash); ok {
+ return bTxs, nil
+ }
+
+ blockTransactions, err := c.singleBlockTxs.Do(hash.String(), func() (interface{}, error) {
+ bTxs, err := c.fillBlockTransactionFn(hash)
+ if err != nil {
+ return nil, err
+ }
+
+ if bTxs == nil {
+ return nil, fmt.Errorf("There are no block transactions with given hash %s", hash.String())
+ }
+
+ c.addBlockTxs(*hash, bTxs)
+ return bTxs, nil
+ })
+ if err != nil {
+ return nil, err
+ }
+ return blockTransactions.([]*types.Tx), nil
+}
+
+func (c *blockCache) getBlockHeader(hash *bc.Hash) (*types.BlockHeader, bool) {
+ blockHeader, ok := c.lruBlockHeaders.Get(*hash)
+ if blockHeader == nil {
return nil, ok
}
- return block.(*types.Block), ok
+ return blockHeader.(*types.BlockHeader), ok
+}
+
+func (c *blockCache) getBlockTransactions(hash *bc.Hash) ([]*types.Tx, bool) {
+ txs, ok := c.lruBlockTxs.Get(*hash)
+ if txs == nil {
+ return nil, ok
+ }
+ return txs.([]*types.Tx), ok
+}
+
+func (c *blockCache) addBlockHeader(blockHeader *types.BlockHeader) {
+ c.lruBlockHeaders.Add(blockHeader.Hash(), blockHeader)
}
-func (c *blockCache) add(block *types.Block) {
- c.mu.Lock()
- c.lru.Add(block.Hash(), block)
- c.mu.Unlock()
+func (c *blockCache) addBlockTxs(hash bc.Hash, txs []*types.Tx) {
+ c.lruBlockTxs.Add(hash, txs)
}