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.

183 lines
4.4 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, must be greater than or equal to 40.")
  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 txSize < 40 {
  58. fmt.Fprintln(
  59. os.Stderr,
  60. "The size of a transaction must be greater than or equal to 40.",
  61. )
  62. os.Exit(1)
  63. }
  64. if broadcastTxMethod != "async" &&
  65. broadcastTxMethod != "sync" &&
  66. broadcastTxMethod != "commit" {
  67. fmt.Fprintln(
  68. os.Stderr,
  69. "broadcast-tx-method should be either 'sync', 'async' or 'commit'.",
  70. )
  71. os.Exit(1)
  72. }
  73. var (
  74. endpoints = strings.Split(flagSet.Arg(0), ",")
  75. client = tmrpc.NewHTTP(endpoints[0], "/websocket")
  76. initialHeight = latestBlockHeight(client)
  77. )
  78. logger.Info("Latest block height", "h", initialHeight)
  79. transacters := startTransacters(
  80. endpoints,
  81. connections,
  82. txsRate,
  83. txSize,
  84. "broadcast_tx_"+broadcastTxMethod,
  85. )
  86. // Wait until transacters have begun until we get the start time
  87. timeStart := time.Now()
  88. logger.Info("Time last transacter started", "t", timeStart)
  89. duration := time.Duration(durationInt) * time.Second
  90. timeEnd := timeStart.Add(duration)
  91. logger.Info("End time for calculation", "t", timeEnd)
  92. <-time.After(duration)
  93. for i, t := range transacters {
  94. t.Stop()
  95. numCrashes := countCrashes(t.connsBroken)
  96. if numCrashes != 0 {
  97. fmt.Printf("%d connections crashed on transacter #%d\n", numCrashes, i)
  98. }
  99. }
  100. logger.Debug("Time all transacters stopped", "t", time.Now())
  101. stats, err := calculateStatistics(
  102. client,
  103. initialHeight,
  104. timeStart,
  105. durationInt,
  106. )
  107. if err != nil {
  108. fmt.Fprintln(os.Stderr, err)
  109. os.Exit(1)
  110. }
  111. printStatistics(stats, outputFormat)
  112. }
  113. func latestBlockHeight(client tmrpc.Client) int64 {
  114. status, err := client.Status()
  115. if err != nil {
  116. fmt.Fprintln(os.Stderr, err)
  117. os.Exit(1)
  118. }
  119. return status.SyncInfo.LatestBlockHeight
  120. }
  121. func countCrashes(crashes []bool) int {
  122. count := 0
  123. for i := 0; i < len(crashes); i++ {
  124. if crashes[i] {
  125. count++
  126. }
  127. }
  128. return count
  129. }
  130. func startTransacters(
  131. endpoints []string,
  132. connections,
  133. txsRate int,
  134. txSize int,
  135. broadcastTxMethod string,
  136. ) []*transacter {
  137. transacters := make([]*transacter, len(endpoints))
  138. wg := sync.WaitGroup{}
  139. wg.Add(len(endpoints))
  140. for i, e := range endpoints {
  141. t := newTransacter(e, connections, txsRate, txSize, broadcastTxMethod)
  142. t.SetLogger(logger)
  143. go func(i int) {
  144. defer wg.Done()
  145. if err := t.Start(); err != nil {
  146. fmt.Fprintln(os.Stderr, err)
  147. os.Exit(1)
  148. }
  149. transacters[i] = t
  150. }(i)
  151. }
  152. wg.Wait()
  153. return transacters
  154. }