You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

175 lines
4.2 KiB

  1. package main
  2. import (
  3. "flag"
  4. "fmt"
  5. "os"
  6. "strings"
  7. "sync"
  8. "time"
  9. "github.com/go-kit/kit/log/term"
  10. "github.com/tendermint/tendermint/libs/log"
  11. tmrpc "github.com/tendermint/tendermint/rpc/client"
  12. )
  13. var logger = log.NewNopLogger()
  14. func main() {
  15. var durationInt, txsRate, connections, txSize int
  16. var verbose bool
  17. var outputFormat, broadcastTxMethod string
  18. flagSet := flag.NewFlagSet("tm-bench", flag.ExitOnError)
  19. flagSet.IntVar(&connections, "c", 1, "Connections to keep open per endpoint")
  20. flagSet.IntVar(&durationInt, "T", 10, "Exit after the specified amount of time in seconds")
  21. flagSet.IntVar(&txsRate, "r", 1000, "Txs per second to send in a connection")
  22. flagSet.IntVar(&txSize, "s", 250, "The size of a transaction in bytes.")
  23. flagSet.StringVar(&outputFormat, "output-format", "plain", "Output format: plain or json")
  24. 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)")
  25. flagSet.BoolVar(&verbose, "v", false, "Verbose output")
  26. flagSet.Usage = func() {
  27. fmt.Println(`Tendermint blockchain benchmarking tool.
  28. Usage:
  29. tm-bench [-c 1] [-T 10] [-r 1000] [-s 250] [endpoints] [-output-format <plain|json> [-broadcast-tx-method <async|sync|commit>]]
  30. Examples:
  31. tm-bench localhost:26657`)
  32. fmt.Println("Flags:")
  33. flagSet.PrintDefaults()
  34. }
  35. flagSet.Parse(os.Args[1:])
  36. if flagSet.NArg() == 0 {
  37. flagSet.Usage()
  38. os.Exit(1)
  39. }
  40. if verbose {
  41. if outputFormat == "json" {
  42. fmt.Fprintln(os.Stderr, "Verbose mode not supported with json output.")
  43. os.Exit(1)
  44. }
  45. // Color errors red
  46. colorFn := func(keyvals ...interface{}) term.FgBgColor {
  47. for i := 1; i < len(keyvals); i += 2 {
  48. if _, ok := keyvals[i].(error); ok {
  49. return term.FgBgColor{Fg: term.White, Bg: term.Red}
  50. }
  51. }
  52. return term.FgBgColor{}
  53. }
  54. logger = log.NewTMLoggerWithColorFn(log.NewSyncWriter(os.Stdout), colorFn)
  55. fmt.Printf("Running %ds test @ %s\n", durationInt, flagSet.Arg(0))
  56. }
  57. if broadcastTxMethod != "async" &&
  58. broadcastTxMethod != "sync" &&
  59. broadcastTxMethod != "commit" {
  60. fmt.Fprintln(
  61. os.Stderr,
  62. "broadcast-tx-method should be either 'sync', 'async' or 'commit'.",
  63. )
  64. os.Exit(1)
  65. }
  66. var (
  67. endpoints = strings.Split(flagSet.Arg(0), ",")
  68. client = tmrpc.NewHTTP(endpoints[0], "/websocket")
  69. initialHeight = latestBlockHeight(client)
  70. )
  71. logger.Info("Latest block height", "h", initialHeight)
  72. transacters := startTransacters(
  73. endpoints,
  74. connections,
  75. txsRate,
  76. txSize,
  77. "broadcast_tx_"+broadcastTxMethod,
  78. )
  79. // Wait until transacters have begun until we get the start time
  80. timeStart := time.Now()
  81. logger.Info("Time last transacter started", "t", timeStart)
  82. duration := time.Duration(durationInt) * time.Second
  83. timeEnd := timeStart.Add(duration)
  84. logger.Info("End time for calculation", "t", timeEnd)
  85. <-time.After(duration)
  86. for i, t := range transacters {
  87. t.Stop()
  88. numCrashes := countCrashes(t.connsBroken)
  89. if numCrashes != 0 {
  90. fmt.Printf("%d connections crashed on transacter #%d\n", numCrashes, i)
  91. }
  92. }
  93. logger.Debug("Time all transacters stopped", "t", time.Now())
  94. stats, err := calculateStatistics(
  95. client,
  96. initialHeight,
  97. timeStart,
  98. durationInt,
  99. )
  100. if err != nil {
  101. fmt.Fprintln(os.Stderr, err)
  102. os.Exit(1)
  103. }
  104. printStatistics(stats, outputFormat)
  105. }
  106. func latestBlockHeight(client tmrpc.Client) int64 {
  107. status, err := client.Status()
  108. if err != nil {
  109. fmt.Fprintln(os.Stderr, err)
  110. os.Exit(1)
  111. }
  112. return status.SyncInfo.LatestBlockHeight
  113. }
  114. func countCrashes(crashes []bool) int {
  115. count := 0
  116. for i := 0; i < len(crashes); i++ {
  117. if crashes[i] {
  118. count++
  119. }
  120. }
  121. return count
  122. }
  123. func startTransacters(
  124. endpoints []string,
  125. connections,
  126. txsRate int,
  127. txSize int,
  128. broadcastTxMethod string,
  129. ) []*transacter {
  130. transacters := make([]*transacter, len(endpoints))
  131. wg := sync.WaitGroup{}
  132. wg.Add(len(endpoints))
  133. for i, e := range endpoints {
  134. t := newTransacter(e, connections, txsRate, txSize, broadcastTxMethod)
  135. t.SetLogger(logger)
  136. go func(i int) {
  137. defer wg.Done()
  138. if err := t.Start(); err != nil {
  139. fmt.Fprintln(os.Stderr, err)
  140. os.Exit(1)
  141. }
  142. transacters[i] = t
  143. }(i)
  144. }
  145. wg.Wait()
  146. return transacters
  147. }