From ae6a99ec9f10dab418b9206322f88cb2ed077d3c Mon Sep 17 00:00:00 2001 From: Srinivasan Muralidharan Date: Tue, 9 May 2017 20:37:54 -0400 Subject: [PATCH] [FAB-1163] range query access tx ctxt after timeout Range query iterators accessed transaction contexts after they were deleted on timeout. Added a sleep parameter to the map chaincode's range query path with which to test timeout. Change-Id: If4fc92b49839d306593cc63667b878fc41f6588c Signed-off-by: Srinivasan Muralidharan --- core/chaincode/exectransaction_test.go | 22 ++++++++++++++++++++++ core/chaincode/handler.go | 14 ++++++++++++++ examples/chaincode/go/map/map.go | 13 +++++++++++++ 3 files changed, 49 insertions(+) diff --git a/core/chaincode/exectransaction_test.go b/core/chaincode/exectransaction_test.go index eb8f5fbe421..ad1f4c77a3c 100644 --- a/core/chaincode/exectransaction_test.go +++ b/core/chaincode/exectransaction_test.go @@ -1146,6 +1146,28 @@ func TestQueries(t *testing.T) { return } + //FAB-1163- The following range query should timeout and produce an error + //the peer should handle this gracefully and not die + + //save the original timeout and set a new timeout of 1 sec + origTimeout := theChaincodeSupport.executetimeout + theChaincodeSupport.executetimeout = time.Duration(1) * time.Second + + //chaincode to sleep for 2 secs with timeout 1 + args = util.ToChaincodeArgs(f, "marble001", "marble002", "2000") + + spec = &pb.ChaincodeSpec{Type: 1, ChaincodeId: cID, Input: &pb.ChaincodeInput{Args: args}} + _, _, retval, err = invoke(ctxt, chainID, spec, nextBlockNumber, nil) + if err == nil { + t.Fail() + t.Logf("expected timeout error but succeeded") + theChaincodeSupport.Stop(ctxt, cccid, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec}) + return + } + + //restore timeout + theChaincodeSupport.executetimeout = origTimeout + // querying for all marbles will return 101 marbles // this query should return exactly 101 results (one call to Next()) //The following range query for "marble001" to "marble102" should return 101 marbles diff --git a/core/chaincode/handler.go b/core/chaincode/handler.go index 07fd132a228..81b4e6c7a3a 100644 --- a/core/chaincode/handler.go +++ b/core/chaincode/handler.go @@ -839,6 +839,13 @@ func (handler *Handler) handleQueryStateNext(msg *pb.ChaincodeMessage) { } txContext := handler.getTxContext(msg.Txid) + if txContext == nil { + payload := []byte("transaction context not found (timed out ?)") + chaincodeLogger.Errorf("[%s]Failed to get transaction context. Sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_ERROR) + serialSendMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid} + return + } + queryIter := handler.getQueryIterator(txContext, queryStateNext.Id) if queryIter == nil { @@ -918,6 +925,13 @@ func (handler *Handler) handleQueryStateClose(msg *pb.ChaincodeMessage) { } txContext := handler.getTxContext(msg.Txid) + if txContext == nil { + payload := []byte("transaction context not found (timed out ?)") + chaincodeLogger.Errorf("[%s]Failed to get transaction context. Sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_ERROR) + serialSendMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid} + return + } + iter := handler.getQueryIterator(txContext, queryStateClose.Id) if iter != nil { iter.Close() diff --git a/examples/chaincode/go/map/map.go b/examples/chaincode/go/map/map.go index daa7ae70a2e..ce1b94df553 100644 --- a/examples/chaincode/go/map/map.go +++ b/examples/chaincode/go/map/map.go @@ -19,6 +19,8 @@ package main import ( "encoding/json" "fmt" + "strconv" + "time" "github.com/hyperledger/fabric/core/chaincode/shim" pb "github.com/hyperledger/fabric/protos/peer" @@ -104,6 +106,12 @@ func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response { startKey := args[0] endKey := args[1] + //sleep needed to test peer's timeout behavior when using iterators + stime := 0 + if len(args) > 2 { + stime, _ = strconv.Atoi(args[2]) + } + keysIter, err := stub.GetStateByRange(startKey, endKey) if err != nil { return shim.Error(fmt.Sprintf("keys operation failed. Error accessing state: %s", err)) @@ -112,6 +120,11 @@ func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response { var keys []string for keysIter.HasNext() { + //if sleeptime is specied, take a nap + if stime > 0 { + time.Sleep(time.Duration(stime) * time.Millisecond) + } + response, iterErr := keysIter.Next() if iterErr != nil { return shim.Error(fmt.Sprintf("keys operation failed. Error accessing state: %s", err))