github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/ledger/kvledger/txmgmt/statedb/statecouchdb/cache.go (about)

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