github.com/ahlemtn/fabric@v2.1.1+incompatible/core/ledger/cceventmgmt/mgr.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package cceventmgmt 8 9 import ( 10 "sync" 11 12 "github.com/hyperledger/fabric/common/flogging" 13 "github.com/hyperledger/fabric/core/ledger" 14 ) 15 16 var logger = flogging.MustGetLogger("cceventmgmt") 17 18 var mgr *Mgr 19 20 // Initialize initializes event mgmt 21 func Initialize(ccInfoProvider ChaincodeInfoProvider) { 22 initialize(ccInfoProvider) 23 } 24 25 func initialize(ccInfoProvider ChaincodeInfoProvider) { 26 mgr = newMgr(ccInfoProvider) 27 } 28 29 // GetMgr returns the reference to singleton event manager 30 func GetMgr() *Mgr { 31 return mgr 32 } 33 34 // Mgr encapsulate important interactions for events related to the interest of ledger 35 type Mgr struct { 36 // rwlock is mainly used to synchronize across deploy transaction, chaincode install, and channel creation 37 // Ideally, different services in the peer should be designed such that they expose locks for different important 38 // events so that a code on top can synchronize across if needs to. However, in the lack of any such system-wide design, 39 // we use this lock for contextual use 40 rwlock sync.RWMutex 41 infoProvider ChaincodeInfoProvider 42 ccLifecycleListeners map[string][]ChaincodeLifecycleEventListener 43 callbackStatus *callbackStatus 44 } 45 46 func newMgr(chaincodeInfoProvider ChaincodeInfoProvider) *Mgr { 47 return &Mgr{ 48 infoProvider: chaincodeInfoProvider, 49 ccLifecycleListeners: make(map[string][]ChaincodeLifecycleEventListener), 50 callbackStatus: newCallbackStatus()} 51 } 52 53 // Register registers a ChaincodeLifecycleEventListener for given ledgerid 54 // Since, `Register` is expected to be invoked when creating/opening a ledger instance 55 func (m *Mgr) Register(ledgerid string, l ChaincodeLifecycleEventListener) { 56 // write lock to synchronize concurrent 'chaincode install' operations with ledger creation/open 57 m.rwlock.Lock() 58 defer m.rwlock.Unlock() 59 m.ccLifecycleListeners[ledgerid] = append(m.ccLifecycleListeners[ledgerid], l) 60 } 61 62 // HandleChaincodeDeploy is expected to be invoked when a chaincode is deployed via a deploy transaction 63 // The `chaincodeDefinitions` parameter contains all the chaincodes deployed in a block 64 // We need to store the last received `chaincodeDefinitions` because this function is expected to be invoked 65 // after the deploy transactions validation is performed but not committed yet to the ledger. Further, we 66 // release the read lock after this function. This leaves a small window when a `chaincode install` can happen 67 // before the deploy transaction is committed and hence the function `HandleChaincodeInstall` may miss finding 68 // the deployed chaincode. So, in function `HandleChaincodeInstall`, we explicitly check for chaincode deployed 69 // in this stored `chaincodeDefinitions` 70 func (m *Mgr) HandleChaincodeDeploy(chainid string, chaincodeDefinitions []*ChaincodeDefinition) error { 71 logger.Debugf("Channel [%s]: Handling chaincode deploy event for chaincode [%s]", chainid, chaincodeDefinitions) 72 // Read lock to allow concurrent deploy on multiple channels but to synchronize concurrent `chaincode install` operation 73 m.rwlock.RLock() 74 for _, chaincodeDefinition := range chaincodeDefinitions { 75 installed, dbArtifacts, err := m.infoProvider.RetrieveChaincodeArtifacts(chaincodeDefinition) 76 if err != nil { 77 return err 78 } 79 if !installed { 80 logger.Infof("Channel [%s]: Chaincode [%s] is not installed hence no need to create chaincode artifacts for endorsement", 81 chainid, chaincodeDefinition) 82 continue 83 } 84 m.callbackStatus.setDeployPending(chainid) 85 if err := m.invokeHandler(chainid, chaincodeDefinition, dbArtifacts); err != nil { 86 logger.Warningf("Channel [%s]: Error while invoking a listener for handling chaincode install event: %s", chainid, err) 87 return err 88 } 89 logger.Debugf("Channel [%s]: Handled chaincode deploy event for chaincode [%s]", chainid, chaincodeDefinitions) 90 } 91 return nil 92 } 93 94 // ChaincodeDeployDone is expected to be called when the deploy transaction state is committed 95 func (m *Mgr) ChaincodeDeployDone(chainid string) { 96 // release the lock acquired in function `HandleChaincodeDeploy` 97 defer m.rwlock.RUnlock() 98 if m.callbackStatus.isDeployPending(chainid) { 99 m.invokeDoneOnHandlers(chainid, true) 100 m.callbackStatus.unsetDeployPending(chainid) 101 } 102 } 103 104 // HandleChaincodeInstall is expected to get invoked during installation of a chaincode package 105 func (m *Mgr) HandleChaincodeInstall(chaincodeDefinition *ChaincodeDefinition, dbArtifacts []byte) error { 106 logger.Debugf("HandleChaincodeInstall() - chaincodeDefinition=%#v", chaincodeDefinition) 107 // Write lock prevents concurrent deploy operations 108 m.rwlock.Lock() 109 for chainid := range m.ccLifecycleListeners { 110 logger.Debugf("Channel [%s]: Handling chaincode install event for chaincode [%s]", chainid, chaincodeDefinition) 111 var deployedCCInfo *ledger.DeployedChaincodeInfo 112 var err error 113 if deployedCCInfo, err = m.infoProvider.GetDeployedChaincodeInfo(chainid, chaincodeDefinition); err != nil { 114 logger.Warningf("Channel [%s]: Error while getting the deployment status of chaincode: %s", chainid, err) 115 return err 116 } 117 if deployedCCInfo == nil { 118 logger.Debugf("Channel [%s]: Chaincode [%s] is not deployed on channel hence not creating chaincode artifacts.", 119 chainid, chaincodeDefinition) 120 continue 121 } 122 if !deployedCCInfo.IsLegacy { 123 // the chaincode has already been defined via new lifecycle, we reach here because of a subsequent 124 // install of chaincode using legacy package. So, ignoring this event 125 logger.Debugf("Channel [%s]: Chaincode [%s] is already defined in new lifecycle hence not creating chaincode artifacts.", 126 chainid, chaincodeDefinition) 127 continue 128 } 129 m.callbackStatus.setInstallPending(chainid) 130 chaincodeDefinition.CollectionConfigs = deployedCCInfo.ExplicitCollectionConfigPkg 131 if err := m.invokeHandler(chainid, chaincodeDefinition, dbArtifacts); err != nil { 132 logger.Warningf("Channel [%s]: Error while invoking a listener for handling chaincode install event: %s", chainid, err) 133 return err 134 } 135 logger.Debugf("Channel [%s]: Handled chaincode install event for chaincode [%s]", chainid, chaincodeDefinition) 136 } 137 return nil 138 } 139 140 // ChaincodeInstallDone is expected to get invoked when chaincode install finishes 141 func (m *Mgr) ChaincodeInstallDone(succeeded bool) { 142 // release the lock acquired in function `HandleChaincodeInstall` 143 defer m.rwlock.Unlock() 144 for chainid := range m.callbackStatus.installPending { 145 m.invokeDoneOnHandlers(chainid, succeeded) 146 m.callbackStatus.unsetInstallPending(chainid) 147 } 148 } 149 150 func (m *Mgr) invokeHandler(chainid string, chaincodeDefinition *ChaincodeDefinition, dbArtifactsTar []byte) error { 151 listeners := m.ccLifecycleListeners[chainid] 152 for _, listener := range listeners { 153 if err := listener.HandleChaincodeDeploy(chaincodeDefinition, dbArtifactsTar); err != nil { 154 return err 155 } 156 } 157 return nil 158 } 159 160 func (m *Mgr) invokeDoneOnHandlers(chainid string, succeeded bool) { 161 listeners := m.ccLifecycleListeners[chainid] 162 for _, listener := range listeners { 163 listener.ChaincodeDeployDone(succeeded) 164 } 165 } 166 167 type callbackStatus struct { 168 l sync.Mutex 169 deployPending map[string]bool 170 installPending map[string]bool 171 } 172 173 func newCallbackStatus() *callbackStatus { 174 return &callbackStatus{ 175 deployPending: make(map[string]bool), 176 installPending: make(map[string]bool)} 177 } 178 179 func (s *callbackStatus) setDeployPending(channelID string) { 180 s.l.Lock() 181 defer s.l.Unlock() 182 s.deployPending[channelID] = true 183 } 184 185 func (s *callbackStatus) unsetDeployPending(channelID string) { 186 s.l.Lock() 187 defer s.l.Unlock() 188 delete(s.deployPending, channelID) 189 } 190 191 func (s *callbackStatus) isDeployPending(channelID string) bool { 192 s.l.Lock() 193 defer s.l.Unlock() 194 return s.deployPending[channelID] 195 } 196 197 func (s *callbackStatus) setInstallPending(channelID string) { 198 s.l.Lock() 199 defer s.l.Unlock() 200 s.installPending[channelID] = true 201 } 202 203 func (s *callbackStatus) unsetInstallPending(channelID string) { 204 s.l.Lock() 205 defer s.l.Unlock() 206 delete(s.installPending, channelID) 207 }