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