github.com/ewagmig/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  }