github.com/iotexproject/iotex-core@v1.14.1-rc1/db/kvstorewithcache.go (about) 1 package db 2 3 import ( 4 "context" 5 "encoding/hex" 6 "sync" 7 8 "github.com/iotexproject/go-pkgs/cache" 9 "github.com/iotexproject/iotex-core/db/batch" 10 "github.com/iotexproject/iotex-core/pkg/log" 11 ) 12 13 // kvStoreWithCache is an implementation of KVStore, wrapping kvstore with LRU caches of latest states 14 type kvStoreWithCache struct { 15 mutex sync.RWMutex // lock for stateCaches 16 store KVStore 17 stateCaches map[string]cache.LRUCache // map having lru cache to store current states for speed up 18 cacheSize int 19 } 20 21 // NewKvStoreWithCache wraps kvstore with stateCaches 22 func NewKvStoreWithCache(kvstore KVStore, cacheSize int) KVStore { 23 return &kvStoreWithCache{ 24 store: kvstore, 25 stateCaches: make(map[string]cache.LRUCache), 26 cacheSize: cacheSize, 27 } 28 } 29 30 // Start starts the kvStoreWithCache 31 func (kvc *kvStoreWithCache) Start(ctx context.Context) error { 32 return kvc.store.Start(ctx) 33 } 34 35 // Stop stops the kvStoreWithCache 36 func (kvc *kvStoreWithCache) Stop(ctx context.Context) error { 37 kvc.mutex.Lock() 38 defer kvc.mutex.Unlock() 39 for _, sc := range kvc.stateCaches { 40 sc.Clear() 41 } 42 return kvc.store.Stop(ctx) 43 } 44 45 // Put inserts a <key, value> record into stateCaches and kvstore 46 func (kvc *kvStoreWithCache) Put(namespace string, key, value []byte) (err error) { 47 if err := kvc.store.Put(namespace, key, value); err != nil { 48 return err 49 } 50 kvc.updateStateCachesIfExists(namespace, key, value) 51 return nil 52 } 53 54 // Get retrieves a <key, value> record from stateCaches, and if not exists, retrieves from kvstore 55 func (kvc *kvStoreWithCache) Get(namespace string, key []byte) ([]byte, error) { 56 if cachedData, isExist := kvc.getStateCaches(namespace, key); isExist { 57 return cachedData, nil 58 } 59 kvStoreData, err := kvc.store.Get(namespace, key) 60 if err != nil { 61 return nil, err 62 } 63 // in case of read-miss, put into statecaches 64 kvc.putStateCaches(namespace, key, kvStoreData) 65 return kvStoreData, nil 66 } 67 68 // Filter returns <k, v> pair in a bucket that meet the condition 69 func (kvc *kvStoreWithCache) Filter(namespace string, cond Condition, minKey, maxKey []byte) ([][]byte, [][]byte, error) { 70 return kvc.store.Filter(namespace, cond, minKey, maxKey) 71 } 72 73 // Delete deletes a record from statecaches if exists, and from kvstore 74 func (kvc *kvStoreWithCache) Delete(namespace string, key []byte) (err error) { 75 if err := kvc.store.Delete(namespace, key); err != nil { 76 return err 77 } 78 kvc.deleteStateCaches(namespace, key) 79 return nil 80 } 81 82 // WriteBatch commits a batch into stateCaches and kvstore 83 func (kvc *kvStoreWithCache) WriteBatch(kvsb batch.KVStoreBatch) (err error) { 84 if err := kvc.store.WriteBatch(kvsb); err != nil { 85 return err 86 } 87 kvsb.Lock() 88 defer kvsb.ClearAndUnlock() 89 for i := 0; i < kvsb.Size(); i++ { 90 write, e := kvsb.Entry(i) 91 if e != nil { 92 return e 93 } 94 ns := write.Namespace() 95 switch write.WriteType() { 96 case batch.Put: 97 kvc.updateStateCachesIfExists(ns, write.Key(), write.Value()) 98 case batch.Delete: 99 kvc.deleteStateCaches(ns, write.Key()) 100 } 101 } 102 return nil 103 } 104 105 // ====================================== 106 // private functions 107 // ====================================== 108 109 // store on stateCaches 110 func (kvc *kvStoreWithCache) putStateCaches(namespace string, key, value []byte) { 111 kvc.mutex.Lock() 112 defer kvc.mutex.Unlock() 113 if sc, ok := kvc.stateCaches[namespace]; ok { 114 sc.Add(hex.EncodeToString(key), value) 115 } else { 116 sc := cache.NewThreadSafeLruCache(kvc.cacheSize) 117 sc.Add(hex.EncodeToString(key), value) 118 kvc.stateCaches[namespace] = sc 119 } 120 } 121 122 // update on stateCaches if the key exists 123 func (kvc *kvStoreWithCache) updateStateCachesIfExists(namespace string, key, value []byte) { 124 kvc.mutex.Lock() 125 defer kvc.mutex.Unlock() 126 if sc, ok := kvc.stateCaches[namespace]; ok { 127 if _, ok := sc.Get(hex.EncodeToString(key)); ok { 128 sc.Add(hex.EncodeToString(key), value) 129 } 130 } 131 } 132 133 // remove on stateCaches if the key exists 134 func (kvc *kvStoreWithCache) deleteStateCaches(namespace string, key []byte) { 135 kvc.mutex.Lock() 136 defer kvc.mutex.Unlock() 137 if sc, ok := kvc.stateCaches[namespace]; ok { 138 sc.Remove(hex.EncodeToString(key)) 139 } 140 } 141 142 // look up stateCachces 143 func (kvc *kvStoreWithCache) getStateCaches(namespace string, key []byte) ([]byte, bool) { 144 kvc.mutex.RLock() 145 defer kvc.mutex.RUnlock() 146 if sc, ok := kvc.stateCaches[namespace]; ok { 147 if data, ok := sc.Get(hex.EncodeToString(key)); ok { 148 if byteData, ok := data.([]byte); ok { 149 return byteData, true 150 } 151 log.L().Fatal("failed to convert interface{} to bytes from stateCaches") 152 return nil, true 153 } 154 } 155 return nil, false 156 }