github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/cosmos-sdk/store/cachekv/store.go (about) 1 package cachekv 2 3 import ( 4 "bytes" 5 "io" 6 "reflect" 7 "sort" 8 "sync" 9 "unsafe" 10 11 "github.com/fibonacci-chain/fbc/libs/iavl" 12 "github.com/fibonacci-chain/fbc/libs/system/trace" 13 dbm "github.com/fibonacci-chain/fbc/libs/tm-db" 14 "github.com/tendermint/go-amino" 15 16 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/store/tracekv" 17 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/store/types" 18 kv "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/kv" 19 ) 20 21 // If value is nil but deleted is false, it means the parent doesn't have the 22 // key. (No need to delete upon Write()) 23 type cValue struct { 24 value []byte 25 deleted bool 26 } 27 28 type PreChangesHandler func(keys []string, setOrDel []byte) 29 30 // Store wraps an in-memory cache around an underlying types.KVStore. 31 type Store struct { 32 mtx sync.Mutex 33 dirty map[string]cValue 34 readList map[string][]byte 35 unsortedCache map[string]struct{} 36 sortedCache *kv.List // always ascending sorted 37 parent types.KVStore 38 39 preChangesHandler PreChangesHandler 40 disableCacheReadList bool // not cache readList for group-paralleled-tx 41 trace.StatisticsCell 42 } 43 44 var _ types.CacheKVStore = (*Store)(nil) 45 46 func NewStore(parent types.KVStore) *Store { 47 return &Store{ 48 dirty: make(map[string]cValue), 49 readList: make(map[string][]byte), 50 unsortedCache: make(map[string]struct{}), 51 sortedCache: kv.NewList(), 52 parent: parent, 53 } 54 } 55 56 func NewStoreWithPreChangeHandler(parent types.KVStore, preChangesHandler PreChangesHandler) *Store { 57 s := NewStore(parent) 58 s.preChangesHandler = preChangesHandler 59 return s 60 } 61 62 // Implements Store. 63 func (store *Store) GetStoreType() types.StoreType { 64 return store.parent.GetStoreType() 65 } 66 67 // Implements types.KVStore. 68 func (store *Store) Get(key []byte) (value []byte) { 69 store.mtx.Lock() 70 defer store.mtx.Unlock() 71 72 types.AssertValidKey(key) 73 74 cacheValue, ok := store.dirty[string(key)] 75 if !ok { 76 if c, ok := store.readList[string(key)]; ok { 77 value = c 78 } else { 79 value = store.parent.Get(key) 80 if !store.disableCacheReadList { 81 store.setCacheValue(key, value, false, false) 82 } 83 } 84 } else { 85 value = cacheValue.value 86 } 87 88 return value 89 } 90 91 func (store *Store) GetRWSet(mp types.MsRWSet) { 92 panic("implement me") 93 } 94 95 func (store *Store) IteratorCache(isdirty bool, cb func(key string, value []byte, isDirty bool, isDelete bool, sKey types.StoreKey) bool, sKey types.StoreKey) bool { 96 if cb == nil { 97 return true 98 } 99 store.mtx.Lock() 100 defer store.mtx.Unlock() 101 102 if isdirty { 103 for key, v := range store.dirty { 104 if !cb(key, v.value, true, v.deleted, sKey) { 105 return false 106 } 107 } 108 } else { 109 for key, v := range store.readList { 110 if !cb(key, v, false, false, sKey) { 111 return false 112 } 113 } 114 } 115 116 return true 117 } 118 119 // Implements types.KVStore. 120 func (store *Store) Set(key []byte, value []byte) { 121 store.mtx.Lock() 122 defer store.mtx.Unlock() 123 124 types.AssertValidKey(key) 125 types.AssertValidValue(value) 126 127 store.setCacheValue(key, value, false, true) 128 } 129 130 // Implements types.KVStore. 131 func (store *Store) Has(key []byte) bool { 132 value := store.Get(key) 133 return value != nil 134 } 135 136 // Implements types.KVStore. 137 func (store *Store) Delete(key []byte) { 138 store.mtx.Lock() 139 defer store.mtx.Unlock() 140 141 types.AssertValidKey(key) 142 143 store.setCacheValue(key, nil, true, true) 144 } 145 146 // Implements Cachetypes.KVStore. 147 func (store *Store) Write() { 148 // if parent is cachekv.Store, we can write kv more efficiently 149 if pStore, ok := store.parent.(*Store); ok { 150 store.writeToCacheKv(pStore) 151 return 152 } 153 154 store.mtx.Lock() 155 defer store.mtx.Unlock() 156 157 // We need a copy of all of the keys. 158 // Not the best, but probably not a bottleneck depending. 159 keys := make([]string, len(store.dirty)) 160 index := 0 161 for key, _ := range store.dirty { 162 keys[index] = key 163 index++ 164 165 } 166 167 sort.Strings(keys) 168 store.preWrite(keys) 169 170 store.StartTiming() 171 172 // TODO: Consider allowing usage of Batch, which would allow the write to 173 // at least happen atomically. 174 for _, key := range keys { 175 cacheValue := store.dirty[key] 176 switch { 177 case cacheValue.deleted: 178 store.parent.Delete([]byte(key)) 179 case cacheValue.value == nil: 180 // Skip, it already doesn't exist in parent. 181 default: 182 store.parent.Set([]byte(key), cacheValue.value) 183 } 184 } 185 186 // Clear the cache 187 store.clearCache() 188 store.EndTiming(trace.FlushCache) 189 } 190 191 func (store *Store) preWrite(keys []string) { 192 if store.preChangesHandler == nil || len(keys) < 4 { 193 return 194 } 195 196 setOrDel := make([]byte, 0, len(keys)) 197 198 for _, key := range keys { 199 cacheValue := store.dirty[key] 200 switch { 201 case cacheValue.deleted: 202 setOrDel = append(setOrDel, iavl.PreChangeOpDelete) 203 case cacheValue.value == nil: 204 // Skip, it already doesn't exist in parent. 205 setOrDel = append(setOrDel, iavl.PreChangeNop) 206 default: 207 setOrDel = append(setOrDel, iavl.PreChangeOpSet) 208 } 209 } 210 211 store.preChangesHandler(keys, setOrDel) 212 } 213 214 // writeToCacheKv will write cached kv to the parent Store, then clear the cache. 215 func (store *Store) writeToCacheKv(parent *Store) { 216 store.mtx.Lock() 217 defer store.mtx.Unlock() 218 219 // TODO: Consider allowing usage of Batch, which would allow the write to 220 // at least happen atomically. 221 for key, cacheValue := range store.dirty { 222 switch { 223 case cacheValue.deleted: 224 parent.Delete(amino.StrToBytes(key)) 225 case cacheValue.value == nil: 226 // Skip, it already doesn't exist in parent. 227 default: 228 parent.Set(amino.StrToBytes(key), cacheValue.value) 229 } 230 } 231 232 // Clear the cache 233 store.clearCache() 234 } 235 236 func (store *Store) clearCache() { 237 // https://github.com/golang/go/issues/20138 238 for key := range store.dirty { 239 delete(store.dirty, key) 240 } 241 242 for Key := range store.readList { 243 delete(store.readList, Key) 244 } 245 for key := range store.unsortedCache { 246 delete(store.unsortedCache, key) 247 } 248 store.disableCacheReadList = false 249 store.sortedCache.Init() 250 } 251 252 //---------------------------------------- 253 // To cache-wrap this Store further. 254 255 // Implements CacheWrapper. 256 func (store *Store) CacheWrap() types.CacheWrap { 257 return NewStore(store) 258 } 259 260 // CacheWrapWithTrace implements the CacheWrapper interface. 261 func (store *Store) CacheWrapWithTrace(w io.Writer, tc types.TraceContext) types.CacheWrap { 262 return NewStore(tracekv.NewStore(store, w, tc)) 263 } 264 265 //---------------------------------------- 266 // Iteration 267 268 // Implements types.KVStore. 269 func (store *Store) Iterator(start, end []byte) types.Iterator { 270 return store.iterator(start, end, true) 271 } 272 273 // Implements types.KVStore. 274 func (store *Store) ReverseIterator(start, end []byte) types.Iterator { 275 return store.iterator(start, end, false) 276 } 277 278 func (store *Store) iterator(start, end []byte, ascending bool) types.Iterator { 279 store.mtx.Lock() 280 defer store.mtx.Unlock() 281 282 var parent, cache types.Iterator 283 284 if ascending { 285 parent = store.parent.Iterator(start, end) 286 } else { 287 parent = store.parent.ReverseIterator(start, end) 288 } 289 290 store.dirtyItems(start, end) 291 cache = newMemIterator(start, end, store.sortedCache, ascending) 292 293 return newCacheMergeIterator(parent, cache, ascending) 294 } 295 296 // strToByte is meant to make a zero allocation conversion 297 // from string -> []byte to speed up operations, it is not meant 298 // to be used generally, but for a specific pattern to check for available 299 // keys within a domain. 300 func strToByte(s string) []byte { 301 var b []byte 302 hdr := (*reflect.SliceHeader)(unsafe.Pointer(&b)) 303 hdr.Cap = len(s) 304 hdr.Len = len(s) 305 hdr.Data = (*reflect.StringHeader)(unsafe.Pointer(&s)).Data 306 return b 307 } 308 309 // byteSliceToStr is meant to make a zero allocation conversion 310 // from []byte -> string to speed up operations, it is not meant 311 // to be used generally, but for a specific pattern to delete keys 312 // from a map. 313 func byteSliceToStr(b []byte) string { 314 hdr := (*reflect.StringHeader)(unsafe.Pointer(&b)) 315 return *(*string)(unsafe.Pointer(hdr)) 316 } 317 318 // Constructs a slice of dirty items, to use w/ memIterator. 319 func (store *Store) dirtyItems(start, end []byte) { 320 unsorted := make([]*kv.Pair, 0) 321 322 n := len(store.unsortedCache) 323 for key := range store.unsortedCache { 324 if dbm.IsKeyInDomain(strToByte(key), start, end) { 325 cacheValue := store.dirty[key] 326 unsorted = append(unsorted, &kv.Pair{Key: []byte(key), Value: cacheValue.value}) 327 } 328 } 329 330 if len(unsorted) == n { // This pattern allows the Go compiler to emit the map clearing idiom for the entire map. 331 for key := range store.unsortedCache { 332 delete(store.unsortedCache, key) 333 } 334 } else { // Otherwise, normally delete the unsorted keys from the map. 335 for _, kv := range unsorted { 336 delete(store.unsortedCache, byteSliceToStr(kv.Key)) 337 } 338 } 339 340 sort.Slice(unsorted, func(i, j int) bool { 341 return bytes.Compare(unsorted[i].Key, unsorted[j].Key) < 0 342 }) 343 344 for e := store.sortedCache.Front(); e != nil && len(unsorted) != 0; { 345 uitem := unsorted[0] 346 sitem := e.Value 347 comp := bytes.Compare(uitem.Key, sitem.Key) 348 switch comp { 349 case -1: 350 unsorted = unsorted[1:] 351 store.sortedCache.InsertBefore(uitem, e) 352 case 1: 353 e = e.Next() 354 case 0: 355 unsorted = unsorted[1:] 356 e.Value = uitem 357 e = e.Next() 358 } 359 } 360 361 for _, kvp := range unsorted { 362 store.sortedCache.PushBack(kvp) 363 } 364 365 } 366 367 //---------------------------------------- 368 // etc 369 370 // Only entrypoint to mutate store.cache. 371 func (store *Store) setCacheValue(key, value []byte, deleted bool, dirty bool) { 372 keyStr := string(key) 373 if !dirty { 374 store.readList[keyStr] = value 375 return 376 } 377 378 store.dirty[keyStr] = cValue{ 379 value: value, 380 deleted: deleted, 381 } 382 if dirty { 383 store.unsortedCache[keyStr] = struct{}{} 384 } 385 } 386 387 // Reset will clear all internal data without writing to the parent and set the new parent. 388 func (store *Store) Reset(parent types.KVStore) { 389 store.mtx.Lock() 390 391 store.preChangesHandler = nil 392 store.parent = parent 393 store.StatisticsCell = nil 394 store.clearCache() 395 396 store.mtx.Unlock() 397 } 398 399 // Clear will clear all internal data without writing to the parent. 400 func (store *Store) Clear() { 401 store.mtx.Lock() 402 store.clearCache() 403 store.mtx.Unlock() 404 } 405 406 func (store *Store) DisableCacheReadList() { 407 store.mtx.Lock() 408 store.disableCacheReadList = true 409 store.mtx.Unlock() 410 } 411 412 func (store *Store) StartTiming() { 413 if store.StatisticsCell != nil { 414 store.StatisticsCell.StartTiming() 415 } 416 } 417 418 func (store *Store) EndTiming(tag string) { 419 if store.StatisticsCell != nil { 420 store.StatisticsCell.EndTiming(tag) 421 } 422 } 423 424 func (store *Store) CopyRWSet(rw types.CacheKVRWSet) { 425 store.mtx.Lock() 426 defer store.mtx.Unlock() 427 428 for k, v := range store.readList { 429 rw.Read[k] = v 430 } 431 432 for k, v := range store.dirty { 433 rw.Write[k] = types.DirtyValue{ 434 Deleted: v.deleted, 435 Value: v.value, 436 } 437 } 438 }