OSDN Git Service

a5252626dea70a636831003ac9dce5f974936487
[bytom/vapor.git] / database / cache.go
1 package database
2
3 import (
4         "fmt"
5
6         "github.com/golang/groupcache/singleflight"
7
8         "github.com/vapor/common"
9         "github.com/vapor/protocol/bc"
10         "github.com/vapor/protocol/bc/types"
11 )
12
13 const (
14         maxCachedBlockHeaders      = 1000
15         maxCachedBlockTransactions = 1000
16 )
17
18 type fillBlockHeaderFn func(hash *bc.Hash, height uint64) (*types.BlockHeader, error)
19 type fillBlockTransactionsFn func(hash *bc.Hash) ([]*types.Tx, error)
20
21 func newBlockCache(fillBlockHeader fillBlockHeaderFn, fillBlockTxs fillBlockTransactionsFn) blockCache {
22         return blockCache{
23                 lruBlockHeaders: common.NewCache(maxCachedBlockHeaders),
24                 lruBlockTxs:     common.NewCache(maxCachedBlockTransactions),
25
26                 fillBlockHeaderFn:      fillBlockHeader,
27                 fillBlockTransactionFn: fillBlockTxs,
28         }
29 }
30
31 type blockCache struct {
32         lruBlockHeaders *common.Cache
33         lruBlockTxs     *common.Cache
34
35         fillBlockHeaderFn      func(hash *bc.Hash, height uint64) (*types.BlockHeader, error)
36         fillBlockTransactionFn func(hash *bc.Hash) ([]*types.Tx, error)
37
38         singleBlockHeader singleflight.Group
39         singleBlockTxs    singleflight.Group
40 }
41
42 func (c *blockCache) lookupBlockHeader(hash *bc.Hash, height uint64) (*types.BlockHeader, error) {
43         if bH, ok := c.getBlockHeader(hash); ok {
44                 return bH, nil
45         }
46
47         blockHeader, err := c.singleBlockHeader.Do(hash.String(), func() (interface{}, error) {
48                 bH, err := c.fillBlockHeaderFn(hash, height)
49                 if err != nil {
50                         return nil, err
51                 }
52
53                 if bH == nil {
54                         return nil, fmt.Errorf("There are no blockHeader with given hash %s", hash.String())
55                 }
56
57                 c.addBlockHeader(bH)
58                 return bH, nil
59         })
60         if err != nil {
61                 return nil, err
62         }
63         return blockHeader.(*types.BlockHeader), nil
64 }
65
66 func (c *blockCache) lookupBlockTxs(hash *bc.Hash) ([]*types.Tx, error) {
67         if bTxs, ok := c.getBlockTransactions(hash); ok {
68                 return bTxs, nil
69         }
70
71         blockTransactions, err := c.singleBlockTxs.Do(hash.String(), func() (interface{}, error) {
72                 bTxs, err := c.fillBlockTransactionFn(hash)
73                 if err != nil {
74                         return nil, err
75                 }
76
77                 if bTxs == nil {
78                         return nil, fmt.Errorf("There are no block transactions with given hash %s", hash.String())
79                 }
80
81                 c.addBlockTxs(*hash, bTxs)
82                 return bTxs, nil
83         })
84         if err != nil {
85                 return nil, err
86         }
87         return blockTransactions.([]*types.Tx), nil
88 }
89
90 func (c *blockCache) getBlockHeader(hash *bc.Hash) (*types.BlockHeader, bool) {
91         blockHeader, ok := c.lruBlockHeaders.Get(*hash)
92         if blockHeader == nil {
93                 return nil, ok
94         }
95         return blockHeader.(*types.BlockHeader), ok
96 }
97
98 func (c *blockCache) getBlockTransactions(hash *bc.Hash) ([]*types.Tx, bool) {
99         txs, ok := c.lruBlockTxs.Get(*hash)
100         if txs == nil {
101                 return nil, ok
102         }
103         return txs.([]*types.Tx), ok
104 }
105
106 func (c *blockCache) addBlockHeader(blockHeader *types.BlockHeader) {
107         c.lruBlockHeaders.Add(blockHeader.Hash(), blockHeader)
108 }
109
110 func (c *blockCache) addBlockTxs(hash bc.Hash, txs []*types.Tx) {
111         c.lruBlockTxs.Add(hash, txs)
112 }