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  }