Skip to content

Commit

Permalink
Better gRPC error on context error from CommitStatus service
Browse files Browse the repository at this point in the history
Rather than always return a FailedPrecondition error on failure obtaining commit status, return either a DeadlineExceeded or Canceled error on a context error. This may happen if the call is cancelled from the client end, either by an explicit context cancel or timeout.

Signed-off-by: Mark S. Lewis <mark_lewis@uk.ibm.com>
  • Loading branch information
bestbeforetoday authored and ale-linux committed Nov 5, 2021
1 parent 62fb4c0 commit 5113aa9
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 10 deletions.
12 changes: 6 additions & 6 deletions internal/pkg/gateway/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ func (gs *Server) Evaluate(ctx context.Context, request *gp.EvaluateRequest) (*g
gs.registry.removeEndorser(endorser)
endorser = plan.retry(endorser)
if endorser == nil {
return nil, rpcError(codes.Aborted, "failed to evaluate transaction, see attached details for more info", errDetails...)
return nil, newRpcError(codes.Aborted, "failed to evaluate transaction, see attached details for more info", errDetails...)
}
}

Expand All @@ -88,7 +88,7 @@ func (gs *Server) Evaluate(ctx context.Context, request *gp.EvaluateRequest) (*g
endpointErr := errorDetail(endorser.endpointConfig, err)
errDetails = append(errDetails, endpointErr)
// this is a chaincode error response - don't retry
return nil, rpcError(codes.Aborted, "evaluate call to endorser returned error: "+response.Message, errDetails...)
return nil, newRpcError(codes.Aborted, "evaluate call to endorser returned error: "+response.Message, errDetails...)
}
}

Expand Down Expand Up @@ -176,7 +176,7 @@ func (gs *Server) Endorse(ctx context.Context, request *gp.EndorseRequest) (*gp.
}
}
if firstEndorser == nil || firstResponse == nil {
return nil, rpcError(codes.Aborted, "failed to endorse transaction, see attached details for more info", errDetails...)
return nil, newRpcError(codes.Aborted, "failed to endorse transaction, see attached details for more info", errDetails...)
}

// 3. Extract ChaincodeInterest and SBE policies
Expand Down Expand Up @@ -258,7 +258,7 @@ func (gs *Server) Endorse(ctx context.Context, request *gp.EndorseRequest) (*gp.
}

if endorsements == nil {
return nil, rpcError(codes.Aborted, "failed to collect enough transaction endorsements, see attached details for more info", errorDetails...)
return nil, newRpcError(codes.Aborted, "failed to collect enough transaction endorsements, see attached details for more info", errorDetails...)
}

env, err := protoutil.CreateTx(proposal, endorsements...)
Expand Down Expand Up @@ -309,7 +309,7 @@ func (gs *Server) Submit(ctx context.Context, request *gp.SubmitRequest) (*gp.Su
errDetails = append(errDetails, errorDetail(orderer.endpointConfig, err))
}

return nil, rpcError(codes.Aborted, "no orderers could successfully process transaction", errDetails...)
return nil, newRpcError(codes.Aborted, "no orderers could successfully process transaction", errDetails...)
}

func (gs *Server) broadcast(ctx context.Context, orderer *orderer, txn *common.Envelope) error {
Expand Down Expand Up @@ -363,7 +363,7 @@ func (gs *Server) CommitStatus(ctx context.Context, signedRequest *gp.SignedComm

txStatus, err := gs.commitFinder.TransactionStatus(ctx, request.ChannelId, request.TransactionId)
if err != nil {
return nil, status.Error(codes.FailedPrecondition, err.Error())
return nil, toRpcError(err, codes.FailedPrecondition)
}

response := &gp.CommitStatusResponse{
Expand Down
13 changes: 11 additions & 2 deletions internal/pkg/gateway/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1198,6 +1198,16 @@ func TestCommitStatus(t *testing.T) {
BlockNumber: 101,
},
},
{
name: "context timeout",
finderErr: context.DeadlineExceeded,
errString: "rpc error: code = DeadlineExceeded desc = context deadline exceeded",
},
{
name: "context canceled",
finderErr: context.Canceled,
errString: "rpc error: code = Canceled desc = context canceled",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down Expand Up @@ -1624,7 +1634,7 @@ func TestNilArgs(t *testing.T) {
}

func TestRpcErrorWithBadDetails(t *testing.T) {
err := rpcError(codes.InvalidArgument, "terrible error", nil)
err := newRpcError(codes.InvalidArgument, "terrible error", nil)
require.ErrorIs(t, err, status.Error(codes.InvalidArgument, "terrible error"))
}

Expand Down Expand Up @@ -1741,7 +1751,6 @@ func prepareTest(t *testing.T, tt *testDef) *preparedTest {
dialer.Returns(nil, nil)
server.registry.endpointFactory = createEndpointFactory(t, epDef, dialer.Spy)

require.NoError(t, err, "Failed to sign the proposal")
ctx := context.WithValue(context.Background(), contextKey("orange"), "apples")

pt := &preparedTest{
Expand Down
18 changes: 16 additions & 2 deletions internal/pkg/gateway/apiutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func getChannelAndChaincodeFromSignedProposal(signedProposal *peer.SignedProposa
return channelHeader.ChannelId, spec.ChaincodeSpec.ChaincodeId.Name, len(payload.TransientMap) > 0, nil
}

func rpcError(code codes.Code, message string, details ...proto.Message) error {
func newRpcError(code codes.Code, message string, details ...proto.Message) error {
st := status.New(code, message)
if len(details) != 0 {
std, err := st.WithDetails(details...)
Expand All @@ -58,7 +58,21 @@ func rpcError(code codes.Code, message string, details ...proto.Message) error {

func wrappedRpcError(err error, message string, details ...proto.Message) error {
statusErr := status.Convert(err)
return rpcError(statusErr.Code(), message+": "+statusErr.Message(), details...)
return newRpcError(statusErr.Code(), message+": "+statusErr.Message(), details...)
}

func toRpcError(err error, unknownCode codes.Code) error {
errStatus, ok := status.FromError(err)
if ok {
return errStatus.Err()
}

errStatus = status.FromContextError(err)
if errStatus.Code() != codes.Unknown {
return errStatus.Err()
}

return status.Error(unknownCode, err.Error())
}

func errorDetail(e *endpointConfig, err error) *gp.ErrorDetail {
Expand Down

0 comments on commit 5113aa9

Please sign in to comment.