diff --git a/examples/ccchecker/ccchecker.go b/examples/ccchecker/ccchecker.go index 77c5f11c228..66bb9778993 100644 --- a/examples/ccchecker/ccchecker.go +++ b/examples/ccchecker/ccchecker.go @@ -36,7 +36,7 @@ var ccchecker *CCChecker //CCChecker encapsulates ccchecker properties and runtime type CCChecker struct { //Chaincodes to do ccchecker over (see ccchecker.json for defaults) - Chaincodes []*chaincodes.CC + Chaincodes []*chaincodes.CCClient //TimeoutToAbortSecs abort deadline TimeoutToAbortSecs int //ChainName name of the chain @@ -62,7 +62,7 @@ func LoadCCCheckerParams(file string) error { //concurrency <=0 will be dropped if scc.Concurrency > 0 { for i := 0; i < scc.Concurrency; i++ { - tmp := &chaincodes.CC{} + tmp := &chaincodes.CCClient{} *tmp = *scc tmp.ID = id id = id + 1 @@ -77,14 +77,14 @@ func LoadCCCheckerParams(file string) error { return nil } -//CCCheckerInit assigns shadow chaincode to each of the CC from registered shadow chaincodes +//CCCheckerInit assigns shadow chaincode to each of the CCClient from registered shadow chaincodes func CCCheckerInit() { if ccchecker == nil { fmt.Printf("LoadCCCheckerParams needs to be called before init\n") os.Exit(1) } - if err := chaincodes.RegisterCCs(ccchecker.Chaincodes); err != nil { + if err := chaincodes.RegisterCCClients(ccchecker.Chaincodes); err != nil { panic(fmt.Sprintf("%s", err)) } } @@ -119,16 +119,16 @@ func CCCheckerRun(report bool, verbose bool) error { //an anonymous struct to hold failures var failures struct { sync.Mutex - failedCCs int + failedCCClients int } //run the invokes ccerrs := make([]error, len(ccchecker.Chaincodes)) for _, cc := range ccchecker.Chaincodes { - go func(cc2 *chaincodes.CC) { + go func(cc2 *chaincodes.CCClient) { if ccerrs[cc2.ID] = cc2.Run(ctxt, ccchecker.ChainName, bc, ec, signer, &ccsWG); ccerrs[cc2.ID] != nil { failures.Lock() - failures.failedCCs = failures.failedCCs + 1 + failures.failedCCClients = failures.failedCCClients + 1 failures.Unlock() } }(cc) @@ -138,11 +138,11 @@ func CCCheckerRun(report bool, verbose bool) error { err = ccchecker.wait(&ccsWG) //verify results - if err == nil && failures.failedCCs < len(ccchecker.Chaincodes) { + if err == nil && failures.failedCCClients < len(ccchecker.Chaincodes) { ccsWG = sync.WaitGroup{} - ccsWG.Add(len(ccchecker.Chaincodes) - failures.failedCCs) + ccsWG.Add(len(ccchecker.Chaincodes) - failures.failedCCClients) for _, cc := range ccchecker.Chaincodes { - go func(cc2 *chaincodes.CC) { + go func(cc2 *chaincodes.CCClient) { if ccerrs[cc2.ID] == nil { ccerrs[cc2.ID] = cc2.Validate(ctxt, ccchecker.ChainName, bc, ec, signer, &ccsWG) } else { diff --git a/examples/ccchecker/ccchecker.json b/examples/ccchecker/ccchecker.json index 9c2aebe2d70..a335ca38212 100644 --- a/examples/ccchecker/ccchecker.json +++ b/examples/ccchecker/ccchecker.json @@ -1,9 +1,10 @@ {"Chaincodes": [ {"Name": "mycc", + "InitArgs":[""], "Path": "github.com/hyperledger/fabric/examples/ccchecker/chaincodes/newkeyperinvoke", "NumFinalQueryAttempts": 10, - "NumberOfIterations": 10, + "NumberOfInvokes": 10, "DelayBetweenInvokeMs": 1, "DelayBetweenQueryMs": 10, "TimeoutToAbortSecs": 60, diff --git a/examples/ccchecker/chaincodes/chaincodes.go b/examples/ccchecker/chaincodes/chaincodes.go index 21725726606..5d95bad5ee8 100644 --- a/examples/ccchecker/chaincodes/chaincodes.go +++ b/examples/ccchecker/chaincodes/chaincodes.go @@ -32,7 +32,7 @@ import ( //ShadowCCIntf interfaces to be implemented by shadow chaincodes type ShadowCCIntf interface { //InitShadowCC initializes the shadow chaincode (will be called once for each chaincode) - InitShadowCC() + InitShadowCC(initArgs []string) //GetInvokeArgs gets invoke arguments from shadow GetInvokeArgs(ccnum int, iter int) [][]byte @@ -45,22 +45,31 @@ type ShadowCCIntf interface { //Validate the results against the query arguments Validate(args [][]byte, value []byte) error + + //GetNumQueries returns number of queries to perform + GetNumQueries(numSuccessfulInvokes int) int + + //OverrideNumInvokes overrides the users number of invoke request + OverrideNumInvokes(numInvokesPlanned int) int } -//CC chaincode properties, config and runtime -type CC struct { +//CCClient chaincode properties, config and runtime +type CCClient struct { //-------------config properties ------------ //Name of the chaincode Name string + //InitArgs used for deploying the chaincode + InitArgs []string + //Path to the chaincode Path string //NumFinalQueryAttempts number of times to try final query before giving up NumFinalQueryAttempts int - //NumberOfInvokeIterations number of iterations to do invoke on - NumberOfIterations int + //NumberOfInvokes number of iterations to do invoke on + NumberOfInvokes int //DelayBetweenInvokeMs delay between each invoke DelayBetweenInvokeMs int @@ -87,14 +96,22 @@ type CC struct { //shadow CC where the chaincode stats is maintained shadowCC ShadowCCIntf + //number of iterations a shadow cc actually wants + //this could be different from NumberOfInvokes + overriddenNumInvokes int + + //numer of queries to perform + //retrieved from the shadow CC + numQueries int + //current iteration of invoke currentInvokeIter int //start of invokes in epoch seconds - invokeStartTime int + invokeStartTime int64 //end of invokes in epoch seconds - invokeEndTime int + invokeEndTime int64 //error that stopped invoke iterations invokeErr error @@ -109,7 +126,7 @@ type CC struct { queryErrs []error } -func (cc *CC) getChaincodeSpec(args [][]byte) *pb.ChaincodeSpec { +func (cc *CCClient) getChaincodeSpec(args [][]byte) *pb.ChaincodeSpec { return &pb.ChaincodeSpec{ Type: pb.ChaincodeSpec_Type(pb.ChaincodeSpec_Type_value[cc.Lang]), ChaincodeID: &pb.ChaincodeID{Path: cc.Path, Name: cc.Name}, @@ -120,12 +137,15 @@ func (cc *CC) getChaincodeSpec(args [][]byte) *pb.ChaincodeSpec { //doInvokes calls invoke for each iteration for the chaincode //Stops at the first invoke with error //currentInvokeIter contains the number of successful iterations -func (cc *CC) doInvokes(ctxt context.Context, chainID string, +func (cc *CCClient) doInvokes(ctxt context.Context, chainID string, bc common.BroadcastClient, ec pb.EndorserClient, signer msp.SigningIdentity, wg *sync.WaitGroup, quit func() bool) error { + //perhaps the shadow CC wants to override the number of iterations + cc.overriddenNumInvokes = cc.shadowCC.OverrideNumInvokes(cc.NumberOfInvokes) + var err error - for cc.currentInvokeIter = 0; cc.currentInvokeIter < cc.NumberOfIterations; cc.currentInvokeIter++ { + for cc.currentInvokeIter = 0; cc.currentInvokeIter < cc.overriddenNumInvokes; cc.currentInvokeIter++ { if quit() { break } @@ -154,7 +174,7 @@ func (cc *CC) doInvokes(ctxt context.Context, chainID string, } //don't sleep for the last iter - if cc.DelayBetweenInvokeMs > 0 && cc.currentInvokeIter < (cc.NumberOfIterations-1) { + if cc.DelayBetweenInvokeMs > 0 && cc.currentInvokeIter < (cc.overriddenNumInvokes-1) { time.Sleep(time.Duration(cc.DelayBetweenInvokeMs) * time.Millisecond) } } @@ -165,7 +185,7 @@ func (cc *CC) doInvokes(ctxt context.Context, chainID string, //Run test over given number of iterations // i will be unique across chaincodes and can be used as a key // this is useful if chaincode occurs multiple times in the array of chaincodes -func (cc *CC) Run(ctxt context.Context, chainID string, bc common.BroadcastClient, ec pb.EndorserClient, signer msp.SigningIdentity, wg *sync.WaitGroup) error { +func (cc *CCClient) Run(ctxt context.Context, chainID string, bc common.BroadcastClient, ec pb.EndorserClient, signer msp.SigningIdentity, wg *sync.WaitGroup) error { defer wg.Done() var ( @@ -181,12 +201,12 @@ func (cc *CC) Run(ctxt context.Context, chainID string, bc common.BroadcastClien quitF := func() bool { return quit } //start of invokes - cc.invokeStartTime = time.Now().Second() + cc.invokeStartTime = time.Now().UnixNano() / 1000000 err = cc.doInvokes(ctxt, chainID, bc, ec, signer, wg, quitF) //end of invokes - cc.invokeEndTime = time.Now().Second() + cc.invokeEndTime = time.Now().UnixNano() / 1000000 }() //we could be done or cancelled or timedout @@ -203,7 +223,7 @@ func (cc *CC) Run(ctxt context.Context, chainID string, bc common.BroadcastClien } //validates the invoke iteration for this chaincode -func (cc *CC) validateIter(ctxt context.Context, iter int, chainID string, bc common.BroadcastClient, ec pb.EndorserClient, signer msp.SigningIdentity, wg *sync.WaitGroup, quit func() bool) { +func (cc *CCClient) validateIter(ctxt context.Context, iter int, chainID string, bc common.BroadcastClient, ec pb.EndorserClient, signer msp.SigningIdentity, wg *sync.WaitGroup, quit func() bool) { defer wg.Done() args := cc.shadowCC.GetQueryArgs(cc.ID, iter) @@ -253,7 +273,7 @@ func (cc *CC) validateIter(ctxt context.Context, iter int, chainID string, bc co } //Validate test that was Run. Each successful iteration in the run is validated against -func (cc *CC) Validate(ctxt context.Context, chainID string, bc common.BroadcastClient, ec pb.EndorserClient, signer msp.SigningIdentity, wg *sync.WaitGroup) error { +func (cc *CCClient) Validate(ctxt context.Context, chainID string, bc common.BroadcastClient, ec pb.EndorserClient, signer msp.SigningIdentity, wg *sync.WaitGroup) error { defer wg.Done() //this will signal inner validators to get out via @@ -280,8 +300,10 @@ func (cc *CC) Validate(ctxt context.Context, chainID string, bc common.Broadcast //return the quit closure for validation within validateIter quitF := func() bool { return quit } + cc.numQueries = cc.shadowCC.GetNumQueries(cc.currentInvokeIter) + //try only till successful invoke iterations - for i := 0; i < cc.currentInvokeIter; i++ { + for i := 0; i < cc.numQueries; i++ { go func(iter int) { cc.validateIter(ctxt, iter, chainID, bc, ec, signer, &innerwg, quitF) }(i) @@ -317,22 +339,22 @@ func (cc *CC) Validate(ctxt context.Context, chainID string, bc common.Broadcast } //Report reports chaincode test execution, iter by iter -func (cc *CC) Report(verbose bool, chainID string) { +func (cc *CCClient) Report(verbose bool, chainID string) { fmt.Printf("%s/%s(%d)\n", cc.Name, chainID, cc.ID) - fmt.Printf("\tNum successful invokes: %d(%d)\n", cc.currentInvokeIter, cc.NumberOfIterations) + fmt.Printf("\tNum successful invokes: %d(%d,%d)\n", cc.currentInvokeIter, cc.NumberOfInvokes, cc.overriddenNumInvokes) if cc.invokeErr != nil { fmt.Printf("\tError on invoke: %s\n", cc.invokeErr) } //test to see if validate was called (validate alloc the arrays, one of which is queryWorked) if cc.queryWorked != nil { - for i := 0; i < cc.currentInvokeIter; i++ { + for i := 0; i < cc.numQueries; i++ { fmt.Printf("\tQuery(%d) : succeeded-%t, num trials-%d(%d), error if any(%s)\n", i, cc.queryWorked[i], cc.currQueryIter[i], cc.NumFinalQueryAttempts, cc.queryErrs[i]) } } else { fmt.Printf("\tQuery validation appears not have been performed(#invokes-%d). timed out ?\n", cc.currentInvokeIter) } //total actual time for cc.currentInvokeIter - invokeTime := (cc.invokeEndTime-cc.invokeStartTime)*1000 - (cc.DelayBetweenInvokeMs * (cc.currentInvokeIter - 1)) + invokeTime := cc.invokeEndTime - cc.invokeStartTime - int64(cc.DelayBetweenInvokeMs*(cc.currentInvokeIter-1)) fmt.Printf("\tTime for invokes(ms): %d\n", invokeTime) fmt.Printf("\tFinal query worked ? %t\n", cc.queryWorked) diff --git a/examples/ccchecker/chaincodes/newkeyperinvoke/shadow/newkeyperinvoke.go b/examples/ccchecker/chaincodes/newkeyperinvoke/shadow/newkeyperinvoke.go index 74ff80155a5..1c88ddff686 100644 --- a/examples/ccchecker/chaincodes/newkeyperinvoke/shadow/newkeyperinvoke.go +++ b/examples/ccchecker/chaincodes/newkeyperinvoke/shadow/newkeyperinvoke.go @@ -33,15 +33,15 @@ type NewKeyPerInvoke struct { //---------- implements ShadowCCIntf functions ------- //InitShadowCC initializes CC -func (t *NewKeyPerInvoke) InitShadowCC() { +func (t *NewKeyPerInvoke) InitShadowCC(initArgs []string) { t.state = make(map[string][]byte) } -//setState sets the state -func (t *NewKeyPerInvoke) setState(key []byte, val []byte) { +//invokeSuccessful sets the state and increments succefull invokes counter +func (t *NewKeyPerInvoke) invokeSuccessful(key []byte, val []byte) { t.Lock() + defer t.Unlock() t.state[string(key)] = val - t.Unlock() } //getState gets the state @@ -52,6 +52,18 @@ func (t *NewKeyPerInvoke) getState(key []byte) ([]byte, bool) { return v, ok } +//OverrideNumInvokes returns the number of invokes shadow wants +//accept users request, no override +func (t *NewKeyPerInvoke) OverrideNumInvokes(numInvokesPlanned int) int { + return numInvokesPlanned +} + +//GetNumQueries returns the number of queries shadow wants ccchecked to do. +//For our purpose, just do as many queries as there were invokes for. +func (t *NewKeyPerInvoke) GetNumQueries(numInvokesCompletedSuccessfully int) int { + return numInvokesCompletedSuccessfully +} + //GetInvokeArgs get args for invoke based on chaincode ID and iteration num func (t *NewKeyPerInvoke) GetInvokeArgs(ccnum int, iter int) [][]byte { args := make([][]byte, 3) @@ -77,7 +89,7 @@ func (t *NewKeyPerInvoke) PostInvoke(args [][]byte, resp []byte) error { return fmt.Errorf("invalid response %s", string(resp)) } - t.setState(args[1], args[2]) + t.invokeSuccessful(args[1], args[2]) return nil } diff --git a/examples/ccchecker/chaincodes/registershadow.go b/examples/ccchecker/chaincodes/registershadow.go index b57d3d9a264..f7007dc58d4 100644 --- a/examples/ccchecker/chaincodes/registershadow.go +++ b/examples/ccchecker/chaincodes/registershadow.go @@ -28,25 +28,39 @@ var shadowCCs = map[string]ShadowCCIntf{ "github.com/hyperledger/fabric/examples/ccchecker/chaincodes/newkeyperinvoke": &nkpi.NewKeyPerInvoke{}, } -//RegisterCCs registers all possible chaincodes that can be used in test -func RegisterCCs(ccs []*CC) error { - inUse := make(map[string]ShadowCCIntf) +type shadowCCArgs struct { + initArgs []string +} + +//For each chaincode that's deployed corresdponds a shadowCC. Not all +//shadows in shadowCCs maybe used in a run using the input JSON. And each +//chaincode maybe used multiple times (either because Concurrency is > 1 or +//the chaincode is used multiple times in the JSON with different CCChecker +//parameters). inUseShadowCCs keeps a set of all shadow CCs in use +var inUseShadowCCs map[ShadowCCIntf]*shadowCCArgs + +//RegisterCCClients registers and maps chaincode clients to their shadows +func RegisterCCClients(ccs []*CCClient) error { + inUseShadowCCs = make(map[ShadowCCIntf]*shadowCCArgs) for _, cc := range ccs { scc, ok := shadowCCs[cc.Path] if !ok || scc == nil { return fmt.Errorf("%s not a registered chaincode", cc.Path) } - if _, ok := inUse[cc.Path]; !ok { - inUse[cc.Path] = scc - } + //setup the shadow chaincode to plug into the ccchecker framework cc.shadowCC = scc + + //add cc to the list in shadow cc + if _, ok := inUseShadowCCs[scc]; !ok { + inUseShadowCCs[scc] = &shadowCCArgs{cc.InitArgs} + } } //initialize a shadow chaincode just once. A chaincode may be used //multiple times in test run - for _, cc := range inUse { - cc.InitShadowCC() + for scc, sccArgs := range inUseShadowCCs { + scc.InitShadowCC(sccArgs.initArgs) } return nil