Skip to content

Commit fb61d76

Browse files
committed
[FAB-10540] stop getting tx sim for qscc/cscc
The state database transaction manager uses a read/write lock to serialize access to the state database. FAB-7595 introduced an additional lock in the block storage layer to prevent stale reads from the block access APIs after a commit event. During block commit processing, an exclusive lock is acquired at the block storage layer. While this lock is held, the state database transaction manager is called to perform its commit processing. During commit processing, the state database transaction manager attempts to acquire exclusive access to its commit lock and blocks while waiting for the associated readers to complete. At this layer, read locks are held by open transaction simulators associated with chaincode invocations. In the case of qscc, since it's chaincode, a transaction simulator is obtained for it and a read lock is held while is running. If the query happens to execute concurrent to commit processing, qscc will be unable to access the block storage layer because of the exclusive lock held during commit. The commit processing fails to complete as it is unable to acquire exclusive access to the commit lock maintained by the state database transaction manager because the query chaincode is currently holding the lock as a reader. This commit removes the acquisition of a transaction simulator for the query and configuration system chaincode as they do not require the simulator. This prevents the read lock from getting obtained by the state database transaction manager. Change-Id: Icde801b583b2bf8a2c9953a6c160b0478816ef64 Signed-off-by: Matthew Sykes <sykesmat@us.ibm.com>
1 parent 4236764 commit fb61d76

File tree

3 files changed

+89
-29
lines changed

3 files changed

+89
-29
lines changed

