-
Notifications
You must be signed in to change notification settings - Fork 0
/
statecache.go
229 lines (186 loc) · 4.72 KB
/
statecache.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
package statecache
import (
"sync"
"time"
"github.com/0chain/common/core/logging"
lru "github.com/hashicorp/golang-lru"
"go.uber.org/zap"
)
// NewBlockTxnCaches creates a new block cache and a transaction cache for the given block
func NewBlockTxnCaches(sc *StateCache, b Block) (*BlockCache, *TransactionCache) {
bc := NewBlockCache(sc, b)
tc := NewTransactionCache(bc)
return bc, tc
}
// Value is an interface that all values in the state cache must implement
type Value interface {
Clone() Value
CopyFrom(v interface{}) bool
}
type String string
func (se String) Clone() Value {
return se
}
func (set String) CopyFrom(v interface{}) bool {
return false
}
// Cacheable checks if the given value is able to be cached
func Cacheable(v interface{}) (Value, bool) {
cv, ok := v.(Value)
return cv, ok
}
type Copyer interface {
// CopyFrom copies the value from the given value, returns false if not able to copy
CopyFrom(v interface{}) bool
}
// Copyable checks if the given value is able to be copied
func Copyable(v interface{}) (Copyer, bool) {
cv, ok := v.(Copyer)
return cv, ok
}
type valueNode struct {
data Value
deleted bool // indicates the value was removed
// round int64 // round number when this value is updated
// prevBlockHash string // previous block hash
}
type StateCache struct {
maxHisDepth int
cache *lru.Cache
hashCache *lru.Cache
lock sync.Mutex
hits int64
miss int64
}
func NewStateCache() *StateCache {
cache, err := lru.New(100 * 1024)
if err != nil {
panic(err)
}
maxHisDepth := 2000
hCache, err := lru.New(maxHisDepth)
if err != nil {
panic(err)
}
return &StateCache{
maxHisDepth: maxHisDepth,
cache: cache,
hashCache: hCache,
}
}
func (sc *StateCache) commitRound(round int64, prevHash, blockHash string) {
sc.hashCache.Add(blockHash, prevHash)
}
func (sc *StateCache) commit(bc *BlockCache) {
sc.lock.Lock()
defer sc.lock.Unlock()
_, ok := sc.hashCache.Get(bc.blockHash)
if ok {
// block already committed
return
}
bc.mu.Lock()
defer bc.mu.Unlock()
ts := time.Now()
for key, v := range bc.cache {
bvsi, ok := sc.cache.Get(key)
if !ok {
var err error
bvsi, err = lru.New(200)
if err != nil {
panic(err)
}
}
bvs := bvsi.(*lru.Cache)
if v.data != nil {
v.data = v.data.Clone()
}
bvs.Add(bc.blockHash, v)
sc.cache.Add(key, bvs)
}
sc.commitRound(bc.round, bc.prevBlockHash, bc.blockHash)
sc.hits += bc.hits
sc.miss += bc.miss
// Clear the pre-commit cache
bc.cache = make(map[string]valueNode)
logging.Logger.Debug("statecache - commit",
zap.String("block", bc.blockHash),
zap.Int64("bc_hits", bc.hits),
zap.Int64("bc_miss", bc.miss),
zap.Int64("sc_hits", sc.hits),
zap.Int64("sc_miss", sc.miss),
zap.Any("duration", time.Since(ts)))
}
// Get returns the value with the given key and block hash
func (sc *StateCache) Get(key, blockHash string) (Value, bool) {
// sc.mu.RLock()
// defer sc.mu.RUnlock()
blockValues, ok := sc.cache.Get(key)
if !ok {
logging.Logger.Debug("state cache get - key not found", zap.String("key", key))
return nil, false
}
bvs := blockValues.(*lru.Cache)
vv, ok := bvs.Get(blockHash)
if ok {
v := vv.(valueNode)
if !v.deleted {
// logging.Logger.Debug("state cache get", zap.String("key", key))
return v.data.Clone(), true
}
return nil, false
}
oldBlockHash := blockHash
var count int
for {
count++
// get previous block hash
prevHash, ok := sc.hashCache.Get(blockHash)
if !ok {
// could not find previous hash
logging.Logger.Debug("state cache - see gap", zap.String("block", blockHash))
return nil, false
}
blockHash = prevHash.(string)
vv, ok = bvs.Get(blockHash)
if !ok {
// stop if the value is not found in previous maxHisDepth rounds
if count >= sc.maxHisDepth {
logging.Logger.Debug("state cache - reach max depth", zap.String("block", blockHash))
return nil, false
}
continue
}
v := vv.(valueNode)
// // save into current block cache when it's 20 rounds behind
// if count >= 20 {
bvsi, err := lru.New(200)
if err != nil {
panic(err)
}
bvsi.Add(oldBlockHash, v)
sc.cache.Add(key, bvsi)
// logging.Logger.Debug("state cache - migrate from previous block",
// zap.String("key", key),
// zap.Int("depth", count))
// }
if v.deleted {
logging.Logger.Debug("state cache - is deleted")
return nil, false
}
return v.data.Clone(), true
}
}
// Remove removes the values map with the given key
func (sc *StateCache) Remove(key string) {
// sc.mu.Lock()
// defer sc.mu.Unlock()
sc.cache.Remove(key)
// delete(sc.cache, key)
}
func (sc *StateCache) Stats() (hits int64, miss int64) {
sc.lock.Lock()
hits, miss = sc.hits, sc.miss
sc.lock.Unlock()
return
}