From 17219e214ba298672340b0b5245b736625c8b797 Mon Sep 17 00:00:00 2001 From: Srinivasan Muralidharan Date: Wed, 15 Mar 2017 13:06:34 -0400 Subject: [PATCH] FAB-2767 expose chaincode timeout as a property https://jira.hyperledger.org/browse/FAB-2767 The new property "chaincode.executetimeout" (CORE_CHAINCODE_EXECUTETIMEOUT) can be used override the default value of 30000ms (30 secs) for Init and Invoke methods to wait. The value should be at least 1000ms This was tested by setting CORE_CHAINCODE_EXECUTETIMEOUT=1000 and using "sleeper" chaincode to sleep for less than and greater than 1000ms with expected results of success and failure respectively. Change-Id: Icc02d4c83c1e389283dd57a7d35a694085e38f56 Signed-off-by: Srinivasan Muralidharan --- core/chaincode/chaincode_support.go | 12 +++ core/chaincode/exectransaction.go | 6 +- examples/chaincode/go/sleeper/sleeper.go | 124 +++++++++++++++++++++++ peer/core.yaml | 5 + 4 files changed, 142 insertions(+), 5 deletions(-) create mode 100644 examples/chaincode/go/sleeper/sleeper.go diff --git a/core/chaincode/chaincode_support.go b/core/chaincode/chaincode_support.go index c8bdafaafb0..d45669410b8 100644 --- a/core/chaincode/chaincode_support.go +++ b/core/chaincode/chaincode_support.go @@ -168,6 +168,17 @@ func NewChaincodeSupport(getPeerEndpoint func() (*pb.PeerEndpoint, error), userr theChaincodeSupport.keepalive = time.Duration(t) * time.Second } + //default chaincode execute timeout is 30000ms (30 secs) + execto := 30000 + if eto := viper.GetInt("chaincode.executetimeout"); eto <= 1000 { + chaincodeLogger.Errorf("Invalid execute timeout value %d (should be at least 1000ms) defaulting to %d ms", eto, execto) + } else { + chaincodeLogger.Debugf("Setting execute timeout value to %d ms", eto) + execto = eto + } + + theChaincodeSupport.executetimeout = time.Duration(execto) * time.Millisecond + viper.SetEnvPrefix("CORE") viper.AutomaticEnv() replacer := strings.NewReplacer(".", "_") @@ -208,6 +219,7 @@ type ChaincodeSupport struct { keepalive time.Duration chaincodeLogLevel string logFormat string + executetimeout time.Duration } // DuplicateChaincodeHandlerError returned if attempt to register same chaincodeID while a stream already exists. diff --git a/core/chaincode/exectransaction.go b/core/chaincode/exectransaction.go index f6c81bdfe40..3abe5b444e2 100644 --- a/core/chaincode/exectransaction.go +++ b/core/chaincode/exectransaction.go @@ -19,7 +19,6 @@ package chaincode import ( "errors" "fmt" - "time" "github.com/golang/protobuf/proto" "golang.org/x/net/context" @@ -57,9 +56,6 @@ func Execute(ctxt context.Context, cccid *ccprovider.CCContext, spec interface{} return nil, nil, fmt.Errorf("Failed to stablish stream to container %s", chaincode) } - // TODO: Need to comment next line and uncomment call to getTimeout, when transaction blocks are being created - timeout := time.Duration(30000) * time.Millisecond - if err != nil { return nil, nil, fmt.Errorf("Failed to retrieve chaincode spec(%s)", err) } @@ -70,7 +66,7 @@ func Execute(ctxt context.Context, cccid *ccprovider.CCContext, spec interface{} return nil, nil, fmt.Errorf("Failed to transaction message(%s)", err) } - resp, err := theChaincodeSupport.Execute(ctxt, cccid, ccMsg, timeout) + resp, err := theChaincodeSupport.Execute(ctxt, cccid, ccMsg, theChaincodeSupport.executetimeout) if err != nil { // Rollback transaction return nil, nil, fmt.Errorf("Failed to execute transaction (%s)", err) diff --git a/examples/chaincode/go/sleeper/sleeper.go b/examples/chaincode/go/sleeper/sleeper.go new file mode 100644 index 00000000000..088b598cbd7 --- /dev/null +++ b/examples/chaincode/go/sleeper/sleeper.go @@ -0,0 +1,124 @@ +/* +Copyright IBM Corp. 2016 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +// Sleeper chaincode sleeps and works with one state variable +// Init - 1 param, a sleep time in millisecs +// Invoke - 4 or 3 params, "put" or "get", value to set and sleep time in millisecs +// +// Sleeper can be used to test the "chaincode.executetimeout" property + +import ( + "fmt" + "strconv" + "time" + + "github.com/hyperledger/fabric/core/chaincode/shim" + pb "github.com/hyperledger/fabric/protos/peer" +) + +// SleeperChaincode example simple Chaincode implementation +type SleeperChaincode struct { +} + +func (t *SleeperChaincode) sleep(sleepTime string) { + st, _ := strconv.Atoi(sleepTime) + if st >= 0 { + time.Sleep(time.Duration(st) * time.Millisecond) + } +} + +// Init initializes chaincode...all it does is sleep a bi +func (t *SleeperChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response { + args := stub.GetStringArgs() + + if len(args) != 1 { + return shim.Error("Incorrect number of arguments. Expecting 1") + } + + sleepTime := args[0] + + t.sleep(sleepTime) + + return shim.Success(nil) +} + +// Invoke sets key/value and sleeps a bit +func (t *SleeperChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response { + function, args := stub.GetFunctionAndParameters() + if function == "put" { + if len(args) != 3 { + return shim.Error("Incorrect number of arguments. Expecting 3") + } + + // Make payment of X units from A to B + return t.invoke(stub, args) + } else if function == "get" { + if len(args) != 2 { + return shim.Error("Incorrect number of arguments. Expecting 2") + } + + // the old "Query" is now implemtned in invoke + return t.query(stub, args) + } + + return shim.Error("Invalid invoke function name. Expecting \"put\" or \"get\"") +} + +// Transaction makes payment of X units from A to B +func (t *SleeperChaincode) invoke(stub shim.ChaincodeStubInterface, args []string) pb.Response { + // set state + key := args[0] + val := args[1] + + err := stub.PutState(key, []byte(val)) + if err != nil { + return shim.Error(err.Error()) + } + + sleepTime := args[2] + + //sleep for a bit + t.sleep(sleepTime) + + return shim.Success([]byte("OK")) +} + +// query callback representing the query of a chaincode +func (t *SleeperChaincode) query(stub shim.ChaincodeStubInterface, args []string) pb.Response { + key := args[0] + + // Get the state from the ledger + val, err := stub.GetState(key) + if err != nil { + return shim.Error(err.Error()) + } + + sleepTime := args[1] + + //sleep for a bit + t.sleep(sleepTime) + + return shim.Success(val) +} + +func main() { + err := shim.Start(new(SleeperChaincode)) + if err != nil { + fmt.Printf("Error starting Sleeper chaincode: %s", err) + } +} diff --git a/peer/core.yaml b/peer/core.yaml index b34bce7ed81..6c07854009f 100644 --- a/peer/core.yaml +++ b/peer/core.yaml @@ -323,6 +323,11 @@ chaincode: # to come through. 1sec should be plenty for chaincode unit tests startuptimeout: 300000 + # timeout in millisecs for invokes and initialize commands + # this timeout is used by all chaincodes in all the channels including + # system chaincodes. Default is 30000ms (30 seconds) + executetimeout: 30000 + #timeout in millisecs for deploying chaincode from a remote repository. deploytimeout: 30000