package cache import ( "fmt" "github.com/cosmos/cosmos-sdk/store/cachekv" "github.com/cosmos/cosmos-sdk/store/types" lru "github.com/hashicorp/golang-lru" ) var ( _ types.CommitKVStore = (*CommitKVStoreCache)(nil) _ types.MultiStorePersistentCache = (*CommitKVStoreCacheManager)(nil) // DefaultCommitKVStoreCacheSize defines the persistent ARC cache size for a // CommitKVStoreCache. DefaultCommitKVStoreCacheSize uint = 1000 ) type ( // CommitKVStoreCache implements an inter-block (persistent) cache that wraps a // CommitKVStore. Reads first hit the internal ARC (Adaptive Replacement Cache). // During a cache miss, the read is delegated to the underlying CommitKVStore // and cached. Deletes and writes always happen to both the cache and the // CommitKVStore in a write-through manner. Caching performed in the // CommitKVStore and below is completely irrelevant to this layer. CommitKVStoreCache struct { types.CommitKVStore cache *lru.ARCCache } // CommitKVStoreCacheManager maintains a mapping from a StoreKey to a // CommitKVStoreCache. Each CommitKVStore, per StoreKey, is meant to be used // in an inter-block (persistent) manner and typically provided by a // CommitMultiStore. CommitKVStoreCacheManager struct { cacheSize uint caches map[string]types.CommitKVStore } ) func NewCommitKVStoreCache(store types.CommitKVStore, size uint) *CommitKVStoreCache { cache, err := lru.NewARC(int(size)) if err != nil { panic(fmt.Errorf("failed to create KVStore cache: %s", err)) } return &CommitKVStoreCache{ CommitKVStore: store, cache: cache, } } func NewCommitKVStoreCacheManager(size uint) *CommitKVStoreCacheManager { return &CommitKVStoreCacheManager{ cacheSize: size, caches: make(map[string]types.CommitKVStore), } } // GetStoreCache returns a Cache from the CommitStoreCacheManager for a given // StoreKey. If no Cache exists for the StoreKey, then one is created and set. // The returned Cache is meant to be used in a persistent manner. func (cmgr *CommitKVStoreCacheManager) GetStoreCache(key types.StoreKey, store types.CommitKVStore) types.CommitKVStore { if cmgr.caches[key.Name()] == nil { cmgr.caches[key.Name()] = NewCommitKVStoreCache(store, cmgr.cacheSize) } return cmgr.caches[key.Name()] } // Unwrap returns the underlying CommitKVStore for a given StoreKey. func (cmgr *CommitKVStoreCacheManager) Unwrap(key types.StoreKey) types.CommitKVStore { if ckv, ok := cmgr.caches[key.Name()]; ok { return ckv.(*CommitKVStoreCache).CommitKVStore } return nil } // Reset resets in the internal caches. func (cmgr *CommitKVStoreCacheManager) Reset() { cmgr.caches = make(map[string]types.CommitKVStore) } // CacheWrap returns the inter-block cache as a cache-wrapped CommitKVStore. func (ckv *CommitKVStoreCache) CacheWrap() types.CacheWrap { return cachekv.NewStore(ckv) } // Get retrieves a value by key. It will first look in the write-through cache. // If the value doesn't exist in the write-through cache, the query is delegated // to the underlying CommitKVStore. func (ckv *CommitKVStoreCache) Get(key []byte) []byte { types.AssertValidKey(key) keyStr := string(key) valueI, ok := ckv.cache.Get(keyStr) if ok { // cache hit return valueI.([]byte) } // cache miss; write to cache value := ckv.CommitKVStore.Get(key) ckv.cache.Add(keyStr, value) return value } // Set inserts a key/value pair into both the write-through cache and the // underlying CommitKVStore. func (ckv *CommitKVStoreCache) Set(key, value []byte) { types.AssertValidKey(key) types.AssertValidValue(value) ckv.cache.Add(string(key), value) ckv.CommitKVStore.Set(key, value) } // Delete removes a key/value pair from both the write-through cache and the // underlying CommitKVStore. func (ckv *CommitKVStoreCache) Delete(key []byte) { ckv.cache.Remove(string(key)) ckv.CommitKVStore.Delete(key) }