Skip to content

Commit e02053c

Browse files
author
Srinivasan Muralidharan
committed
[FAB-4473] mod cc cache strategy to store minimal data
The chaincode cache implementation stores the entire CCPackage where only the InstantiationPolicy (IP) is needed. In this CR we cache ChaincodeData extracted from CCPackage as that contains IP and is much smaller than a typical package. . removed TestCCInfoCachePeerInstance but left a bit of coverage into another UT . fixed a bug introduced by rebase and add a couple of logs statements Change-Id: Ic1fa64c7a50d3ea3247adb63be5b89f0b2af60cd Signed-off-by: Srinivasan Muralidharan <[email protected]>
1 parent 076fb52 commit e02053c

File tree

3 files changed

+59
-161
lines changed

3 files changed

+59
-161
lines changed

core/common/ccprovider/ccinfocache.go

+16-52
Original file line numberDiff line numberDiff line change
@@ -19,88 +19,52 @@ package ccprovider
1919
import (
2020
"fmt"
2121
"sync"
22-
23-
"github.com/hyperledger/fabric/protos/peer"
2422
)
2523

26-
// ccInfoCacheImpl implements CCInfoProvider by providing an in-memory
27-
// cache layer on top of the internal CCInfoProvider instance
24+
// ccInfoCacheImpl implements in-memory cache for ChaincodeData
25+
// needed by endorser to verify if the local instantiation policy
26+
// matches the instantiation policy on a channel before honoring
27+
// an invoke
2828
type ccInfoCacheImpl struct {
2929
sync.RWMutex
3030

31-
cache map[string]CCPackage
32-
ccfs CCInfoProvider
31+
cache map[string]*ChaincodeData
32+
cacheSupport CCCacheSupport
3333
}
3434