core/endorser/endorser.go

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -408,8 +408,7 @@ func (e *Endorser) preProcess(signedProp *pb.SignedProposal) (*validateResult, e
408408

409409
// block invocations to security-sensitive system chaincodes
410410
if e.s.IsSysCCAndNotInvokableExternal(hdrExt.ChaincodeId.Name) {
411-
endorserLogger.Errorf("Error: an attempt was made by %#v to invoke system chaincode %s",
412-
shdr.Creator, hdrExt.ChaincodeId.Name)
411+
endorserLogger.Errorf("Error: an attempt was made by %#v to invoke system chaincode %s", shdr.Creator, hdrExt.ChaincodeId.Name)
413412
err = errors.Errorf("chaincode %s cannot be invoked through a proposal", hdrExt.ChaincodeId.Name)
414413
vr.resp = &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}
415414
return vr, err
@@ -468,10 +467,11 @@ func (e *Endorser) ProcessProposal(ctx context.Context, signedProp *pb.SignedPro
468467
// Also obtain a history query executor for history queries, since tx simulator does not cover history
469468
var txsim ledger.TxSimulator
470469
var historyQueryExecutor ledger.HistoryQueryExecutor
471-
if chainID != "" {
470+
if acquireTxSimulator(chainID, vr.hdrExt.ChaincodeId) {
472471
if txsim, err = e.s.GetTxSimulator(chainID, txid); err != nil {
473472
return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, nil
474473
}
474+
475475
// txsim acquires a shared lock on the stateDB. As this would impact the block commits (i.e., commit
476476
// of valid write-sets to the stateDB), we must release the lock as early as possible.
477477
// Hence, this txsim object is closed in simulateProposal() as soon as the tx is simulated and
@@ -547,6 +547,24 @@ func (e *Endorser) ProcessProposal(ctx context.Context, signedProp *pb.SignedPro
547547
return pResp, nil
548548
}
549549

550+
// determine whether or not a transaction simulator should be
551+
// obtained for a proposal.
552+
func acquireTxSimulator(chainID string, ccid *pb.ChaincodeID) bool {
553+
if chainID == "" {
554+
return false
555+
}
556+
557+
// ¯\_(ツ)_/¯ locking.
558+
// Don't get a simulator for the query and config system chaincode.
559+
// These don't need the simulator and its read lock results in deadlocks.
560+
switch ccid.Name {
561+
case "qscc", "cscc":
562+
return false
563+
default:
564+
return true
565+
}
566+
}
567+
550568
// shorttxid replicates the chaincode package function to shorten txids.
551569
// ~~TODO utilize a common shorttxid utility across packages.~~
552570
// TODO use a formal type for transaction ID and make it a stringer

core/endorser/endorser_test.go

Lines changed: 62 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,14 @@ func getSignedPropWithCHIdAndArgs(chid, ccid, ccver string, ccargs [][]byte, t *
6666
return &pb.SignedProposal{ProposalBytes: propBytes, Signature: signature}
6767
}
6868

69+
func newMockTxSim() *mockccprovider.MockTxSim {
70+
return &mockccprovider.MockTxSim{
71+
GetTxSimulationResultsRv: &ledger.TxSimulationResults{
72+
PubSimulationResults: &rwset.TxReadWriteSet{},
73+
},
74+
}
75+
}
76+
6977
func TestEndorserNilProp(t *testing.T) {
7078
es := endorser.NewEndorserServer(pvtEmptyDistributor, &em.MockSupport{
7179
GetApplicationConfigBoolRv: true,
@@ -172,6 +180,7 @@ func TestEndorserSysCC(t *testing.T) {
172180
m := &mock.Mock{}
173181
m.On("Sign", mock.Anything).Return([]byte{1, 2, 3, 4, 5}, nil)
174182
m.On("Serialize").Return([]byte{1, 1, 1}, nil)
183+
m.On("GetTxSimulator", mock.Anything, mock.Anything).Return(newMockTxSim(), nil)
175184
support := &em.MockSupport{
176185
Mock: m,
177186
GetApplicationConfigBoolRv: true,
@@ -180,11 +189,6 @@ func TestEndorserSysCC(t *testing.T) {
180189
IsSysCCRv: true,
181190
ChaincodeDefinitionRv: &ccprovider.ChaincodeData{Escc: "ESCC"},
182191
ExecuteResp: &pb.Response{Status: 200, Payload: utils.MarshalOrPanic(&pb.ProposalResponse{Response: &pb.Response{}})},
183-
GetTxSimulatorRv: &mockccprovider.MockTxSim{
184-
GetTxSimulationResultsRv: &ledger.TxSimulationResults{
185-
PubSimulationResults: &rwset.TxReadWriteSet{},
186-
},
187-
},
188192
}
189193
attachPluginEndorser(support)
190194
es := endorser.NewEndorserServer(pvtEmptyDistributor, support)
@@ -448,6 +452,7 @@ func TestEndorserGoodPathWEvents(t *testing.T) {
448452
m := &mock.Mock{}
449453
m.On("Sign", mock.Anything).Return([]byte{1, 2, 3, 4, 5}, nil)
450454
m.On("Serialize").Return([]byte{1, 1, 1}, nil)
455+
m.On("GetTxSimulator", mock.Anything, mock.Anything).Return(newMockTxSim(), nil)
451456
support := &em.MockSupport{
452457
Mock: m,
453458
GetApplicationConfigBoolRv: true,
@@ -456,11 +461,6 @@ func TestEndorserGoodPathWEvents(t *testing.T) {
456461
ChaincodeDefinitionRv: &ccprovider.ChaincodeData{Escc: "ESCC"},
457462
ExecuteResp: &pb.Response{Status: 200, Payload: utils.MarshalOrPanic(&pb.ProposalResponse{Response: &pb.Response{}})},
458463
ExecuteEvent: &pb.ChaincodeEvent{},
459-
GetTxSimulatorRv: &mockccprovider.MockTxSim{
460-
GetTxSimulationResultsRv: &ledger.TxSimulationResults{
461-
PubSimulationResults: &rwset.TxReadWriteSet{},
462-
},
463-
},
464464
}
465465
attachPluginEndorser(support)
466466
es := endorser.NewEndorserServer(pvtEmptyDistributor, support)
@@ -498,18 +498,14 @@ func TestEndorserGoodPath(t *testing.T) {
498498
m := &mock.Mock{}
499499
m.On("Sign", mock.Anything).Return([]byte{1, 2, 3, 4, 5}, nil)
500500
m.On("Serialize").Return([]byte{1, 1, 1}, nil)
501+
m.On("GetTxSimulator", mock.Anything, mock.Anything).Return(newMockTxSim(), nil)
501502
support := &em.MockSupport{
502503
Mock: m,
503504
GetApplicationConfigBoolRv: true,
504505
GetApplicationConfigRv: &mc.MockApplication{CapabilitiesRv: &mc.MockApplicationCapabilities{}},
505506
GetTransactionByIDErr: errors.New(""),
506507
ChaincodeDefinitionRv: &ccprovider.ChaincodeData{Escc: "ESCC"},
507508
ExecuteResp: &pb.Response{Status: 200, Payload: utils.MarshalOrPanic(&pb.ProposalResponse{Response: &pb.Response{}})},
508-
GetTxSimulatorRv: &mockccprovider.MockTxSim{
509-
GetTxSimulationResultsRv: &ledger.TxSimulationResults{
510-
PubSimulationResults: &rwset.TxReadWriteSet{},
511-
},
512-
},
513509
}
514510
attachPluginEndorser(support)
515511
es := endorser.NewEndorserServer(pvtEmptyDistributor, support)
@@ -525,18 +521,14 @@ func TestEndorserLSCC(t *testing.T) {
525521
m := &mock.Mock{}
526522
m.On("Sign", mock.Anything).Return([]byte{1, 2, 3, 4, 5}, nil)
527523
m.On("Serialize").Return([]byte{1, 1, 1}, nil)
524+
m.On("GetTxSimulator", mock.Anything, mock.Anything).Return(newMockTxSim(), nil)
528525
support := &em.MockSupport{
529526
Mock: m,
530527
GetApplicationConfigBoolRv: true,
531528
GetApplicationConfigRv: &mc.MockApplication{CapabilitiesRv: &mc.MockApplicationCapabilities{}},
532529
GetTransactionByIDErr: errors.New(""),
533530
ChaincodeDefinitionRv: &ccprovider.ChaincodeData{Escc: "ESCC"},
534531
ExecuteResp: &pb.Response{Status: 200, Payload: utils.MarshalOrPanic(&pb.ProposalResponse{Response: &pb.Response{}})},
535-
GetTxSimulatorRv: &mockccprovider.MockTxSim{
536-
GetTxSimulationResultsRv: &ledger.TxSimulationResults{
537-
PubSimulationResults: &rwset.TxReadWriteSet{},
538-
},
539-
},
540532
}
541533
attachPluginEndorser(support)
542534
es := endorser.NewEndorserServer(pvtEmptyDistributor, support)
@@ -576,18 +568,14 @@ func TestEndorseWithPlugin(t *testing.T) {
576568
m := &mock.Mock{}
577569
m.On("Sign", mock.Anything).Return([]byte{1, 2, 3, 4, 5}, nil)
578570
m.On("Serialize").Return([]byte{1, 1, 1}, nil)
571+
m.On("GetTxSimulator", mock.Anything, mock.Anything).Return(newMockTxSim(), nil)
579572
support := &em.MockSupport{
580573
Mock: m,
581574
GetApplicationConfigBoolRv: true,
582575
GetApplicationConfigRv: &mc.MockApplication{CapabilitiesRv: &mc.MockApplicationCapabilities{}},
583576
GetTransactionByIDErr: errors.New("can't find this transaction in the index"),
584577
ChaincodeDefinitionRv: &resourceconfig.MockChaincodeDefinition{EndorsementStr: "ESCC"},
585578
ExecuteResp: &pb.Response{Status: 200, Payload: []byte{1}},
586-
GetTxSimulatorRv: &mockccprovider.MockTxSim{
587-
GetTxSimulationResultsRv: &ledger.TxSimulationResults{
588-
PubSimulationResults: &rwset.TxReadWriteSet{},
589-
},
590-
},
591579
}
592580
attachPluginEndorser(support)
593581

@@ -648,6 +636,55 @@ func TestEndorserJavaChecks(t *testing.T) {
648636
assert.Error(t, err)
649637
}
650638

639+
func TestEndorserAcquireTxSimulator(t *testing.T) {
640+
tc := []struct {
641+
name string
642+
chainID string
643+
chaincodeName string
644+
simAcquired bool
645+
}{
646+
{"empty channel", "", "ignored", false},
647+
{"query scc", util.GetTestChainID(), "qscc", false},
648+
{"config scc", util.GetTestChainID(), "cscc", false},
649+
{"mainline", util.GetTestChainID(), "chaincode", true},
650+
}
651+
652+
expectedResponse := &pb.Response{Status: 200, Payload: utils.MarshalOrPanic(&pb.ProposalResponse{Response: &pb.Response{}})}
653+
for _, tt := range tc {
654+
tt := tt
655+
t.Run(tt.name, func(t *testing.T) {
656+
m := &mock.Mock{}
657+
m.On("Sign", mock.Anything).Return([]byte{1, 2, 3, 4, 5}, nil)
658+
m.On("Serialize").Return([]byte{1, 1, 1}, nil)
659+
m.On("GetTxSimulator", mock.Anything, mock.Anything).Return(newMockTxSim(), nil)
660+
support := &em.MockSupport{
661+
Mock: m,
662+
GetApplicationConfigBoolRv: true,
663+
GetApplicationConfigRv: &mc.MockApplication{CapabilitiesRv: &mc.MockApplicationCapabilities{}},
664+
GetTransactionByIDErr: errors.New(""),
665+
ChaincodeDefinitionRv: &ccprovider.ChaincodeData{Escc: "ESCC"},
666+
ExecuteResp: expectedResponse,
667+
}
668+
attachPluginEndorser(support)
669+
es := endorser.NewEndorserServer(pvtEmptyDistributor, support)
670+
671+
t.Parallel()
672+
args := [][]byte{[]byte("args")}
673+
signedProp := getSignedPropWithCHIdAndArgs(tt.chainID, tt.chaincodeName, "version", args, t)
674+
675+
resp, err := es.ProcessProposal(context.Background(), signedProp)
676+
assert.NoError(t, err)
677+
assert.Equal(t, expectedResponse, resp.Response)
678+
679+
if tt.simAcquired {
680+
m.AssertCalled(t, "GetTxSimulator", mock.Anything, mock.Anything)
681+
} else {
682+
m.AssertNotCalled(t, "GetTxSimulator", mock.Anything, mock.Anything)
683+
}
684+
})
685+
}
686+
}
687+
651688
var signer msp.SigningIdentity
652689

653690
func TestMain(m *testing.M) {

core/mocks/endorser/support.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,12 @@ func (s *MockSupport) IsSysCCAndNotInvokableExternal(name string) bool {
6666
}
6767

6868
func (s *MockSupport) GetTxSimulator(ledgername string, txid string) (ledger.TxSimulator, error) {
69-
return s.GetTxSimulatorRv, s.GetTxSimulatorErr
69+
if s.Mock == nil {
70+
return s.GetTxSimulatorRv, s.GetTxSimulatorErr
71+
}
72+
73+
args := s.Called(ledgername, txid)
74+
return args.Get(0).(ledger.TxSimulator), args.Error(1)
7075
}
7176

7277
func (s *MockSupport) GetHistoryQueryExecutor(ledgername string) (ledger.HistoryQueryExecutor, error) {

0 commit comments

Comments
 (0)