OSDN Git Service

V0.1 vote result cache (#182)
[bytom/vapor.git] / database / cache.go
1 package database
2
3 import (
4         "fmt"
5         "strconv"
6
7         "github.com/golang/groupcache/singleflight"
8
9         "github.com/vapor/common"
10         "github.com/vapor/protocol/bc"
11         "github.com/vapor/protocol/bc/types"
12         "github.com/vapor/protocol/state"
13 )
14
15 const (
16         maxCachedBlockHeaders      = 1000
17         maxCachedBlockTransactions = 1000
18         maxCachedVoteResults       = 144 // int(60 * 60 * 24 * 1000 / consensus.BlockTimeInterval / consensus.RoundVoteBlockNums)
19 )
20
21 type fillBlockHeaderFn func(hash *bc.Hash, height uint64) (*types.BlockHeader, error)
22 type fillBlockTransactionsFn func(hash *bc.Hash) ([]*types.Tx, error)
23 type fillVoteResultFn func(seq uint64) (*state.VoteResult, error)
24
25 func newBlockCache(fillBlockHeader fillBlockHeaderFn, fillBlockTxs fillBlockTransactionsFn, fillVoteResult fillVoteResultFn) blockCache {
26         return blockCache{
27                 lruBlockHeaders: common.NewCache(maxCachedBlockHeaders),
28                 lruBlockTxs:     common.NewCache(maxCachedBlockTransactions),
29                 lruVoteResults:  common.NewCache(maxCachedVoteResults),
30
31                 fillBlockHeaderFn:      fillBlockHeader,
32                 fillBlockTransactionFn: fillBlockTxs,
33                 fillVoteResultFn:       fillVoteResult,
34         }
35 }
36
37 type blockCache struct {
38         lruBlockHeaders *common.Cache
39         lruBlockTxs     *common.Cache
40         lruVoteResults  *common.Cache
41
42         fillBlockHeaderFn      func(hash *bc.Hash, height uint64) (*types.BlockHeader, error)
43         fillBlockTransactionFn func(hash *bc.Hash) ([]*types.Tx, error)
44         fillVoteResultFn       func(seq uint64) (*state.VoteResult, error)
45
46         singleBlockHeader singleflight.Group
47         singleBlockTxs    singleflight.Group
48         singleVoteResult  singleflight.Group
49 }
50
51 func (c *blockCache) lookupBlockHeader(hash *bc.Hash, height uint64) (*types.BlockHeader, error) {
52         if bH, ok := c.getBlockHeader(hash); ok {
53                 return bH, nil
54         }
55
56         blockHeader, err := c.singleBlockHeader.Do(hash.String(), func() (interface{}, error) {
57                 bH, err := c.fillBlockHeaderFn(hash, height)
58                 if err != nil {
59                         return nil, err
60                 }
61
62                 if bH == nil {
63                         return nil, fmt.Errorf("There are no blockHeader with given hash %s", hash.String())
64                 }
65
66                 c.addBlockHeader(bH)
67                 return bH, nil
68         })
69         if err != nil {
70                 return nil, err
71         }
72         return blockHeader.(*types.BlockHeader), nil
73 }
74
75 func (c *blockCache) lookupBlockTxs(hash *bc.Hash) ([]*types.Tx, error) {
76         if bTxs, ok := c.getBlockTransactions(hash); ok {
77                 return bTxs, nil
78         }
79
80         blockTransactions, err := c.singleBlockTxs.Do(hash.String(), func() (interface{}, error) {
81                 bTxs, err := c.fillBlockTransactionFn(hash)
82                 if err != nil {
83                         return nil, err
84                 }
85
86                 if bTxs == nil {
87                         return nil, fmt.Errorf("There are no block transactions with given hash %s", hash.String())
88                 }
89
90                 c.addBlockTxs(*hash, bTxs)
91                 return bTxs, nil
92         })
93         if err != nil {
94                 return nil, err
95         }
96         return blockTransactions.([]*types.Tx), nil
97 }
98
99 func (c *blockCache) lookupVoteResult(seq uint64) (*state.VoteResult, error) {
100         if vr, ok := c.getVoteResult(seq); ok {
101                 return vr, nil
102         }
103
104         seqStr := strconv.FormatUint(seq, 10)
105         voteResult, err := c.singleVoteResult.Do(seqStr, func() (interface{}, error) {
106                 v, err := c.fillVoteResultFn(seq)
107                 if err != nil {
108                         return nil, err
109                 }
110
111                 if v == nil {
112                         return nil, fmt.Errorf("There are no vote result with given seq %s", seqStr)
113                 }
114
115                 c.addVoteResult(v)
116                 return v, nil
117         })
118         if err != nil {
119                 return nil, err
120         }
121         return voteResult.(*state.VoteResult), nil
122 }
123
124 func (c *blockCache) getBlockHeader(hash *bc.Hash) (*types.BlockHeader, bool) {
125         blockHeader, ok := c.lruBlockHeaders.Get(*hash)
126         if blockHeader == nil {
127                 return nil, ok
128         }
129         return blockHeader.(*types.BlockHeader), ok
130 }
131
132 func (c *blockCache) getBlockTransactions(hash *bc.Hash) ([]*types.Tx, bool) {
133         txs, ok := c.lruBlockTxs.Get(*hash)
134         if txs == nil {
135                 return nil, ok
136         }
137         return txs.([]*types.Tx), ok
138 }
139
140 func (c *blockCache) getVoteResult(seq uint64) (*state.VoteResult, bool) {
141         voteResult, ok := c.lruVoteResults.Get(seq)
142         if voteResult == nil {
143                 return nil, ok
144         }
145         return voteResult.(*state.VoteResult), ok
146 }
147
148 func (c *blockCache) addBlockHeader(blockHeader *types.BlockHeader) {
149         c.lruBlockHeaders.Add(blockHeader.Hash(), blockHeader)
150 }
151
152 func (c *blockCache) addBlockTxs(hash bc.Hash, txs []*types.Tx) {
153         c.lruBlockTxs.Add(hash, txs)
154 }
155
156 func (c *blockCache) addVoteResult(voteResult *state.VoteResult) {
157         c.lruVoteResults.Add(voteResult.Seq, voteResult)
158 }