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  }