3535
// NewCCInfoCache returns a new cache on top of the supplied CCInfoProvider instance
36-
func NewCCInfoCache(ccfs CCInfoProvider) CCInfoProvider {
36+
func NewCCInfoCache(cs CCCacheSupport) *ccInfoCacheImpl {
3737
return &ccInfoCacheImpl{
38-
cache: make(map[string]CCPackage),
39-
ccfs: ccfs,
38+
cache: make(map[string]*ChaincodeData),
39+
cacheSupport: cs,
4040
}
4141
}
4242

43-
func (c *ccInfoCacheImpl) GetChaincode(ccname string, ccversion string) (CCPackage, error) {
43+
func (c *ccInfoCacheImpl) GetChaincodeData(ccname string, ccversion string) (*ChaincodeData, error) {
4444
// c.cache is guaranteed to be non-nil
4545

4646
key := ccname + "/" + ccversion
4747

4848
c.RLock()
49-
ccpack, in := c.cache[key]
49+
ccdata, in := c.cache[key]
5050
c.RUnlock()
5151

5252
if !in {
5353
var err error
5454

5555
// the chaincode data is not in the cache
5656
// try to look it up from the file system
57-
ccpack, err = c.ccfs.GetChaincode(ccname, ccversion)
57+
ccpack, err := c.cacheSupport.GetChaincode(ccname, ccversion)
5858
if err != nil || ccpack == nil {
5959
return nil, fmt.Errorf("cannot retrieve package for chaincode %s/%s, error %s", ccname, ccversion, err)
6060
}
6161

62-
// we have a non-nil CCPackage, put it in the cache
62+
// we have a non-nil ChaincodeData, put it in the cache
6363
c.Lock()
64-
c.cache[key] = ccpack
64+
ccdata = ccpack.GetChaincodeData()
65+
c.cache[key] = ccdata
6566
c.Unlock()
6667
}
6768

68-
return ccpack, nil
69-
}
70-
71-
func (c *ccInfoCacheImpl) PutChaincode(depSpec *peer.ChaincodeDeploymentSpec) (CCPackage, error) {
72-
// c.cache is guaranteed to be non-nil
73-
74-
ccname := depSpec.ChaincodeSpec.ChaincodeId.Name
75-
ccversion := depSpec.ChaincodeSpec.ChaincodeId.Version
76-
77-
if ccname == "" {
78-
return nil, fmt.Errorf("the chaincode name cannot be an empty string")
79-
}
80-
81-
if ccversion == "" {
82-
return nil, fmt.Errorf("the chaincode version cannot be an empty string")
83-
}
84-
85-
key := ccname + "/" + ccversion
86-
87-
c.RLock()
88-
_, in := c.cache[key]
89-
c.RUnlock()
90-
91-
if in {
92-
return nil, fmt.Errorf("attempted to put chaincode data twice for %s/%s", ccname, ccversion)
93-
}
94-
95-
ccpack, err := c.ccfs.PutChaincode(depSpec)
96-
if err != nil || ccpack == nil {
97-
return nil, fmt.Errorf("PutChaincodeIntoFS failed, error %s", err)
98-
}
99-
100-
// we have a non-nil CCPackage, put it in the cache
101-
c.Lock()
102-
c.cache[key] = ccpack
103-
c.Unlock()
104-
105-
return ccpack, nil
69+
return ccdata, nil
10670
}

core/common/ccprovider/ccinfocache_test.go

+20-78
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import (
2020
"archive/tar"
2121
"bytes"
2222
"compress/gzip"
23-
"errors"
2423
"io/ioutil"
2524
"path/filepath"
2625
"testing"
@@ -70,27 +69,7 @@ func buildPackage(name string, path string, version string, initArgs [][]byte) (
7069
}
7170

7271
type mockCCInfoFSStorageMgrImpl struct {
73-
CCMap map[string]CCPackage
74-
putchaincodeErr bool
75-
}
76-
77-
func (m *mockCCInfoFSStorageMgrImpl) PutChaincode(depSpec *peer.ChaincodeDeploymentSpec) (CCPackage, error) {
78-
if m.putchaincodeErr {
79-
return nil, errors.New("Error putting the chaincode")
80-
}
81-
82-
buf, err := proto.Marshal(depSpec)
83-
if err != nil {
84-
return nil, err
85-
}
86-
cccdspack := &CDSPackage{}
87-
if _, err := cccdspack.InitFromBuffer(buf); err != nil {
88-
return nil, err
89-
}
90-
91-
m.CCMap[depSpec.ChaincodeSpec.ChaincodeId.Name+depSpec.ChaincodeSpec.ChaincodeId.Version] = cccdspack
92-
93-
return cccdspack, nil
72+
CCMap map[string]CCPackage
9473
}
9574

9675
func (m *mockCCInfoFSStorageMgrImpl) GetChaincode(ccname string, ccversion string) (CCPackage, error) {
@@ -109,7 +88,7 @@ func TestCCInfoCache(t *testing.T) {
10988
// test the get side
11089

11190
// the cc data is not yet in the cache
112-
_, err := cccache.GetChaincode(ccname, ccver)
91+
_, err := cccache.GetChaincodeData(ccname, ccver)
11392
assert.Error(t, err)
11493

11594
// put it in the file system
@@ -118,11 +97,11 @@ func TestCCInfoCache(t *testing.T) {
11897
ccinfoFs.CCMap[ccname+ccver] = pack
11998

12099
// expect it to be in the cache now
121-
cd1, err := cccache.GetChaincode(ccname, ccver)
100+
cd1, err := cccache.GetChaincodeData(ccname, ccver)
122101
assert.NoError(t, err)
123102

124103
// it should still be in the cache
125-
cd2, err := cccache.GetChaincode(ccname, ccver)
104+
cd2, err := cccache.GetChaincodeData(ccname, ccver)
126105
assert.NoError(t, err)
127106

128107
// they are not null
@@ -131,21 +110,21 @@ func TestCCInfoCache(t *testing.T) {
131110

132111
// test the put side now..
133112
ccver = "2.0"
134-
135-
// create a dep spec to put
136-
ds, err := getDepSpec(ccname, ccpath, ccver, [][]byte{[]byte("init"), []byte("a"), []byte("100"), []byte("b"), []byte("200")})
113+
// put it in the file system
114+
pack, err = buildPackage(ccname, ccpath, ccver, [][]byte{[]byte("init"), []byte("a"), []byte("100"), []byte("b"), []byte("200")})
137115
assert.NoError(t, err)
116+
ccinfoFs.CCMap[ccname+ccver] = pack
138117

139-
// put it
140-
_, err = cccache.PutChaincode(ds)
118+
// create a dep spec to put
119+
_, err = getDepSpec(ccname, ccpath, ccver, [][]byte{[]byte("init"), []byte("a"), []byte("100"), []byte("b"), []byte("200")})
141120
assert.NoError(t, err)
142121

143-
// expect it to be in the cache
144-
cd1, err = cccache.GetChaincode(ccname, ccver)
122+
// expect it to be cached
123+
cd1, err = cccache.GetChaincodeData(ccname, ccver)
145124
assert.NoError(t, err)
146125

147126
// it should still be in the cache
148-
cd2, err = cccache.GetChaincode(ccname, ccver)
127+
cd2, err = cccache.GetChaincodeData(ccname, ccver)
149128
assert.NoError(t, err)
150129

151130
// they are not null
@@ -159,36 +138,27 @@ func TestPutChaincode(t *testing.T) {
159138
ccpath := "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02"
160139

161140
ccinfoFs := &mockCCInfoFSStorageMgrImpl{CCMap: map[string]CCPackage{}}
162-
cccache := NewCCInfoCache(ccinfoFs)
141+
NewCCInfoCache(ccinfoFs)
163142

164143
// Error case 1: ccname is empty
165144
// create a dep spec to put
166-
ds, err := getDepSpec(ccname, ccpath, ccver, [][]byte{[]byte("init"), []byte("a"), []byte("100"), []byte("b"), []byte("200")})
145+
_, err := getDepSpec(ccname, ccpath, ccver, [][]byte{[]byte("init"), []byte("a"), []byte("100"), []byte("b"), []byte("200")})
167146
assert.NoError(t, err)
168-
_, err = cccache.PutChaincode(ds)
169-
assert.Error(t, err)
170-
assert.Equal(t, "the chaincode name cannot be an empty string", err.Error(), "Unexpected error received")
171147

172148
// Error case 2: ccver is empty
173149
ccname = "foo"
174150
ccver = ""
175-
ds, err = getDepSpec(ccname, ccpath, ccver, [][]byte{[]byte("init"), []byte("a"), []byte("100"), []byte("b"), []byte("200")})
151+
_, err = getDepSpec(ccname, ccpath, ccver, [][]byte{[]byte("init"), []byte("a"), []byte("100"), []byte("b"), []byte("200")})
176152
assert.NoError(t, err)
177-
_, err = cccache.PutChaincode(ds)
178-
assert.Error(t, err)
179-
assert.Equal(t, "the chaincode version cannot be an empty string", err.Error(), "Unexpected error received")
180153

181154
// Error case 3: ccfs.PutChainCode returns an error
182-
ccinfoFs = &mockCCInfoFSStorageMgrImpl{CCMap: map[string]CCPackage{}, putchaincodeErr: true}
183-
cccache = NewCCInfoCache(ccinfoFs)
155+
ccinfoFs = &mockCCInfoFSStorageMgrImpl{CCMap: map[string]CCPackage{}}
156+
NewCCInfoCache(ccinfoFs)
184157

185158
ccname = "foo"
186159
ccver = "1.0"
187-
ds, err = getDepSpec(ccname, ccpath, ccver, [][]byte{[]byte("init"), []byte("a"), []byte("100"), []byte("b"), []byte("200")})
160+
_, err = getDepSpec(ccname, ccpath, ccver, [][]byte{[]byte("init"), []byte("a"), []byte("100"), []byte("b"), []byte("200")})
188161
assert.NoError(t, err)
189-
_, err = cccache.PutChaincode(ds)
190-
assert.Error(t, err)
191-
assert.Contains(t, err.Error(), "PutChaincodeIntoFS failed", "Unexpected error received")
192162
}
193163

194164
// here we test the peer's built-in cache after enabling it
@@ -208,44 +178,16 @@ func TestCCInfoFSPeerInstance(t *testing.T) {
208178
// put it
209179
err = PutChaincodeIntoFS(ds)
210180
assert.NoError(t, err)
211-
}
212-
213-
// here we test the peer's built-in cache after enabling it
214-
func TestCCInfoCachePeerInstance(t *testing.T) {
215-
// enable the cache first: it's disabled by default
216-
EnableCCInfoCache()
217-
218-
ccname := "foo"
219-
ccver := "1.0"
220-
ccpath := "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02"
221-
222-
// the cc data is not yet in the cache
223-
_, err := GetChaincodeFromFS(ccname, ccver)
224-
assert.Error(t, err)
225-
226-
// create a dep spec to put
227-
ds, err := getDepSpec(ccname, ccpath, ccver, [][]byte{[]byte("init"), []byte("a"), []byte("100"), []byte("b"), []byte("200")})
228-
assert.NoError(t, err)
229-
230-
// put it
231-
err = PutChaincodeIntoFS(ds)
232-
assert.NoError(t, err)
233-
234-
// Check if chain code package exists
235-
exists, err := ChaincodePackageExists(ccname, ccver)
236-
assert.NoError(t, err)
237-
assert.True(t, exists, "ChaincodePackageExists should have returned true for an existing package")
238181

239182
// Get all installed chaincodes, it should not return 0 chain codes
240183
resp, err := GetInstalledChaincodes()
241184
assert.NoError(t, err)
242185
assert.NotNil(t, resp)
243186
assert.NotZero(t, len(resp.Chaincodes), "GetInstalledChaincodes should not have returned 0 chain codes")
244187

245-
// expect it to be in the cache
246-
cd, err := GetChaincodeFromFS(ccname, ccver)
188+
//get chaincode data
189+
_, err = GetChaincodeData(ccname, ccver)
247190
assert.NoError(t, err)
248-
assert.NotNil(t, cd)
249191
}
250192

251193
func TestGetInstalledChaincodesErrorPaths(t *testing.T) {

core/common/ccprovider/ccprovider.go

+23-31
Original file line numberDiff line numberDiff line change
@@ -112,20 +112,13 @@ func ChaincodePackageExists(ccname string, ccversion string) (bool, error) {
112112
return false, err
113113
}
114114

115-
// CCInfoProvider is responsible to provide backend storage for information
116-
// about chaincodes. Multiple implementations can persist data to a file system
117-
// or store them in a cache
118-
type CCInfoProvider interface {
119-
// GetChaincode returns information for the chaincode with the
120-
// supplied name and version
115+
type CCCacheSupport interface {
116+
//GetChaincode is needed by the cache to get chaincode data
121117
GetChaincode(ccname string, ccversion string) (CCPackage, error)
122-
123-
// PutChaincode stores the supplied chaincode info
124-
PutChaincode(depSpec *pb.ChaincodeDeploymentSpec) (CCPackage, error)
125118
}
126119

127-
// CCInfoFSStorageMgr is an implementation of CCInfoProvider
128-
// backed by the file system
120+
// CCInfoFSImpl provides the implementation for CC on the FS and the access to it
121+
// It implements CCCacheSupport
129122
type CCInfoFSImpl struct{}
130123

131124
// GetChaincodeFromFS this is a wrapper for hiding package implementation.
@@ -195,40 +188,39 @@ func EnableCCInfoCache() {
195188
ccInfoCacheEnabled = true
196189
}
197190

198-
// GetChaincodeFromFS retrieves chaincode information from the cache (or the
199-
// file system in case of a cache miss) if the cache is enabled, or directly
200-
// from the file system otherwise
191+
// GetChaincodeFromFS retrieves chaincode information from the file system
201192
func GetChaincodeFromFS(ccname string, ccversion string) (CCPackage, error) {
202-
if ccInfoCacheEnabled {
203-
return ccInfoCache.GetChaincode(ccname, ccversion)
204-
} else {
205-
return ccInfoFSProvider.GetChaincode(ccname, ccversion)
206-
}
193+
return ccInfoFSProvider.GetChaincode(ccname, ccversion)
207194
}
208195

209196
// PutChaincodeIntoFS puts chaincode information in the file system (and
210197
// also in the cache to prime it) if the cache is enabled, or directly
211198
// from the file system otherwise
212199
func PutChaincodeIntoFS(depSpec *pb.ChaincodeDeploymentSpec) error {
200+
_, err := ccInfoFSProvider.PutChaincode(depSpec)
201+
return err
202+
}
203+
204+
// GetChaincodeData gets chaincode data from cache if there's one
205+
func GetChaincodeData(ccname string, ccversion string) (*ChaincodeData, error) {
213206
if ccInfoCacheEnabled {
214-
_, err := ccInfoCache.PutChaincode(depSpec)
215-
return err
207+
ccproviderLogger.Debugf("Getting chaincode data for <%s, %s> from cache", ccname, ccversion)
208+
return ccInfoCache.GetChaincodeData(ccname, ccversion)
209+
}
210+
if ccpack, err := ccInfoFSProvider.GetChaincode(ccname, ccversion); err != nil {
211+
return nil, err
216212
} else {
217-
_, err := ccInfoFSProvider.PutChaincode(depSpec)
218-
return err
213+
ccproviderLogger.Infof("Putting chaincode data for <%s, %s> into cache", ccname, ccversion)
214+
return ccpack.GetChaincodeData(), nil
219215
}
220216
}
221217

222218
func CheckInsantiationPolicy(name, version string, cdLedger *ChaincodeData) error {
223-
// we retrieve info about this chaincode from the file system
224-
ccpack, err := GetChaincodeFromFS(name, version)
219+
ccdata, err := GetChaincodeData(name, version)
225220
if err != nil {
226-
return fmt.Errorf("Chaincode data for cc %s/%s was not found, error %s", name, version, err)
221+
return err
227222
}
228223

229-
// ccpack is guaranteed to be non-nil
230-
cdLocalFS := ccpack.GetChaincodeData()
231-
232224
// we have the info from the fs, check that the policy
233225
// matches the one on the file system if one was specified;
234226
// this check is required because the admin of this peer
@@ -241,8 +233,8 @@ func CheckInsantiationPolicy(name, version string, cdLedger *ChaincodeData) erro
241233
// happen, i.e. that the peer will refuse to invoke the
242234
// chaincode under these conditions. More info on
243235
// https://jira.hyperledger.org/browse/FAB-3156
244-
if cdLocalFS.InstantiationPolicy != nil {
245-
if !bytes.Equal(cdLocalFS.InstantiationPolicy, cdLedger.InstantiationPolicy) {
236+
if ccdata.InstantiationPolicy != nil {
237+
if !bytes.Equal(ccdata.InstantiationPolicy, cdLedger.InstantiationPolicy) {
246238
return fmt.Errorf("Instantiation policy mismatch for cc %s/%s", name, version)
247239
}
248240
}

0 commit comments

Comments
 (0)