Skip to content

Commit 2232d0e

Browse files
committed
[FAB-5391]Prevent concurrent invokes launching cc cont
This CR prevents concurrent invokes from launching a chaincode container at the same time (e.g. during performance testing). The first invoke should succeed (and launch the container) while subsequent invokes should fail until the container has finished launching. Note: This does not change any behavior as subsequent invokes should have failed but it now sends a clear error message that the chaincode container is already launching. Change-Id: Ic1772a5f25dd0e4c34278e6e7bdb8507f16269c8 Signed-off-by: Will Lahti <[email protected]>
1 parent 7a480ce commit 2232d0e

File tree

1 file changed

+56
-5
lines changed

1 file changed

+56
-5
lines changed

core/chaincode/chaincode_support.go

+56-5
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,10 @@ type runningChaincodes struct {
9595
sync.RWMutex
9696
// chaincode environment for each chaincode
9797
chaincodeMap map[string]*chaincodeRTEnv
98+
99+
//mark the starting of launch of a chaincode so multiple requests
100+
//do not attempt to start the chaincode at the same time
101+
launchStarted map[string]bool
98102
}
99103

100104
//GetChain returns the chaincode framework support object
@@ -119,14 +123,22 @@ func (chaincodeSupport *ChaincodeSupport) chaincodeHasBeenLaunched(chaincode str
119123
return chrte, hasbeenlaunched
120124
}
121125

126+
//call this under lock
127+
func (chaincodeSupport *ChaincodeSupport) launchStarted(chaincode string) bool {
128+
if _, launchStarted := chaincodeSupport.runningChaincodes.launchStarted[chaincode]; launchStarted {
129+
return true
130+
}
131+
return false
132+
}
133+
122134
// NewChaincodeSupport creates a new ChaincodeSupport instance
123135
func NewChaincodeSupport(getCCEndpoint func() (*pb.PeerEndpoint, error), userrunsCC bool, ccstartuptimeout time.Duration) *ChaincodeSupport {
124136
ccprovider.SetChaincodesPath(config.GetPath("peer.fileSystemPath") + string(filepath.Separator) + "chaincodes")
125137

126138
pnid := viper.GetString("peer.networkId")
127139
pid := viper.GetString("peer.id")
128140

129-
theChaincodeSupport = &ChaincodeSupport{runningChaincodes: &runningChaincodes{chaincodeMap: make(map[string]*chaincodeRTEnv)}, peerNetworkID: pnid, peerID: pid}
141+
theChaincodeSupport = &ChaincodeSupport{runningChaincodes: &runningChaincodes{chaincodeMap: make(map[string]*chaincodeRTEnv), launchStarted: make(map[string]bool)}, peerNetworkID: pnid, peerID: pid}
130142

131143
//initialize global chain
132144

@@ -396,7 +408,8 @@ func (chaincodeSupport *ChaincodeSupport) getArgsAndEnv(cccid *ccprovider.CCCont
396408
return args, envs, nil
397409
}
398410

399-
// launchAndWaitForRegister will launch container if not already running. Use the targz to create the image if not found
411+
//launchAndWaitForRegister will launch container if not already running. Use
412+
//the targz to create the image if not found
400413
func (chaincodeSupport *ChaincodeSupport) launchAndWaitForRegister(ctxt context.Context, cccid *ccprovider.CCContext, cds *pb.ChaincodeDeploymentSpec, cLang pb.ChaincodeSpec_Type, builder api.BuildSpecFactory) error {
401414
canName := cccid.GetCanonicalName()
402415
if canName == "" {
@@ -408,9 +421,37 @@ func (chaincodeSupport *ChaincodeSupport) launchAndWaitForRegister(ctxt context.
408421
//multiple launch by failing
409422
if _, hasBeenLaunched := chaincodeSupport.chaincodeHasBeenLaunched(canName); hasBeenLaunched {
410423
chaincodeSupport.runningChaincodes.Unlock()
411-
return fmt.Errorf("Error chaincode is being launched: %s", canName)
424+
return fmt.Errorf("Error chaincode has been launched: %s", canName)
412425
}
413426

427+
//prohibit multiple simultaneous invokes (for example while flooding the
428+
//system with invokes as in a stress test scenario) from attempting to launch
429+
//the chaincode. The first one wins. Others receive an error.
430+
//NOTE - this transient behavior as the chaincode is being launched is nothing
431+
//new. All invokes (except the one launching the CC) will fail in any case
432+
//until the container is up and registered.
433+
if chaincodeSupport.launchStarted(canName) {
434+
chaincodeSupport.runningChaincodes.Unlock()
435+
return fmt.Errorf("Error chaincode is already launching: %s", canName)
436+
}
437+
438+
//Chaincode is not up and is not in the process of being launched. Setup flag
439+
//for launching so we can proceed to do that undisturbed by other requests on
440+
//this chaincode
441+
chaincodeLogger.Debugf("chaincode %s is being launched", canName)
442+
chaincodeSupport.runningChaincodes.launchStarted[canName] = true
443+
444+
//now that chaincode launch sequence is done (whether successful or not),
445+
//unset launch flag as we get out of this function. If launch was not
446+
//successful (handler was not created), next invoke will try again.
447+
defer func() {
448+
chaincodeSupport.runningChaincodes.Lock()
449+
defer chaincodeSupport.runningChaincodes.Unlock()
450+
451+
delete(chaincodeSupport.runningChaincodes.launchStarted, canName)
452+
chaincodeLogger.Debugf("chaincode %s launch seq completed", canName)
453+
}()
454+
414455
chaincodeSupport.runningChaincodes.Unlock()
415456

416457
//launch the chaincode
@@ -535,8 +576,8 @@ func (chaincodeSupport *ChaincodeSupport) Launch(context context.Context, cccid
535576
if chrte, ok = chaincodeSupport.chaincodeHasBeenLaunched(canName); ok {
536577
if !chrte.handler.registered {
537578
chaincodeSupport.runningChaincodes.Unlock()
538-
chaincodeLogger.Debugf("premature execution - chaincode (%s) is being launched", canName)
539-
err = fmt.Errorf("premature execution - chaincode (%s) is being launched", canName)
579+
chaincodeLogger.Debugf("premature execution - chaincode (%s) launched and waiting for registration", canName)
580+
err = fmt.Errorf("premature execution - chaincode (%s) launched and waiting for registration", canName)
540581
return cID, cMsg, err
541582
}
542583
if chrte.handler.isRunning() {
@@ -547,6 +588,16 @@ func (chaincodeSupport *ChaincodeSupport) Launch(context context.Context, cccid
547588
return cID, cMsg, nil
548589
}
549590
chaincodeLogger.Debugf("Container not in READY state(%s)...send init/ready", chrte.handler.FSM.Current())
591+
} else {
592+
//chaincode is not up... but is the launch process underway? this is
593+
//strictly not necessary as the actual launch process will catch this
594+
//(in launchAndWaitForRegister), just a bit of optimization for thundering
595+
//herds
596+
if chaincodeSupport.launchStarted(canName) {
597+
chaincodeSupport.runningChaincodes.Unlock()
598+
err = fmt.Errorf("premature execution - chaincode (%s) is being launched", canName)
599+
return cID, cMsg, err
600+
}
550601
}
551602
chaincodeSupport.runningChaincodes.Unlock()
552603

0 commit comments

Comments
 (0)