Skip to content

Commit

Permalink
Properly handle concurrent building of chaincode packages
Browse files Browse the repository at this point in the history
The chaincode building has a mechanism that detects parallel building of the same chaincode package,
and enforces mutual exclusion to prevent that from happening.

Unfortunately, the mechanism had a bug because it relies on locks but returned a copy of the lock and not
the real lock, thus each goroutine was locking its own lock and not a shared lock.

Another issue was that the mapping that held the locks per package ID was growing indefinitely and never shrinking.

Change-Id: I3d33c369487e0c81d8c64ce6f0bc833b3f2a01bc
Signed-off-by: Yacov Manevich <yacovm@il.ibm.com>
(cherry picked from commit cb98555)
  • Loading branch information
yacovm authored and denyeart committed May 6, 2022
1 parent cfbb980 commit 578a648
Showing 1 changed file with 24 additions and 7 deletions.
31 changes: 24 additions & 7 deletions core/chaincode/lifecycle/lifecycle.go
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,8 @@ type ExternalFunctions struct {
ChaincodeBuilder ChaincodeBuilder
BuildRegistry *container.BuildRegistry
mutex sync.Mutex
BuildLocks map[string]sync.Mutex
BuildLocks map[string]*sync.Mutex
concurrentInstalls uint32
}

// CheckCommitReadiness takes a chaincode definition, checks that
Expand Down Expand Up @@ -677,7 +678,9 @@ func (ef *ExternalFunctions) InstallChaincode(chaincodeInstallPackage []byte) (*
return nil, errors.WithMessage(err, "could not save cc install package")
}

buildLock := ef.getBuildLock(packageID)
buildLock, cleanupBuildLocks := ef.getBuildLock(packageID)
defer cleanupBuildLocks()

buildLock.Lock()
defer buildLock.Unlock()

Expand Down Expand Up @@ -711,20 +714,34 @@ func (ef *ExternalFunctions) InstallChaincode(chaincodeInstallPackage []byte) (*
}, nil
}

func (ef *ExternalFunctions) getBuildLock(packageID string) *sync.Mutex {
func (ef *ExternalFunctions) getBuildLock(packageID string) (*sync.Mutex, func()) {
ef.mutex.Lock()
defer ef.mutex.Unlock()

ef.concurrentInstalls++

cleanup := func() {
ef.mutex.Lock()
defer ef.mutex.Unlock()

ef.concurrentInstalls--
// If there are no other concurrent installs happening in parallel,
// cleanup the build lock mapping to release memory
if ef.concurrentInstalls == 0 {
ef.BuildLocks = nil
}
}

if ef.BuildLocks == nil {
ef.BuildLocks = map[string]sync.Mutex{}
ef.BuildLocks = map[string]*sync.Mutex{}
}

buildLock, ok := ef.BuildLocks[packageID]
_, ok := ef.BuildLocks[packageID]
if !ok {
ef.BuildLocks[packageID] = sync.Mutex{}
ef.BuildLocks[packageID] = &sync.Mutex{}
}

return &buildLock
return ef.BuildLocks[packageID], cleanup
}

// GetInstalledChaincodePackage retrieves the installed chaincode with the given package ID
Expand Down

0 comments on commit 578a648

Please sign in to comment.