diff --git a/store/cache_kvstore.go b/store/cache_kvstore.go index 105f4fc..755ecdb 100644 --- a/store/cache_kvstore.go +++ b/store/cache_kvstore.go @@ -3,18 +3,21 @@ package store import ( "context" + corestoretypes "cosmossdk.io/core/store" "cosmossdk.io/errors" cachekv "cosmossdk.io/store/cachekv" - "cosmossdk.io/store/types" + storetypes "cosmossdk.io/store/types" bigcache "github.com/allegro/bigcache/v3" ) +var _ corestoretypes.KVStore = (*CacheStore)(nil) + type CacheStore struct { - store types.CacheKVStore + store storetypes.CacheKVStore cache *bigcache.BigCache } -func NewCacheStore(store types.KVStore, capacity int) *CacheStore { +func NewCacheStore(store storetypes.KVStore, capacity int) *CacheStore { // default with no eviction and custom hard max cache capacity cacheCfg := bigcache.DefaultConfig(0) cacheCfg.Verbose = false @@ -31,33 +34,48 @@ func NewCacheStore(store types.KVStore, capacity int) *CacheStore { } } +// Get returns nil iff key doesn't exist. Errors on nil key. func (c CacheStore) Get(key []byte) ([]byte, error) { - types.AssertValidKey(key) + storetypes.AssertValidKey(key) - v, err := c.cache.Get(string(key)) - // cache hit - if err == nil { - return v, nil + if value, err := c.cache.Get(string(key)); err == nil { + return value, nil } // get from store and write to cache value := c.store.Get(key) - err = c.cache.Set(string(key), value) - if err != nil { - return nil, errors.Wrap(err, "failed to set cache") + if value == nil { + return nil, nil } + // ignore cache error + _ = c.cache.Set(string(key), value) + return value, nil } +// Has checks if a key exists. Errors on nil key. func (c CacheStore) Has(key []byte) (bool, error) { _, err := c.cache.Get(string(key)) - return err == nil, err + if err == nil { + return true, nil + } + + value := c.store.Get(key) + if value == nil { + return false, nil + } + + // ignore cache error + _ = c.cache.Set(string(key), value) + + return true, nil } +// Set sets the key. Errors on nil key or value. func (c CacheStore) Set(key, value []byte) error { - types.AssertValidKey(key) - types.AssertValidValue(value) + storetypes.AssertValidKey(key) + storetypes.AssertValidValue(value) err := c.cache.Set(string(key), value) if err != nil { @@ -68,20 +86,35 @@ func (c CacheStore) Set(key, value []byte) error { return nil } +// Delete deletes the key. Errors on nil key. func (c CacheStore) Delete(key []byte) error { - types.AssertValidKey(key) + storetypes.AssertValidKey(key) - _ = c.cache.Delete(string(key)) + err := c.cache.Delete(string(key)) + if err != nil && errors.IsOf(err, bigcache.ErrEntryNotFound) { + return errors.Wrap(err, "failed to delete cache") + } c.store.Delete(key) return nil } -func (c CacheStore) Iterator(start, end []byte) (types.Iterator, error) { +// Iterator iterates over a domain of keys in ascending order. End is exclusive. +// Start must be less than end, or the Iterator is invalid. +// Iterator must be closed by caller. +// To iterate over entire domain, use store.Iterator(nil, nil) +// CONTRACT: No writes may happen within a domain while an iterator exists over it. +// Exceptionally allowed for cachekv.Store, safe to write in the modules. +func (c CacheStore) Iterator(start, end []byte) (storetypes.Iterator, error) { return c.store.Iterator(start, end), nil } -func (c CacheStore) ReverseIterator(start, end []byte) (types.Iterator, error) { +// ReverseIterator iterates over a domain of keys in descending order. End is exclusive. +// Start must be less than end, or the Iterator is invalid. +// Iterator must be closed by caller. +// CONTRACT: No writes may happen within a domain while an iterator exists over it. +// Exceptionally allowed for cachekv.Store, safe to write in the modules. +func (c CacheStore) ReverseIterator(start, end []byte) (storetypes.Iterator, error) { return c.store.ReverseIterator(start, end), nil }