Skip to content

Commit ee7871c

Browse files
author
Jason Yellick
committed
FAB-14376 Cache info about installed chaincodes
Up until this CR, the chaincode cache has only held information about defined chaincodes. However, for making decisions about whether to run chaincodes we also need information about installed chaincodes. This CR adds a list of locally available chaincodes and which channel instantiated chaincodes they correspond to. Change-Id: Id9bb21cd19827a4492c14d5e51b3a3f087511151 Signed-off-by: Jason Yellick <jyellick@us.ibm.com>
1 parent 99b5595 commit ee7871c

File tree

4 files changed

+320
-9
lines changed

4 files changed

+320
-9
lines changed

core/chaincode/lifecycle/cache.go

Lines changed: 124 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,22 @@ import (
1111
"sync"
1212

1313
"github.com/hyperledger/fabric/common/util"
14+
"github.com/hyperledger/fabric/core/chaincode/persistence"
1415
"github.com/hyperledger/fabric/core/ledger"
1516

1617
"github.com/pkg/errors"
1718
)
1819

20+
type ChaincodeInstallInfo struct {
21+
Hash []byte
22+
Type string
23+
Path string
24+
}
25+
1926
type CachedChaincodeDefinition struct {
20-
Definition *ChaincodeDefinition
21-
Approved bool
27+
Definition *ChaincodeDefinition
28+
Approved bool
29+
InstallInfo *ChaincodeInstallInfo
2230

2331
// Hashes is the list of hashed keys in the implicit collection referring to this definition.
2432
// These hashes are determined by the current sequence number of chaincode definition. When dirty,
@@ -48,16 +56,72 @@ type Cache struct {
4856
// event, by synchronizing at a peer global level, we drastically simplify accounting for which
4957
// chaincodes are installed and which channels that installed chaincode is currently in use on.
5058
mutex sync.RWMutex
59+
60+
// localChaincodes is a map from the hash of the locally installed chaincode's hash
61+
// (yes, the hash of the hash), to a set of channels, to a set of chaincode
62+
// definitions which reference this local installed chaincode hash.
63+
localChaincodes map[string]*LocalChaincode
64+
}
65+
66+
type LocalChaincode struct {
67+
Info *ChaincodeInstallInfo
68+
References map[string]map[string]*CachedChaincodeDefinition
5169
}
5270

5371
func NewCache(lifecycle *Lifecycle, myOrgMSPID string) *Cache {
5472
return &Cache{
5573
definedChaincodes: map[string]*ChannelCache{},
74+
localChaincodes: map[string]*LocalChaincode{},
5675
Lifecycle: lifecycle,
5776
MyOrgMSPID: myOrgMSPID,
5877
}
5978
}
6079

80+
// InitializeLocalChaincodes should be called once after cache creation (timing doesn't matter,
81+
// though already installed chaincodes will not be invokable until it it completes). Ideally,
82+
// this would be part of the constructor, but, we cannot rely on the chaincode store being created
83+
// before the cache is created.
84+
func (c *Cache) InitializeLocalChaincodes() error {
85+
c.mutex.Lock()
86+
defer c.mutex.Unlock()
87+
ccPackages, err := c.Lifecycle.ChaincodeStore.ListInstalledChaincodes()
88+
if err != nil {
89+
return errors.WithMessage(err, "could not list installed chaincodes")
90+
}
91+
92+
for _, ccPackage := range ccPackages {
93+
ccPackageBytes, _, err := c.Lifecycle.ChaincodeStore.Load(ccPackage.Id)
94+
if err != nil {
95+
return errors.WithMessage(err, fmt.Sprintf("could not load chaincode with hash '%x'", ccPackage.Id))
96+
}
97+
parsedCCPackage, err := c.Lifecycle.PackageParser.Parse(ccPackageBytes)
98+
if err != nil {
99+
return errors.WithMessage(err, fmt.Sprintf("could not parse chaincode package with hash '%x'", ccPackage.Id))
100+
}
101+
c.handleChaincodeInstalledWhileLocked(parsedCCPackage.Metadata, ccPackage.Id)
102+
}
103+
104+
logger.Infof("Initialized lifecycle cache with %d already installed chaincodes", len(c.localChaincodes))
105+
for channelID, chaincodeCache := range c.definedChaincodes {
106+
approved, installed, runnable := 0, 0, 0
107+
for _, cachedChaincode := range chaincodeCache.Chaincodes {
108+
if cachedChaincode.Approved {
109+
approved++
110+
}
111+
if cachedChaincode.InstallInfo != nil {
112+
installed++
113+
}
114+
if cachedChaincode.Approved && cachedChaincode.InstallInfo != nil {
115+
runnable++
116+
}
117+
}
118+
119+
logger.Infof("Initialized lifecycle cache for channel '%s' with %d chaincodes runnable (%d approved, %d installed)", channelID, runnable, approved, installed)
120+
}
121+
122+
return nil
123+
}
124+
61125
// Initialize will populate the set of currently committed chaincode definitions
62126
// for a channel into the cache. Note, it this looks like a bit of a DRY violation
63127
// with respect to 'Update', but, the error handling is quite different and attempting
@@ -88,6 +152,35 @@ func (c *Cache) Initialize(channelID string, qe ledger.SimpleQueryExecutor) erro
88152
return c.update(channelID, dirtyChaincodes, qe)
89153
}
90154

