github.com/iotexproject/iotex-core@v1.14.1-rc1/db/batch/batch_impl.go (about) 1 // Copyright (c) 2019 IoTeX Foundation 2 // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability 3 // or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed. 4 // This source code is governed by Apache License 2.0 that can be found in the LICENSE file. 5 6 package batch 7 8 import ( 9 "sync" 10 11 "github.com/pkg/errors" 12 ) 13 14 const ( 15 // resetSnapshotIgnoreThreshold is the threshold of snapshot number to ignore reset snapshots 16 resetSnapshotIgnoreThreshold = 10 17 ) 18 19 type ( 20 // baseKVStoreBatch is the base implementation of KVStoreBatch 21 baseKVStoreBatch struct { 22 mutex sync.RWMutex 23 fillLock sync.RWMutex 24 writeQueue []*WriteInfo 25 fill map[string]float64 26 } 27 28 // cachedBatch implements the CachedBatch interface 29 cachedBatch struct { 30 lock sync.RWMutex 31 kvStoreBatch *baseKVStoreBatch 32 tag int // latest snapshot + 1 33 batchShots []int // snapshots of batch are merely size of write queue at time of snapshot 34 caches []KVStoreCache // snapshots of cache 35 keyTags map[kvCacheKey]*kvCacheValue 36 tagKeys [][]kvCacheKey 37 } 38 ) 39 40 func newBaseKVStoreBatch() *baseKVStoreBatch { 41 return &baseKVStoreBatch{ 42 fill: make(map[string]float64), 43 } 44 } 45 46 // NewBatch returns a batch 47 func NewBatch() KVStoreBatch { 48 return newBaseKVStoreBatch() 49 } 50 51 // Lock locks the batch 52 func (b *baseKVStoreBatch) Lock() { 53 b.mutex.Lock() 54 } 55 56 // Unlock unlocks the batch 57 func (b *baseKVStoreBatch) Unlock() { 58 b.mutex.Unlock() 59 } 60 61 // ClearAndUnlock clears the write queue and unlocks the batch 62 func (b *baseKVStoreBatch) ClearAndUnlock() { 63 defer b.mutex.Unlock() 64 b.writeQueue = nil 65 66 b.fillLock.Lock() 67 defer b.fillLock.Unlock() 68 for k := range b.fill { 69 delete(b.fill, k) 70 } 71 } 72 73 // Put inserts a <key, value> record 74 func (b *baseKVStoreBatch) Put(namespace string, key, value []byte, errorMessage string) { 75 b.mutex.Lock() 76 defer b.mutex.Unlock() 77 b.batch(Put, namespace, key, value, errorMessage) 78 } 79 80 // Delete deletes a record 81 func (b *baseKVStoreBatch) Delete(namespace string, key []byte, errorMessage string) { 82 b.mutex.Lock() 83 defer b.mutex.Unlock() 84 b.batch(Delete, namespace, key, nil, errorMessage) 85 } 86 87 // Size returns the size of batch 88 func (b *baseKVStoreBatch) Size() int { 89 return len(b.writeQueue) 90 } 91 92 // Entry returns the entry at the index 93 func (b *baseKVStoreBatch) Entry(index int) (*WriteInfo, error) { 94 if index < 0 || index >= len(b.writeQueue) { 95 return nil, errors.Wrap(ErrOutOfBound, "index out of range") 96 } 97 return b.writeQueue[index], nil 98 } 99 100 func (b *baseKVStoreBatch) SerializeQueue(serialize WriteInfoSerialize, filter WriteInfoFilter) []byte { 101 // 1. Digest could be replaced by merkle root if we need proof 102 b.mutex.Lock() 103 defer b.mutex.Unlock() 104 105 var ( 106 serialisedBytes = make([][]byte, len(b.writeQueue)) 107 wg = sync.WaitGroup{} 108 ) 109 110 wg.Add(len(b.writeQueue)) 111 for i, wi := range b.writeQueue { 112 go func(i int, info *WriteInfo) { 113 defer wg.Done() 114 if filter != nil && filter(info) { 115 return 116 } 117 118 idx := i 119 var data []byte 120 if serialize != nil { 121 data = serialize(info) 122 } else { 123 data = info.Serialize() 124 } 125 126 serialisedBytes[idx] = data 127 }(i, wi) 128 } 129 wg.Wait() 130 131 var returnedBytes []byte 132 for _, sb := range serialisedBytes { 133 returnedBytes = append(returnedBytes, sb...) 134 } 135 136 return returnedBytes 137 } 138 139 // Clear clear write queue 140 func (b *baseKVStoreBatch) Clear() { 141 b.mutex.Lock() 142 defer b.mutex.Unlock() 143 b.writeQueue = nil 144 145 b.fillLock.Lock() 146 defer b.fillLock.Unlock() 147 for k := range b.fill { 148 delete(b.fill, k) 149 } 150 } 151 152 func (b *baseKVStoreBatch) Translate(wit WriteInfoTranslate) KVStoreBatch { 153 b.mutex.Lock() 154 defer b.mutex.Unlock() 155 if wit == nil { 156 c := &baseKVStoreBatch{ 157 writeQueue: make([]*WriteInfo, b.Size()), 158 } 159 // clone the writeQueue 160 copy(c.writeQueue, b.writeQueue) 161 return c 162 } 163 c := &baseKVStoreBatch{ 164 writeQueue: []*WriteInfo{}, 165 } 166 for _, wi := range b.writeQueue { 167 newWi := wit(wi) 168 if newWi != nil { 169 c.writeQueue = append(c.writeQueue, newWi) 170 } 171 } 172 173 return c 174 } 175 176 func (b *baseKVStoreBatch) CheckFillPercent(ns string) (float64, bool) { 177 b.fillLock.RLock() 178 defer b.fillLock.RUnlock() 179 p, ok := b.fill[ns] 180 return p, ok 181 } 182 183 func (b *baseKVStoreBatch) AddFillPercent(ns string, percent float64) { 184 b.fillLock.Lock() 185 defer b.fillLock.Unlock() 186 b.fill[ns] = percent 187 } 188 189 // batch puts an entry into the write queue 190 func (b *baseKVStoreBatch) batch(op WriteType, namespace string, key, value []byte, errorMessage string) { 191 b.writeQueue = append( 192 b.writeQueue, 193 &WriteInfo{ 194 writeType: op, 195 namespace: namespace, 196 key: key, 197 value: value, 198 errorMessage: errorMessage, 199 }) 200 } 201 202 // truncate the write queue 203 func (b *baseKVStoreBatch) truncate(size int) { 204 b.writeQueue = b.writeQueue[:size] 205 } 206 207 //////////////////////////////////////// 208 // CachedBatch implementation 209 //////////////////////////////////////// 210 211 // NewCachedBatch returns a new cached batch buffer 212 func NewCachedBatch() CachedBatch { 213 cb := &cachedBatch{ 214 kvStoreBatch: newBaseKVStoreBatch(), 215 } 216 cb.clear() 217 218 return cb 219 } 220 221 func (cb *cachedBatch) Translate(wit WriteInfoTranslate) KVStoreBatch { 222 return cb.kvStoreBatch.Translate(wit) 223 } 224 225 func (cb *cachedBatch) Entry(i int) (*WriteInfo, error) { 226 return cb.kvStoreBatch.Entry(i) 227 } 228 229 func (cb *cachedBatch) SerializeQueue(serialize WriteInfoSerialize, filter WriteInfoFilter) []byte { 230 return cb.kvStoreBatch.SerializeQueue(serialize, filter) 231 } 232 233 func (cb *cachedBatch) Size() int { 234 return cb.kvStoreBatch.Size() 235 } 236 237 // Lock locks the batch 238 func (cb *cachedBatch) Lock() { 239 cb.lock.Lock() 240 } 241 242 // Unlock unlocks the batch 243 func (cb *cachedBatch) Unlock() { 244 cb.lock.Unlock() 245 } 246 247 // ClearAndUnlock clears the write queue and unlocks the batch 248 func (cb *cachedBatch) ClearAndUnlock() { 249 defer cb.lock.Unlock() 250 cb.clear() 251 } 252 253 func (cb *cachedBatch) currentCache() KVStoreCache { 254 return cb.caches[len(cb.caches)-1] 255 } 256 257 func (cb *cachedBatch) clear() { 258 cb.kvStoreBatch.Clear() 259 cb.tag = 0 260 cb.batchShots = make([]int, 0) 261 cb.caches = []KVStoreCache{NewKVCache()} 262 cb.keyTags = map[kvCacheKey]*kvCacheValue{} 263 cb.tagKeys = [][]kvCacheKey{{}} 264 } 265 266 func (cb *cachedBatch) touchKey(h kvCacheKey) { 267 tags, ok := cb.keyTags[h] 268 if !ok { 269 cb.keyTags[h] = newkvCacheValue([]int{cb.tag}) 270 cb.tagKeys[cb.tag] = append(cb.tagKeys[cb.tag], h) 271 return 272 } 273 if tags.last() != cb.tag { 274 tags.append(cb.tag) 275 cb.tagKeys[cb.tag] = append(cb.tagKeys[cb.tag], h) 276 } 277 } 278 279 // Put inserts a <key, value> record 280 func (cb *cachedBatch) Put(namespace string, key, value []byte, errorMessage string) { 281 cb.lock.Lock() 282 defer cb.lock.Unlock() 283 h := cb.hash(namespace, key) 284 cb.touchKey(h) 285 cb.currentCache().Write(&h, value) 286 cb.kvStoreBatch.batch(Put, namespace, key, value, errorMessage) 287 } 288 289 // Delete deletes a record 290 func (cb *cachedBatch) Delete(namespace string, key []byte, errorMessage string) { 291 cb.lock.Lock() 292 defer cb.lock.Unlock() 293 h := cb.hash(namespace, key) 294 cb.touchKey(h) 295 cb.currentCache().Evict(&h) 296 cb.kvStoreBatch.batch(Delete, namespace, key, nil, errorMessage) 297 } 298 299 // Clear clear the cached batch buffer 300 func (cb *cachedBatch) Clear() { 301 cb.lock.Lock() 302 defer cb.lock.Unlock() 303 cb.clear() 304 } 305 306 // Get retrieves a record 307 func (cb *cachedBatch) Get(namespace string, key []byte) ([]byte, error) { 308 cb.lock.RLock() 309 defer cb.lock.RUnlock() 310 h := cb.hash(namespace, key) 311 var v []byte 312 err := ErrNotExist 313 if tags, ok := cb.keyTags[h]; ok { 314 for i := tags.len() - 1; i >= 0; i-- { 315 v, err = cb.caches[tags.getAt(i)].Read(&h) 316 if errors.Cause(err) == ErrNotExist { 317 continue 318 } 319 break 320 } 321 } 322 return v, err 323 } 324 325 // Snapshot takes a snapshot of current cached batch 326 func (cb *cachedBatch) Snapshot() int { 327 cb.lock.Lock() 328 defer cb.lock.Unlock() 329 defer func() { cb.tag++ }() 330 // save a copy of current batch/cache 331 cb.batchShots = append(cb.batchShots, cb.kvStoreBatch.Size()) 332 cb.caches = append(cb.caches, NewKVCache()) 333 cb.tagKeys = append(cb.tagKeys, []kvCacheKey{}) 334 return cb.tag 335 } 336 337 func (cb *cachedBatch) RevertSnapshot(snapshot int) error { 338 cb.lock.Lock() 339 defer cb.lock.Unlock() 340 // throw error if the snapshot number does not exist 341 if snapshot < 0 || snapshot >= cb.tag { 342 return errors.Wrapf(ErrOutOfBound, "invalid snapshot number = %d", snapshot) 343 } 344 cb.tag = snapshot + 1 345 cb.batchShots = cb.batchShots[:cb.tag] 346 cb.kvStoreBatch.truncate(cb.batchShots[snapshot]) 347 cb.caches = cb.caches[:cb.tag+1] 348 cb.caches[cb.tag].Clear() 349 for tag := cb.tag; tag < len(cb.tagKeys); tag++ { 350 keys := cb.tagKeys[tag] 351 for _, key := range keys { 352 kv := cb.keyTags[key] 353 kv.pop() 354 if kv.len() == 0 { 355 delete(cb.keyTags, key) 356 } 357 } 358 } 359 cb.tagKeys = cb.tagKeys[:cb.tag+1] 360 cb.tagKeys[cb.tag] = []kvCacheKey{} 361 return nil 362 } 363 364 func (cb *cachedBatch) ResetSnapshots() { 365 cb.lock.Lock() 366 defer cb.lock.Unlock() 367 368 // ignore reset snapshots if the snapshot number is less than threshold 369 if cb.tag <= resetSnapshotIgnoreThreshold { 370 return 371 } 372 cb.tag = 0 373 cb.batchShots = nil 374 cb.batchShots = make([]int, 0) 375 if len(cb.caches) > 1 { 376 if err := cb.caches[0].Append(cb.caches[1:]...); err != nil { 377 panic(errors.Wrap(err, "failed to reset snapshots")) 378 } 379 cb.caches = cb.caches[:1] 380 } 381 keys := make([]kvCacheKey, 0, len(cb.keyTags)) 382 for key, v := range cb.keyTags { 383 v.reset() 384 keys = append(keys, key) 385 } 386 cb.tagKeys = [][]kvCacheKey{keys} 387 } 388 389 func (cb *cachedBatch) CheckFillPercent(ns string) (float64, bool) { 390 return cb.kvStoreBatch.CheckFillPercent(ns) 391 } 392 393 func (cb *cachedBatch) AddFillPercent(ns string, percent float64) { 394 cb.kvStoreBatch.AddFillPercent(ns, percent) 395 } 396 397 func (cb *cachedBatch) hash(namespace string, key []byte) kvCacheKey { 398 return kvCacheKey{namespace, string(key)} 399 }