- package main
-
- import (
- "flag"
- "fmt"
- "os"
- "os/signal"
- "strings"
- "sync"
- "syscall"
- "time"
-
- "github.com/go-kit/kit/log/term"
-
- "github.com/tendermint/tendermint/libs/log"
- tmrpc "github.com/tendermint/tendermint/rpc/client"
- )
-
- var logger = log.NewNopLogger()
-
- func main() {
- var durationInt, txsRate, connections, txSize int
- var verbose bool
- var outputFormat, broadcastTxMethod string
-
- flagSet := flag.NewFlagSet("tm-bench", flag.ExitOnError)
- flagSet.IntVar(&connections, "c", 1, "Connections to keep open per endpoint")
- flagSet.IntVar(&durationInt, "T", 10, "Exit after the specified amount of time in seconds")
- flagSet.IntVar(&txsRate, "r", 1000, "Txs per second to send in a connection")
- flagSet.IntVar(&txSize, "s", 250, "The size of a transaction in bytes, must be greater than or equal to 40.")
- 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() {
- fmt.Println(`Tendermint blockchain benchmarking tool.
-
- Usage:
- tm-bench [-c 1] [-T 10] [-r 1000] [-s 250] [endpoints] [-output-format <plain|json> [-broadcast-tx-method <async|sync|commit>]]
-
- Examples:
- tm-bench localhost:26657`)
- fmt.Println("Flags:")
- flagSet.PrintDefaults()
- }
-
- flagSet.Parse(os.Args[1:])
-
- if flagSet.NArg() == 0 {
- flagSet.Usage()
- os.Exit(1)
- }
-
- if verbose {
- if outputFormat == "json" {
- printErrorAndExit("Verbose mode not supported with json output.")
- }
- // 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{}
- }
- logger = log.NewTMLoggerWithColorFn(log.NewSyncWriter(os.Stdout), colorFn)
-
- fmt.Printf("Running %ds test @ %s\n", durationInt, flagSet.Arg(0))
- }
-
- if txSize < 40 {
- printErrorAndExit("The size of a transaction must be greater than or equal to 40.")
- }
-
- if broadcastTxMethod != "async" &&
- broadcastTxMethod != "sync" &&
- broadcastTxMethod != "commit" {
- printErrorAndExit("broadcast-tx-method should be either 'sync', 'async' or 'commit'.")
- }
-
- var (
- endpoints = strings.Split(flagSet.Arg(0), ",")
- client = tmrpc.NewHTTP(endpoints[0], "/websocket")
- initialHeight = latestBlockHeight(client)
- )
- logger.Info("Latest block height", "h", initialHeight)
-
- transacters := startTransacters(
- endpoints,
- connections,
- txsRate,
- txSize,
- "broadcast_tx_"+broadcastTxMethod,
- )
-
- // Quit when interrupted or received SIGTERM.
- c := make(chan os.Signal, 1)
- signal.Notify(c, os.Interrupt, syscall.SIGTERM)
- go func() {
- for sig := range c {
- fmt.Printf("captured %v, exiting...\n", sig)
- for _, t := range transacters {
- t.Stop()
- }
- os.Exit(1)
- }
- }()
-
- // Wait until transacters have begun until we get the start time.
- timeStart := time.Now()
- logger.Info("Time last transacter started", "t", timeStart)
-
- duration := time.Duration(durationInt) * time.Second
-
- timeEnd := timeStart.Add(duration)
- logger.Info("End time for calculation", "t", timeEnd)
-
- <-time.After(duration)
- 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)
- }
- }
-
- logger.Debug("Time all transacters stopped", "t", time.Now())
-
- stats, err := calculateStatistics(
- client,
- initialHeight,
- timeStart,
- durationInt,
- )
- if err != nil {
- printErrorAndExit(err.Error())
- }
-
- printStatistics(stats, outputFormat)
- }
-
- 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
- }
-
- func countCrashes(crashes []bool) int {
- count := 0
- for i := 0; i < len(crashes); i++ {
- if crashes[i] {
- count++
- }
- }
- return count
- }
-
- func startTransacters(
- endpoints []string,
- connections,
- txsRate int,
- txSize int,
- broadcastTxMethod string,
- ) []*transacter {
- transacters := make([]*transacter, len(endpoints))
-
- wg := sync.WaitGroup{}
- wg.Add(len(endpoints))
- for i, e := range endpoints {
- t := newTransacter(e, connections, txsRate, txSize, broadcastTxMethod)
- t.SetLogger(logger)
- 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)
- }
- wg.Wait()
-
- return transacters
- }
-
- func printErrorAndExit(err string) {
- fmt.Fprintln(os.Stderr, err)
- os.Exit(1)
- }
|