2017-03-16 20:53:09 +04:00
package main
2017-03-13 19:10:51 +04:00
import (
"flag"
"fmt"
"os"
"strings"
2018-07-12 15:37:46 -07:00
"sync"
2017-03-13 19:10:51 +04:00
"time"
2017-03-23 16:13:27 +04:00
"github.com/go-kit/kit/log/term"
2018-06-23 01:57:50 +02:00
2018-07-10 16:42:27 -04:00
"github.com/tendermint/tendermint/libs/log"
tmrpc "github.com/tendermint/tendermint/rpc/client"
2017-03-13 19:10:51 +04:00
)
2017-03-23 16:13:27 +04:00
var logger = log . NewNopLogger ( )
2017-03-13 19:10:51 +04:00
func main ( ) {
2018-07-12 12:59:40 -07:00
var durationInt , txsRate , connections , txSize int
2017-03-23 16:13:27 +04:00
var verbose bool
2018-05-04 16:35:39 +04:00
var outputFormat , broadcastTxMethod string
2017-03-13 19:10:51 +04:00
2018-07-10 22:54:59 -07:00
flagSet := flag . NewFlagSet ( "tm-bench" , flag . ExitOnError )
flagSet . IntVar ( & connections , "c" , 1 , "Connections to keep open per endpoint" )
2018-07-12 12:59:40 -07:00
flagSet . IntVar ( & durationInt , "T" , 10 , "Exit after the specified amount of time in seconds" )
2018-07-10 22:54:59 -07:00
flagSet . IntVar ( & txsRate , "r" , 1000 , "Txs per second to send in a connection" )
2018-09-17 17:08:47 +08:00
flagSet . IntVar ( & txSize , "s" , 250 , "The size of a transaction in bytes, must be greater than or equal to 40." )
2018-07-10 22:54:59 -07:00
flagSet . StringVar ( & outputFormat , "output-format" , "plain" , "Output format: plain or json" )
flagSet . StringVar ( & broadcastTxMethod , "broadcast-tx-method" , "async" , "Broadcast method: async (no guarantees; fastest), sync (ensures tx is checked) or commit (ensures tx is checked and committed; slowest)" )
flagSet . BoolVar ( & verbose , "v" , false , "Verbose output" )
flagSet . Usage = func ( ) {
2017-03-17 01:12:37 +04:00
fmt . Println ( ` Tendermint blockchain benchmarking tool .
2017-03-13 19:10:51 +04:00
Usage :
2018-07-11 21:54:46 -07:00
tm - bench [ - c 1 ] [ - T 10 ] [ - r 1000 ] [ - s 250 ] [ endpoints ] [ - output - format < plain | json > [ - broadcast - tx - method < async | sync | commit > ] ]
2017-03-13 19:10:51 +04:00
Examples :
2018-06-22 11:55:07 -04:00
tm - bench localhost : 26657 ` )
2017-03-13 19:10:51 +04:00
fmt . Println ( "Flags:" )
2018-07-10 22:54:59 -07:00
flagSet . PrintDefaults ( )
2017-03-13 19:10:51 +04:00
}
2018-07-10 22:54:59 -07:00
flagSet . Parse ( os . Args [ 1 : ] )
2017-03-13 19:10:51 +04:00
2018-07-10 22:54:59 -07:00
if flagSet . NArg ( ) == 0 {
flagSet . Usage ( )
2017-03-13 19:10:51 +04:00
os . Exit ( 1 )
}
2017-03-23 16:13:27 +04:00
if verbose {
2018-05-03 05:08:19 -04:00
if outputFormat == "json" {
2018-05-04 16:13:42 +04:00
fmt . Fprintln ( os . Stderr , "Verbose mode not supported with json output." )
2018-05-03 05:08:19 -04:00
os . Exit ( 1 )
}
2017-03-23 16:13:27 +04:00
// Color errors red
colorFn := func ( keyvals ... interface { } ) term . FgBgColor {
for i := 1 ; i < len ( keyvals ) ; i += 2 {
if _ , ok := keyvals [ i ] . ( error ) ; ok {
return term . FgBgColor { Fg : term . White , Bg : term . Red }
}
}
return term . FgBgColor { }
}
2017-07-28 18:13:39 -04:00
logger = log . NewTMLoggerWithColorFn ( log . NewSyncWriter ( os . Stdout ) , colorFn )
2017-03-23 16:13:27 +04:00
2018-07-12 12:59:40 -07:00
fmt . Printf ( "Running %ds test @ %s\n" , durationInt , flagSet . Arg ( 0 ) )
2018-05-03 05:08:19 -04:00
}
2017-03-13 19:10:51 +04:00
2018-09-17 17:08:47 +08:00
if txSize < 40 {
fmt . Fprintln (
os . Stderr ,
"The size of a transaction must be greater than or equal to 40." ,
)
os . Exit ( 1 )
}
2018-06-23 01:57:50 +02:00
if broadcastTxMethod != "async" &&
broadcastTxMethod != "sync" &&
broadcastTxMethod != "commit" {
fmt . Fprintln (
os . Stderr ,
"broadcast-tx-method should be either 'sync', 'async' or 'commit'." ,
)
2018-05-04 16:35:39 +04:00
os . Exit ( 1 )
}
2018-06-23 01:57:50 +02:00
var (
2018-07-10 22:54:59 -07:00
endpoints = strings . Split ( flagSet . Arg ( 0 ) , "," )
2018-06-23 01:57:50 +02:00
client = tmrpc . NewHTTP ( endpoints [ 0 ] , "/websocket" )
initialHeight = latestBlockHeight ( client )
)
2018-06-22 16:16:27 -07:00
logger . Info ( "Latest block height" , "h" , initialHeight )
2018-05-04 16:13:42 +04:00
2018-06-23 01:57:50 +02:00
transacters := startTransacters (
endpoints ,
connections ,
txsRate ,
2018-06-22 18:22:30 -07:00
txSize ,
2018-06-23 01:57:50 +02:00
"broadcast_tx_" + broadcastTxMethod ,
)
2018-07-11 21:54:46 -07:00
2018-07-11 22:55:20 -07:00
// Wait until transacters have begun until we get the start time
2018-07-11 21:54:46 -07:00
timeStart := time . Now ( )
logger . Info ( "Time last transacter started" , "t" , timeStart )
2018-07-12 12:59:40 -07:00
duration := time . Duration ( durationInt ) * time . Second
2018-05-04 16:13:42 +04:00
2018-07-12 12:59:40 -07:00
timeEnd := timeStart . Add ( duration )
logger . Info ( "End time for calculation" , "t" , timeEnd )
<- time . After ( duration )
2018-07-10 16:42:27 -04:00
for i , t := range transacters {
t . Stop ( )
numCrashes := countCrashes ( t . connsBroken )
if numCrashes != 0 {
fmt . Printf ( "%d connections crashed on transacter #%d\n" , numCrashes , i )
2018-06-23 01:57:50 +02:00
}
2018-07-10 16:42:27 -04:00
}
2018-05-04 16:13:42 +04:00
2018-07-12 12:59:40 -07:00
logger . Debug ( "Time all transacters stopped" , "t" , time . Now ( ) )
2018-05-04 16:13:42 +04:00
2018-07-10 16:42:27 -04:00
stats , err := calculateStatistics (
client ,
initialHeight ,
timeStart ,
2018-07-12 12:59:40 -07:00
durationInt ,
2018-07-10 16:42:27 -04:00
)
if err != nil {
fmt . Fprintln ( os . Stderr , err )
os . Exit ( 1 )
2018-05-04 16:13:42 +04:00
}
2018-07-10 16:42:27 -04:00
printStatistics ( stats , outputFormat )
2018-05-04 16:13:42 +04:00
}
func latestBlockHeight ( client tmrpc . Client ) int64 {
status , err := client . Status ( )
if err != nil {
fmt . Fprintln ( os . Stderr , err )
os . Exit ( 1 )
}
return status . SyncInfo . LatestBlockHeight
}
2018-07-02 03:19:51 -07:00
func countCrashes ( crashes [ ] bool ) int {
count := 0
for i := 0 ; i < len ( crashes ) ; i ++ {
if crashes [ i ] {
count ++
}
}
return count
}
2018-06-23 01:57:50 +02:00
func startTransacters (
endpoints [ ] string ,
connections ,
txsRate int ,
2018-06-22 18:22:30 -07:00
txSize int ,
2018-06-23 01:57:50 +02:00
broadcastTxMethod string ,
) [ ] * transacter {
2017-03-17 00:56:22 +04:00
transacters := make ( [ ] * transacter , len ( endpoints ) )
2018-07-12 15:37:46 -07:00
wg := sync . WaitGroup { }
wg . Add ( len ( endpoints ) )
2017-03-17 00:56:22 +04:00
for i , e := range endpoints {
2018-06-22 18:22:30 -07:00
t := newTransacter ( e , connections , txsRate , txSize , broadcastTxMethod )
2017-03-23 16:13:27 +04:00
t . SetLogger ( logger )
2018-07-12 15:37:46 -07:00
go func ( i int ) {
defer wg . Done ( )
if err := t . Start ( ) ; err != nil {
fmt . Fprintln ( os . Stderr , err )
os . Exit ( 1 )
}
transacters [ i ] = t
} ( i )
2018-07-12 12:42:26 -07:00
}
2018-07-12 15:37:46 -07:00
wg . Wait ( )
2018-07-12 12:42:26 -07:00
2017-03-17 00:56:22 +04:00
return transacters
2017-03-13 19:10:51 +04:00
}