155+
// HandleChaincodeInstalled should be invoked whenever a new chaincode is installed
156+
func (c *Cache) HandleChaincodeInstalled(md *persistence.ChaincodePackageMetadata, hash []byte) {
157+
c.mutex.Lock()
158+
defer c.mutex.Unlock()
159+
c.handleChaincodeInstalledWhileLocked(md, hash)
160+
}
161+
162+
func (c *Cache) handleChaincodeInstalledWhileLocked(md *persistence.ChaincodePackageMetadata, hash []byte) {
163+
hashOfCCHash := string(util.ComputeSHA256(hash))
164+
localChaincode, ok := c.localChaincodes[hashOfCCHash]
165+
if !ok {
166+
localChaincode = &LocalChaincode{
167+
References: map[string]map[string]*CachedChaincodeDefinition{},
168+
}
169+
c.localChaincodes[hashOfCCHash] = localChaincode
170+
}
171+
localChaincode.Info = &ChaincodeInstallInfo{
172+
Hash: hash,
173+
Type: md.Type,
174+
Path: md.Path,
175+
}
176+
for channelID, channelCache := range localChaincode.References {
177+
for chaincodeName, cachedChaincode := range channelCache {
178+
cachedChaincode.InstallInfo = localChaincode.Info
179+
logger.Infof("Installed chaincode with hash %x now available on channel %s for chaincode definition %s:%s", hash, channelID, chaincodeName, cachedChaincode.Definition.EndorsementInfo.Version)
180+
}
181+
}
182+
}
183+
91184
// HandleStateUpdates is required to implement the ledger state listener interface. It applies
92185
// any state updates to the cache.
93186
func (c *Cache) HandleStateUpdates(trigger *ledger.StateUpdateTrigger) error {
@@ -200,6 +293,7 @@ func (c *Cache) update(channelID string, dirtyChaincodes map[string]struct{}, qe
200293
}
201294

202295
for name := range dirtyChaincodes {
296+
logger.Infof("Updating cached definition for chaincode '%s' on channel '%s'", name, channelID)
203297
cachedChaincode, ok := channelCache.Chaincodes[name]
204298
if !ok {
205299
cachedChaincode = &CachedChaincodeDefinition{}
@@ -243,9 +337,35 @@ func (c *Cache) update(channelID string, dirtyChaincodes map[string]struct{}, qe
243337
if err != nil {
244338
return errors.WithMessage(err, fmt.Sprintf("could not check opaque org state for '%s' on channel '%s'", name, channelID))
245339
}
246-
if ok {
247-
cachedChaincode.Approved = true
340+
if !ok {
341+
logger.Debugf("Channel %s for chaincode definition %s:%s does not have our org's approval", channelID, name, chaincodeDefinition.EndorsementInfo.Version)
342+
continue
343+
}
344+
345+
cachedChaincode.Approved = true
346+
hashOfCCHash := string(util.ComputeSHA256(cachedChaincode.Definition.EndorsementInfo.Id))
347+
localChaincode, ok := c.localChaincodes[hashOfCCHash]
348+
if !ok {
349+
localChaincode = &LocalChaincode{
350+
References: map[string]map[string]*CachedChaincodeDefinition{},
351+
}
352+
c.localChaincodes[hashOfCCHash] = localChaincode
248353
}
354+
355+
cachedChaincode.InstallInfo = localChaincode.Info
356+
if localChaincode.Info != nil {
357+
logger.Infof("Chaincode with hash %x now available on channel %s for chaincode definition %s:%s", localChaincode.Info.Hash, channelID, name, cachedChaincode.Definition.EndorsementInfo.Version)
358+
} else {
359+
logger.Debugf("Chaincode definition for chaincode '%s' on channel '%s' is approved, but not installed", name, channelID)
360+
}
361+
362+
channelReferences, ok := localChaincode.References[channelID]
363+
if !ok {
364+
channelReferences = map[string]*CachedChaincodeDefinition{}
365+
localChaincode.References[channelID] = channelReferences
366+
}
367+
368+
channelReferences[name] = cachedChaincode
249369
}
250370

251371
return nil

core/chaincode/lifecycle/cache_internal_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,11 @@ func SetChaincodeMap(c *Cache, channelID string, channelCache *ChannelCache) {
1515
func GetChaincodeMap(c *Cache, channelID string) *ChannelCache {
1616
return c.definedChaincodes[channelID]
1717
}
18+
19+
func SetLocalChaincodesMap(c *Cache, localChaincodes map[string]*LocalChaincode) {
20+
c.localChaincodes = localChaincodes
21+
}
22+
23+
func GetLocalChaincodeMap(c *Cache) map[string]*LocalChaincode {
24+
return c.localChaincodes
25+
}

0 commit comments

Comments
 (0)