Skip to content

Commit

Permalink
[FAB-9935] retry on 'chaincode is already launching'
Browse files Browse the repository at this point in the history
- this error is removed in fabric v1.2, but we are still
handling it for v1.1


Change-Id: I4a4f5b8b18eaabc6007a5e4dca3f4b1a0f84afef
Signed-off-by: Sudesh Shetty <sudesh.shetty@securekey.com>
  • Loading branch information
sudeshrshetty committed May 7, 2018
1 parent 914555e commit 9093022
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 2 deletions.
3 changes: 3 additions & 0 deletions pkg/common/errors/retry/defaults.go
Expand Up @@ -71,6 +71,7 @@ var DefaultRetryableCodes = map[status.Group][]status.Code{
status.EndorserClientStatus: {
status.EndorsementMismatch,
status.PrematureChaincodeExecution,
status.ChaincodeAlreadyLaunching,
},
status.EndorserServerStatus: {
status.Code(common.Status_SERVICE_UNAVAILABLE),
Expand Down Expand Up @@ -99,6 +100,7 @@ var ResMgmtDefaultRetryableCodes = map[status.Group][]status.Code{
status.EndorserClientStatus: {
status.EndorsementMismatch,
status.PrematureChaincodeExecution,
status.ChaincodeAlreadyLaunching,
},
status.EndorserServerStatus: {
status.Code(common.Status_SERVICE_UNAVAILABLE),
Expand Down Expand Up @@ -129,6 +131,7 @@ var ChannelClientRetryableCodes = map[status.Group][]status.Code{
status.EndorserClientStatus: {
status.ConnectionFailed, status.EndorsementMismatch,
status.PrematureChaincodeExecution,
status.ChaincodeAlreadyLaunching,
},
status.EndorserServerStatus: {
status.Code(common.Status_SERVICE_UNAVAILABLE),
Expand Down
3 changes: 3 additions & 0 deletions pkg/common/errors/status/codes.go
Expand Up @@ -63,6 +63,9 @@ const (

// NoMatchingChannelEntity is if entityMatchers are unable to find any matchingChannel
NoMatchingChannelEntity Code = 25

// ChaincodeAlreadyLaunching indicates that an attempt for multiple simultaneous invokes was made to launch chaincode
ChaincodeAlreadyLaunching Code = 26
)

// CodeName maps the codes in this packages to human-readable strings
Expand Down
24 changes: 22 additions & 2 deletions pkg/fab/peer/peerendorser.go
Expand Up @@ -35,6 +35,7 @@ const (
// GRPC max message size (same as Fabric)
maxCallRecvMsgSize = 100 * 1024 * 1024
maxCallSendMsgSize = 100 * 1024 * 1024
statusCodeUnknown = "Unknown"
)

// peerEndorser enables access to a GRPC-based endorser for running transaction proposal simulations
Expand Down Expand Up @@ -158,12 +159,20 @@ func (p *peerEndorser) sendProposal(ctx reqContext.Context, proposal fab.Process
if ok {
code, message, extractErr := extractChaincodeError(rpcStatus)
if extractErr != nil {

code, message1, extractErr := extractPrematureExecutionError(rpcStatus)

if extractErr != nil {
//if not premation excution error, then look for chaincode already launching error
code, message1, extractErr = extractChaincodeAlreadyLaunchingError(rpcStatus)
}

if extractErr != nil {
err = status.NewFromGRPCStatus(rpcStatus)
} else {
err = status.New(status.EndorserClientStatus, code, message1, nil)
}

} else {
err = status.NewFromExtractedChaincodeError(code, message)
}
Expand All @@ -179,7 +188,7 @@ func (p *peerEndorser) sendProposal(ctx reqContext.Context, proposal fab.Process
func extractChaincodeError(status *grpcstatus.Status) (int, string, error) {
var code int
var message string
if status.Code().String() != "Unknown" || status.Message() == "" {
if status.Code().String() != statusCodeUnknown || status.Message() == "" {
return 0, "", errors.New("Unable to parse GRPC status message")
}
statusLength := len("status:")
Expand Down Expand Up @@ -227,7 +236,7 @@ func checkMessage(status *grpcstatus.Status, messageLength int, message string)
}

func extractPrematureExecutionError(grpcstat *grpcstatus.Status) (int32, string, error) {
if grpcstat.Code().String() != "Unknown" || grpcstat.Message() == "" {
if grpcstat.Code().String() != statusCodeUnknown || grpcstat.Message() == "" {
return 0, "", errors.New("not a premature execution error")
}
index := strings.Index(grpcstat.Message(), "premature execution")
Expand All @@ -237,6 +246,17 @@ func extractPrematureExecutionError(grpcstat *grpcstatus.Status) (int32, string,
return int32(status.PrematureChaincodeExecution), grpcstat.Message()[index:], nil
}

func extractChaincodeAlreadyLaunchingError(grpcstat *grpcstatus.Status) (int32, string, error) {
if grpcstat.Code().String() != statusCodeUnknown || grpcstat.Message() == "" {
return 0, "", errors.New("not a chaincode already launching error")
}
index := strings.Index(grpcstat.Message(), "error chaincode is already launching:")
if index == -1 {
return 0, "", errors.New("not a chaincode already launching error")
}
return int32(status.ChaincodeAlreadyLaunching), grpcstat.Message()[index:], nil
}

// getChaincodeResponseStatus gets the actual response status from response.Payload.extension.Response.status, as fabric always returns actual 200
func getChaincodeResponseStatus(response *pb.ProposalResponse) int32 {
if response.Payload != nil {
Expand Down
21 changes: 21 additions & 0 deletions pkg/fab/peer/peerendorser_test.go
Expand Up @@ -298,6 +298,7 @@ func TestExtractChainCodeError(t *testing.T) {
}

func TestExtractPrematureExecError(t *testing.T) {

err := grpcstatus.New(grpcCodes.Unknown, "some error")
_, _, e := extractPrematureExecutionError(err)
assert.EqualError(t, e, "not a premature execution error")
Expand All @@ -313,6 +314,26 @@ func TestExtractPrematureExecError(t *testing.T) {
assert.EqualValues(t, "premature execution - chaincode (somecc:v1) launched and waiting for registration", message, "Invalid message")
}

func TestExtractChaincodeAlreadyLaunchingError(t *testing.T) {

err := grpcstatus.New(grpcCodes.Unknown, "some error")
_, _, e := extractPrematureExecutionError(err)
assert.EqualError(t, e, "not a premature execution error")

err = grpcstatus.New(grpcCodes.Unknown, "error executing chaincode: error chaincode is already launching: somecc:v1")
code, message, extractErr := extractChaincodeAlreadyLaunchingError(err)
assert.EqualValues(t, int32(status.ChaincodeAlreadyLaunching), code, "Expected chaincode already launching error")
assert.EqualValues(t, "error chaincode is already launching: somecc:v1", message, "Invalid message")
assert.Nil(t, extractErr)

err = grpcstatus.New(grpcCodes.Unknown, "error executing chaincode: some random error: somecc:v1")
code, message, extractErr = extractChaincodeAlreadyLaunchingError(err)
assert.NotNil(t, extractErr)
assert.EqualValues(t, 0, code)
assert.Empty(t, message)

}

func TestChaincodeStatusFromResponse(t *testing.T) {
//For error response
response := &pb.ProposalResponse{
Expand Down

0 comments on commit 9093022

Please sign in to comment.