Skip to content

Commit 0088222

Browse files
authored
Move high-throughput sample to go application (hyperledger#364)
Signed-off-by: Nikhil Gupta <[email protected]> high throughput app
1 parent e8d6e46 commit 0088222

25 files changed

+718
-180
lines changed

high-throughput/README.md

+57-27
Original file line numberDiff line numberDiff line change
@@ -107,56 +107,86 @@ Change back into the `high-throughput` directory in `fabic-samples`. Start the n
107107

108108
If successful, you will see messages of the Fabric test network being created and the chaincode being deployed, followed by the execution time of the script:
109109
```
110-
Total setup execution time : 141 secs ...
110+
Total setup execution time : 81 secs ...
111111
```
112112

113113
The `high-throughput` chaincode is now ready to receive invocations.
114114

115115
### Invoke the chaincode
116-
All invocations are provided as scripts in `scripts` folder. You can use these scripts to create and remove assets that you put on the ledger.
116+
117+
You can invoke the `high-througput` chaincode using a Go application in the `application-go` folder. The Go application will allow us to submit many transactions to the network concurrently. Navigate to the application:
118+
```
119+
cd application-go
120+
```
117121

118122
#### Update
119-
The format for update is: `./scripts/update-invoke.sh name value operation` where `name` is the name of the variable to update, `value` is the value to
120-
add to the variable, and `operation` is either `+` or `-` depending on what type of operation you'd like to add to the variable. In the future,
121-
multiply/divide operations will be supported (or add them yourself to the chaincode as an exercise!)
123+
The format for update is: `go run app.go update name value operation` where `name` is the name of the variable to update, `value` is the value to add to the variable, and `operation` is either `+` or `-` depending on what type of operation you'd like to add to the variable.
122124

123-
Example: `./scripts/update-invoke.sh myvar 100 +`
125+
Example: `go run app.go update myvar 100 +`
124126

125-
#### Get
126-
The format for get is: `./get-invoke.sh name` where `name` is the name of the variable to get.
127+
#### Query
128+
You can query the value of a variable by running `go run app.go get name` where `name` is the name of the variable to get.
127129

128-
Example: `./scripts/get-invoke.sh myvar`
130+
Example: `go run app.go get myvar`
129131

130132
#### Prune
131-
Pruning takes all the deltas generated for a variable and combines them all into a single row, deleting all previous rows. This helps cleanup
132-
the ledger when many updates have been performed.
133+
Pruning takes all the deltas generated for a variable and combines them all into a single row, deleting all previous rows. This helps cleanup the ledger when many updates have been performed.
133134

134-
The format for pruning is: `./scripts/prune-invoke.sh name` where `name` is the name of the variable to prune.
135+
The format for pruning is: `go run app.go prune name` where `name` is the name of the variable to prune.
135136

136-
Example: `./scripts/prune-invoke.sh myvar`
137+
Example: `go run app.go prune myvar`
137138

138139
#### Delete
139-
The format for delete is: `./delete-invoke.sh name` where `name` is the name of the variable to delete.
140+
The format for delete is: `go run app.go delete name` where `name` is the name of the variable to delete.
140141

141-
Example: `./scripts/delete-invoke.sh myvar`
142+
Example: `go run app.go delete myvar`
142143

143144
### Test the Network
144-
Two scripts are provided to show the advantage of using this system when running many parallel transactions at once: `many-updates.sh` and
145-
`many-updates-traditional.sh`. The first script accepts the same arguments as `update-invoke.sh` but duplicates the invocation 1000 times
146-
and in parallel. The final value, therefore, should be the given update value * 1000. Run this script to confirm that your network is functioning
147-
properly. You can confirm this by checking your peer and orderer logs and verifying that no invocations are rejected due to improper versions.
148145

149-
The second script, `many-updates-traditional.sh`, also sends 1000 transactions but using the traditional storage system. It'll update a single
150-
row in the ledger 1000 times, with a value incrementing by one each time (i.e. the first invocation sets it to 0 and the last to 1000). The
151-
expectation would be that the final value of the row is 999. However, the final value changes each time this script is run and you'll find
152-
errors in the peer and orderer logs.
146+
The application provides two methods that demonstrate the advantages of this system by submitting many concurrent transactions to the smart contract: `manyUpdates` and `manyUpdatesTraditional`. The first function accepts the same arguments as `update-invoke.sh` but runs the invocation 1000 times in parallel. The final value, therefore, should be the given update value * 1000.
153147

154-
There are two other scripts, `get-traditional.sh`, which simply gets the value of a row in the traditional way, with no deltas, and `del-traditional.sh` will delete an asset in the traditional way.
148+
The second function, `manyUpdatesTraditional`, submits 1000 transactions that attempt to upddate the same key in the world state 1000 times.
149+
150+
Run the following command to create and update `testvar1` a 1000 times:
151+
```
152+
go run app.go manyUpdates testvar1 100 +
153+
```
155154

156-
Examples:
157-
`./scripts/many-updates.sh testvar 100 +` --> final value from `./scripts/get-invoke.sh testvar` should be 100000
155+
The application will query the variable after submitting the transaction. The result should be `100000`.
156+
157+
We will now see what happens when you try to run 1000 concurrent updates using a traditional transaction. Run the following command to create a variable named `testvar2`:
158+
```
159+
go run app.go update testvar2 100 +
160+
```
161+
The variable will have a value of 100:
162+
```
163+
2020/10/27 18:01:45 Value of variable testvar2 : 100
164+
```
158165

159-
`./scripts/many-updates-traditional.sh testvar` --> final value from `./scripts/get-traditional.sh testvar` is undefined
166+
Now lets try to update `testvar2` 1000 times in parallel:
167+
```
168+
go run app.go manyUpdatesTraditional testvar2 100 +
169+
```
170+
171+
When the program ends, you may see that none of the updates succeeded.
172+
```
173+
2020/10/27 18:03:15 Final value of variable testvar2 : 100
174+
```
175+
176+
The transactions failed because multiple transactions in each block updated the same key. Because of these transactions generated read/write conflicts, the transactions included in each block were rejected in the validation stage.
177+
178+
You can can examine the peer logs to view the messages generated by the rejected blocks:
179+
180+
181+
`docker logs peer0.org1.example.com
182+
[...]
183+
2020-10-28 17:37:58.746 UTC [gossip.privdata] StoreBlock -> INFO 2190 [mychannel] Received block [407] from buffer
184+
2020-10-28 17:37:58.749 UTC [committer.txvalidator] Validate -> INFO 2191 [mychannel] Validated block [407] in 2ms
185+
2020-10-28 17:37:58.750 UTC [validation] validateAndPrepareBatch -> WARN 2192 Block [407] Transaction index [0] TxId [b6b14cf988b0d7d35d4e0d7a0d2ae0c9f5569bc10ec5010f03a28c22694b8ef6] marked as invalid by state validator. Reason code [MVCC_READ_CONFLICT]
186+
2020-10-28 17:37:58.750 UTC [validation] validateAndPrepareBatch -> WARN 2193 Block [407] Transaction index [1] TxId [9d7c4f6ff95a0f22e01d6ffeda261227752e78db43f2673ad4ea6f0fdace44d1] marked as invalid by state validator. Reason code [MVCC_READ_CONFLICT]
187+
2020-10-28 17:37:58.750 UTC [validation] validateAndPrepareBatch -> WARN 2194 Block [407] Transaction index [2] TxId [9cc228b61d8841208feb6160254aee098b1b3a903f645e62cfa12222e6f52e65] marked as invalid by state validator. Reason code [MVCC_READ_CONFLICT]
188+
2020-10-28 17:37:58.750 UTC [validation] validateAndPrepareBatch -> WARN 2195 Block [407] Transaction index [3] TxId [2ae78d363c30b5f3445f2b028ccac7cf821f1d5d5c256d8c17bd42f33178e2ed] marked as invalid by state validator. Reason code [MVCC_READ_CONFLICT]
189+
```
160190
161191
### Clean up
162192
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
wallet
2+
!wallet/.gitkeep
3+
4+
keystore

high-throughput/application-go/app.go

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
Copyright 2020 IBM All Rights Reserved.
3+
4+
SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
package main
8+
9+
import (
10+
"log"
11+
"os"
12+
13+
f "github.com/hyperledger/fabric-samples/high-throughput/application-go/functions"
14+
)
15+
16+
func main() {
17+
18+
var function, variableName, change, sign string
19+
20+
if len(os.Args) <= 2 {
21+
log.Println("Usage: function variableName")
22+
log.Fatalf("functions: update manyUpdates manyUpdatesTraditional get prune delete")
23+
} else if (os.Args[1] == "update" || os.Args[1] == "manyUpdates" || os.Args[1] == "manyUpdatesTraditional") && len(os.Args) < 5 {
24+
log.Fatalf("error: provide value and operation")
25+
} else if len(os.Args) == 3 {
26+
function = os.Args[1]
27+
variableName = os.Args[2]
28+
} else if len(os.Args) == 5 {
29+
function = os.Args[1]
30+
variableName = os.Args[2]
31+
change = os.Args[3]
32+
sign = os.Args[4]
33+
}
34+
35+
// Handle different functions
36+
if function == "update" {
37+
result, err := f.Update(function, variableName, change, sign)
38+
if err != nil {
39+
log.Fatalf("error: %v", err)
40+
}
41+
log.Println("Value of variable", string(variableName), ": ", string(result))
42+
43+
} else if function == "delete" || function == "prune" || function == "delstandard" {
44+
result, err := f.DeletePrune(function, variableName)
45+
if err != nil {
46+
log.Fatalf("error: %v", err)
47+
}
48+
log.Println(string(result))
49+
} else if function == "get" || function == "getstandard" {
50+
result, err := f.Query(function, variableName)
51+
if err != nil {
52+
log.Fatalf("error: %v", err)
53+
}
54+
log.Println("Value of variable", string(variableName), ": ", string(result))
55+
} else if function == "manyUpdates" {
56+
log.Println("submitting 1000 concurrent updates...")
57+
result, err := f.ManyUpdates("update", variableName, change, sign)
58+
if err != nil {
59+
log.Fatalf("error: %v", err)
60+
}
61+
log.Println("Final value of variable", string(variableName), ": ", string(result))
62+
} else if function == "manyUpdatesTraditional" {
63+
log.Println("submitting 1000 concurrent updates...")
64+
result, err := f.ManyUpdates("putstandard", variableName, change, sign)
65+
if err != nil {
66+
log.Fatalf("error: %v", err)
67+
}
68+
log.Println("Final value of variable", string(variableName), ": ", string(result))
69+
}
70+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
Copyright 2020 IBM All Rights Reserved.
3+
4+
SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
package functions
8+
9+
import (
10+
"fmt"
11+
"os"
12+
"path/filepath"
13+
14+
"github.com/hyperledger/fabric-sdk-go/pkg/core/config"
15+
"github.com/hyperledger/fabric-sdk-go/pkg/gateway"
16+
)
17+
18+
// DeletePrune deletes or prunes a variable
19+
func DeletePrune(function, variableName string) ([]byte, error) {
20+
21+
err := os.Setenv("DISCOVERY_AS_LOCALHOST", "true")
22+
if err != nil {
23+
return nil, fmt.Errorf("error setting DISCOVERY_AS_LOCALHOST environemnt variable: %v", err)
24+
}
25+
26+
wallet, err := gateway.NewFileSystemWallet("wallet")
27+
if err != nil {
28+
return nil, fmt.Errorf("failed to create wallet: %v", err)
29+
}
30+
31+
if !wallet.Exists("appUser") {
32+
err := populateWallet(wallet)
33+
if err != nil {
34+
return nil, fmt.Errorf("failed to populate wallet contents: %v", err)
35+
}
36+
}
37+
38+
ccpPath := filepath.Join(
39+
"..",
40+
"..",
41+
"test-network",
42+
"organizations",
43+
"peerOrganizations",
44+
"org1.example.com",
45+
"connection-org1.yaml",
46+
)
47+
48+
gw, err := gateway.Connect(
49+
gateway.WithConfig(config.FromFile(filepath.Clean(ccpPath))),
50+
gateway.WithIdentity(wallet, "appUser"),
51+
)
52+
if err != nil {
53+
return nil, fmt.Errorf("failed to connect to gateway: %v", err)
54+
}
55+
defer gw.Close()
56+
57+
network, err := gw.GetNetwork("mychannel")
58+
if err != nil {
59+
return nil, fmt.Errorf("failed to get network: %v", err)
60+
}
61+
62+
contract := network.GetContract("bigdatacc")
63+
64+
result, err := contract.SubmitTransaction(function, variableName)
65+
if err != nil {
66+
return result, fmt.Errorf("failed to Submit transaction: %v", err)
67+
}
68+
return result, err
69+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
Copyright 2020 IBM All Rights Reserved.
3+
4+
SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
package functions
8+
9+
import (
10+
"fmt"
11+
"os"
12+
"path/filepath"
13+
"sync"
14+
15+
"github.com/hyperledger/fabric-sdk-go/pkg/core/config"
16+
"github.com/hyperledger/fabric-sdk-go/pkg/gateway"
17+
)
18+
19+
// ManyUpdates allows you to push many cuncurrent updates to a variable
20+
func ManyUpdates(function, variableName, change, sign string) ([]byte, error) {
21+
22+
err := os.Setenv("DISCOVERY_AS_LOCALHOST", "true")
23+
if err != nil {
24+
return nil, fmt.Errorf("error setting DISCOVERY_AS_LOCALHOST environemnt variable: %v", err)
25+
}
26+
27+
wallet, err := gateway.NewFileSystemWallet("wallet")
28+
if err != nil {
29+
return nil, fmt.Errorf("failed to create wallet: %v", err)
30+
}
31+
32+
if !wallet.Exists("appUser") {
33+
err := populateWallet(wallet)
34+
if err != nil {
35+
return nil, fmt.Errorf("failed to populate wallet contents: %v", err)
36+
}
37+
}
38+
39+
ccpPath := filepath.Join(
40+
"..",
41+
"..",
42+
"test-network",
43+
"organizations",
44+
"peerOrganizations",
45+
"org1.example.com",
46+
"connection-org1.yaml",
47+
)
48+
49+
gw, err := gateway.Connect(
50+
gateway.WithConfig(config.FromFile(filepath.Clean(ccpPath))),
51+
gateway.WithIdentity(wallet, "appUser"),
52+
)
53+
if err != nil {
54+
return nil, fmt.Errorf("failed to connect to gateway: %v", err)
55+
}
56+
defer gw.Close()
57+
58+
network, err := gw.GetNetwork("mychannel")
59+
if err != nil {
60+
return nil, fmt.Errorf("failed to get network: %v", err)
61+
}
62+
63+
contract := network.GetContract("bigdatacc")
64+
65+
var wg sync.WaitGroup
66+
67+
for i := 0; i < 1000; i++ {
68+
wg.Add(1)
69+
go func() ([]byte, error) {
70+
defer wg.Done()
71+
result, err := contract.SubmitTransaction(function, variableName, change, sign)
72+
if err != nil {
73+
return result, fmt.Errorf("failed to evaluate transaction: %v", err)
74+
}
75+
return result, nil
76+
}()
77+
}
78+
79+
wg.Wait()
80+
81+
result, err := contract.EvaluateTransaction("get", variableName)
82+
if err != nil {
83+
return nil, fmt.Errorf("failed to evaluate transaction: %v", err)
84+
}
85+
return result, err
86+
}

0 commit comments

Comments
 (0)