github.com/eris-ltd/erisdb@v0.25.0/storage/kvcache.go (about)

     1  package storage
     2  
     3  import (
     4  	"bytes"
     5  	"sort"
     6  	"sync"
     7  )
     8  
     9  type KVCache struct {
    10  	sync.RWMutex
    11  	cache map[string]valueInfo
    12  	// Store a sortable slice of keys to avoid always hitting
    13  	keys byteSlices
    14  }
    15  
    16  type byteSlices [][]byte
    17  
    18  func (bss byteSlices) Len() int {
    19  	return len(bss)
    20  }
    21  
    22  func (bss byteSlices) Less(i, j int) bool {
    23  	return bytes.Compare(bss[i], bss[j]) == -1
    24  }
    25  
    26  func (bss byteSlices) Swap(i, j int) {
    27  	bss[i], bss[j] = bss[j], bss[i]
    28  }
    29  
    30  type valueInfo struct {
    31  	value   []byte
    32  	deleted bool
    33  }
    34  
    35  // Creates an in-memory cache wrapping a map that stores the provided tombstone value for deleted keys
    36  func NewKVCache() *KVCache {
    37  	return &KVCache{
    38  		cache: make(map[string]valueInfo),
    39  	}
    40  }
    41  
    42  func (kvc *KVCache) Info(key []byte) (value []byte, deleted bool) {
    43  	kvc.RLock()
    44  	defer kvc.RUnlock()
    45  	vi := kvc.cache[string(key)]
    46  	return vi.value, vi.deleted
    47  }
    48  
    49  func (kvc *KVCache) Get(key []byte) []byte {
    50  	kvc.RLock()
    51  	defer kvc.RUnlock()
    52  	return kvc.cache[string(key)].value
    53  }
    54  
    55  func (kvc *KVCache) Has(key []byte) bool {
    56  	kvc.RLock()
    57  	defer kvc.RUnlock()
    58  	vi, ok := kvc.cache[string(key)]
    59  	return ok && !vi.deleted
    60  }
    61  
    62  func (kvc *KVCache) Set(key, value []byte) {
    63  	kvc.Lock()
    64  	defer kvc.Unlock()
    65  	skey := string(key)
    66  	vi, ok := kvc.cache[skey]
    67  	if !ok {
    68  		// first Set/Delete
    69  		kvc.keys = append(kvc.keys, key)
    70  		// This slows down write quite a lot but does give faster repeated iterations
    71  		// kvc.keys = insertKey(kvc.keys, key)
    72  	}
    73  	vi.deleted = false
    74  	vi.value = value
    75  	kvc.cache[skey] = vi
    76  }
    77  
    78  func (kvc *KVCache) Delete(key []byte) {
    79  	kvc.Lock()
    80  	defer kvc.Unlock()
    81  	skey := string(key)
    82  	vi, ok := kvc.cache[skey]
    83  	if !ok {
    84  		// first Set/Delete
    85  		kvc.keys = append(kvc.keys, key)
    86  		// This slows down write quite a lot but does give faster repeated iterations
    87  		// kvc.keys = insertKey(kvc.keys, key)
    88  	}
    89  	vi.deleted = true
    90  	vi.value = nil
    91  	kvc.cache[skey] = vi
    92  }
    93  
    94  func (kvc *KVCache) Iterator(low, high []byte) KVIterator {
    95  	kvc.RLock()
    96  	defer kvc.RUnlock()
    97  	low, high = NormaliseDomain(low, high)
    98  	return kvc.newIterator(low, high, false)
    99  }
   100  
   101  func (kvc *KVCache) ReverseIterator(low, high []byte) KVIterator {
   102  	kvc.RLock()
   103  	defer kvc.RUnlock()
   104  	low, high = NormaliseDomain(low, high)
   105  	return kvc.newIterator(low, high, true)
   106  }
   107  
   108  // Writes contents of cache to backend without flushing the cache
   109  func (kvc *KVCache) WriteTo(writer KVWriter) {
   110  	kvc.Lock()
   111  	defer kvc.Unlock()
   112  	for k, vi := range kvc.cache {
   113  		kb := []byte(k)
   114  		if vi.deleted {
   115  			writer.Delete(kb)
   116  		} else {
   117  			writer.Set(kb, vi.value)
   118  		}
   119  	}
   120  }
   121  
   122  func (kvc *KVCache) Reset() {
   123  	kvc.Lock()
   124  	defer kvc.Unlock()
   125  	kvc.cache = make(map[string]valueInfo)
   126  }
   127  
   128  func (kvc *KVCache) sortedKeysInDomain(low, high []byte) [][]byte {
   129  	// Sort keys (which may be partially sorted if we have iterated before)
   130  	sort.Sort(kvc.keys)
   131  	sortedKeys := kvc.keys
   132  	// Attempt to seek to the first key in the range
   133  	startIndex := len(kvc.keys)
   134  	for i, key := range sortedKeys {
   135  		// !(key < start) => key >= start then include (inclusive start)
   136  		if CompareKeys(key, low) != -1 {
   137  			startIndex = i
   138  			break
   139  		}
   140  	}
   141  	// Reslice to beginning of range or end if not found
   142  	sortedKeys = sortedKeys[startIndex:]
   143  	for i, key := range sortedKeys {
   144  		// !(key < end) => key >= end then exclude (exclusive end)
   145  		if CompareKeys(key, high) != -1 {
   146  			sortedKeys = sortedKeys[:i]
   147  			break
   148  		}
   149  	}
   150  	return sortedKeys
   151  }
   152  
   153  func (kvc *KVCache) newIterator(start, end []byte, reverse bool) *KVCacheIterator {
   154  	keys := kvc.sortedKeysInDomain(start, end)
   155  	kvi := &KVCacheIterator{
   156  		start:   start,
   157  		end:     end,
   158  		keys:    keys,
   159  		cache:   kvc.cache,
   160  		reverse: reverse,
   161  	}
   162  	return kvi
   163  }
   164  
   165  type KVCacheIterator struct {
   166  	cache    map[string]valueInfo
   167  	start    []byte
   168  	end      []byte
   169  	keys     [][]byte
   170  	keyIndex int
   171  	reverse  bool
   172  }
   173  
   174  func (kvi *KVCacheIterator) Domain() ([]byte, []byte) {
   175  	return kvi.start, kvi.end
   176  }
   177  
   178  func (kvi *KVCacheIterator) Info() (key, value []byte, deleted bool) {
   179  	key = kvi.keys[kvi.sliceIndex()]
   180  	vi := kvi.cache[string(key)]
   181  	return key, vi.value, vi.deleted
   182  }
   183  
   184  func (kvi *KVCacheIterator) Key() []byte {
   185  	return []byte(kvi.keys[kvi.sliceIndex()])
   186  }
   187  
   188  func (kvi *KVCacheIterator) Value() []byte {
   189  	return kvi.cache[string(kvi.keys[kvi.sliceIndex()])].value
   190  }
   191  
   192  func (kvi *KVCacheIterator) Next() {
   193  	if !kvi.Valid() {
   194  		panic("KVCacheIterator.Next() called on invalid iterator")
   195  	}
   196  	kvi.keyIndex++
   197  }
   198  
   199  func (kvi *KVCacheIterator) Valid() bool {
   200  	return kvi.keyIndex < len(kvi.keys)
   201  }
   202  
   203  func (kvi *KVCacheIterator) Close() {}
   204  
   205  func (kvi *KVCacheIterator) sliceIndex() int {
   206  	if kvi.reverse {
   207  		//reflect
   208  		return len(kvi.keys) - 1 - kvi.keyIndex
   209  	}
   210  	return kvi.keyIndex
   211  }
   212  
   213  func insertKey(sortedKeys [][]byte, key []byte) [][]byte {
   214  	i := sort.Search(len(sortedKeys), func(i int) bool {
   215  		// Smallest sortedKey such that key
   216  		return bytes.Compare(sortedKeys[i], key) > -1
   217  	})
   218  	// ensure space
   219  	sortedKeys = append(sortedKeys, nil)
   220  	copy(sortedKeys[i+1:], sortedKeys[i:])
   221  	sortedKeys[i] = key
   222  	return sortedKeys
   223  }