github.com/true-sqn/fabric@v2.1.1+incompatible/core/ledger/kvledger/txmgmt/statedb/cache.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package statedb 8 9 import ( 10 "github.com/VictoriaMetrics/fastcache" 11 "github.com/golang/protobuf/proto" 12 ) 13 14 var ( 15 keySep = []byte{0x00} 16 ) 17 18 // Cache holds both the system and user cache 19 type Cache struct { 20 sysCache *fastcache.Cache 21 usrCache *fastcache.Cache 22 sysNamespaces []string 23 } 24 25 // NewCache creates a Cache. The cache consists of both system state cache (for lscc, _lifecycle) 26 // and user state cache (for all user deployed chaincodes). The size of the 27 // system state cache is 64 MB, by default. The size of the user state cache, in terms of MB, is 28 // specified via usrCacheSize parameter. Note that the maximum memory consumption of fastcache 29 // would be in the multiples of 32 MB (due to 512 buckets & an equal number of 64 KB chunks per bucket). 30 // If the usrCacheSizeMBs is not a multiple of 32 MB, the fastcache would round the size 31 // to the next multiple of 32 MB. 32 func NewCache(usrCacheSizeMBs int, sysNamespaces []string) *Cache { 33 cache := &Cache{} 34 // By default, 64 MB is allocated for the system cache 35 cache.sysCache = fastcache.New(64 * 1024 * 1024) 36 cache.sysNamespaces = sysNamespaces 37 38 // User passed size is used to allocate memory for the user cache 39 if usrCacheSizeMBs <= 0 { 40 return cache 41 } 42 cache.usrCache = fastcache.New(usrCacheSizeMBs * 1024 * 1024) 43 return cache 44 } 45 46 // Enabled returns true if the cache is enabled for a given namespace. 47 // Namespace can be of two types: system namespace (such as lscc) and user 48 // namespace (all user's chaincode states). 49 func (c *Cache) Enabled(namespace string) bool { 50 for _, ns := range c.sysNamespaces { 51 if namespace == ns { 52 return true 53 } 54 } 55 return c.usrCache != nil 56 } 57 58 // GetState returns the value for a given namespace and key from 59 // a cache associated with the chainID. 60 func (c *Cache) GetState(chainID, namespace, key string) (*CacheValue, error) { 61 cache := c.getCache(namespace) 62 if cache == nil { 63 return nil, nil 64 } 65 66 cacheKey := constructCacheKey(chainID, namespace, key) 67 valBytes := cache.Get(nil, cacheKey) 68 if valBytes == nil { 69 return nil, nil 70 } 71 72 cacheValue := &CacheValue{} 73 if err := proto.Unmarshal(valBytes, cacheValue); err != nil { 74 return nil, err 75 } 76 return cacheValue, nil 77 } 78 79 // PutState stores a given value in a cache associated with the chainID. 80 func (c *Cache) PutState(chainID, namespace, key string, cacheValue *CacheValue) error { 81 cache := c.getCache(namespace) 82 if cache == nil { 83 return nil 84 } 85 86 cacheKey := constructCacheKey(chainID, namespace, key) 87 valBytes, err := proto.Marshal(cacheValue) 88 if err != nil { 89 return err 90 } 91 cache.Set(cacheKey, valBytes) 92 return nil 93 } 94 95 // CacheUpdates is a map from a namespace to a set of cache KV updates 96 type CacheUpdates map[string]CacheKVs 97 98 // CacheKVs is a map from a key to a cache value 99 type CacheKVs map[string]*CacheValue 100 101 // Add adds the given cacheKVs to the CacheUpdates 102 func (u CacheUpdates) Add(namespace string, ckvs CacheKVs) { 103 nsu, ok := u[namespace] 104 if !ok { 105 nsu = CacheKVs{} 106 u[namespace] = nsu 107 } 108 109 for k, v := range ckvs { 110 nsu[k] = v 111 } 112 } 113 114 // UpdateStates updates only the existing entries in the cache associated with 115 // the chainID. 116 func (c *Cache) UpdateStates(chainID string, updates CacheUpdates) error { 117 for ns, kvs := range updates { 118 cache := c.getCache(ns) 119 if cache == nil { 120 continue 121 } 122 123 for key, newVal := range kvs { 124 cacheKey := constructCacheKey(chainID, ns, key) 125 if newVal == nil { 126 cache.Del(cacheKey) 127 continue 128 } 129 if oldVal := cache.Get(nil, cacheKey); oldVal != nil { 130 newValBytes, err := proto.Marshal(newVal) 131 if err != nil { 132 return err 133 } 134 cache.Set(cacheKey, newValBytes) 135 } 136 } 137 } 138 return nil 139 } 140 141 // Reset removes all the items from the cache. 142 func (c *Cache) Reset() { 143 c.sysCache.Reset() 144 if c.usrCache != nil { 145 c.usrCache.Reset() 146 } 147 } 148 149 func (c *Cache) getCache(namespace string) *fastcache.Cache { 150 for _, ns := range c.sysNamespaces { 151 if namespace == ns { 152 return c.sysCache 153 } 154 } 155 return c.usrCache 156 } 157 158 func constructCacheKey(chainID, namespace, key string) []byte { 159 var cacheKey []byte 160 cacheKey = append(cacheKey, []byte(chainID)...) 161 cacheKey = append(cacheKey, keySep...) 162 cacheKey = append(cacheKey, []byte(namespace)...) 163 cacheKey = append(cacheKey, keySep...) 164 return append(cacheKey, []byte(key)...) 165 }