github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/core/ledger/kvledger/txmgmt/statedb/statecouchdb/cache.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package statecouchdb 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 68 if !cache.Has(cacheKey) { 69 return nil, nil 70 } 71 cacheValue := &CacheValue{} 72 valBytes := cache.Get(nil, cacheKey) 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 89 if err != nil { 90 return err 91 } 92 93 if cache.Has(cacheKey) { 94 cache.Del(cacheKey) 95 } 96 97 cache.Set(cacheKey, valBytes) 98 return nil 99 } 100 101 // CacheUpdates is a map from a namespace to a set of cache KV updates 102 type cacheUpdates map[string]cacheKVs 103 104 // CacheKVs is a map from a key to a cache value 105 type cacheKVs map[string]*CacheValue 106 107 // Add adds the given cacheKVs to the CacheUpdates 108 func (u cacheUpdates) add(namespace string, ckvs cacheKVs) { 109 nsu, ok := u[namespace] 110 if !ok { 111 nsu = cacheKVs{} 112 u[namespace] = nsu 113 } 114 115 for k, v := range ckvs { 116 nsu[k] = v 117 } 118 } 119 120 // UpdateStates updates only the existing entries in the cache associated with 121 // the chainID. 122 func (c *cache) UpdateStates(chainID string, updates cacheUpdates) error { 123 for ns, kvs := range updates { 124 cache := c.getCache(ns) 125 if cache == nil { 126 continue 127 } 128 129 for key, newVal := range kvs { 130 cacheKey := constructCacheKey(chainID, ns, key) 131 if newVal == nil { 132 cache.Del(cacheKey) 133 continue 134 } 135 if cache.Has(cacheKey) { 136 newValBytes, err := proto.Marshal(newVal) 137 if err != nil { 138 return err 139 } 140 cache.Del(cacheKey) 141 cache.Set(cacheKey, newValBytes) 142 } 143 } 144 } 145 return nil 146 } 147 148 // Reset removes all the items from the cache. 149 func (c *cache) Reset() { 150 c.sysCache.Reset() 151 if c.usrCache != nil { 152 c.usrCache.Reset() 153 } 154 } 155 156 func (c *cache) getCache(namespace string) *fastcache.Cache { 157 for _, ns := range c.sysNamespaces { 158 if namespace == ns { 159 return c.sysCache 160 } 161 } 162 return c.usrCache 163 } 164 165 func constructCacheKey(chainID, namespace, key string) []byte { 166 var cacheKey []byte 167 cacheKey = append(cacheKey, []byte(chainID)...) 168 cacheKey = append(cacheKey, keySep...) 169 cacheKey = append(cacheKey, []byte(namespace)...) 170 cacheKey = append(cacheKey, keySep...) 171 return append(cacheKey, []byte(key)...) 172 }