Skip to content

Commit

Permalink
[FAB-1163] range query access tx ctxt after timeout
Browse files Browse the repository at this point in the history
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 <muralisr@us.ibm.com>
  • Loading branch information
Srinivasan Muralidharan committed May 10, 2017
1 parent 4d8c8d7 commit ae6a99e
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 0 deletions.
22 changes: 22 additions & 0 deletions core/chaincode/exectransaction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
14 changes: 14 additions & 0 deletions core/chaincode/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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()
Expand Down
13 changes: 13 additions & 0 deletions examples/chaincode/go/map/map.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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))
Expand All @@ -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))
Expand Down

0 comments on commit ae6a99e

Please sign in to comment.