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.

172 lines
4.1 KiB

  1. package main
  2. import (
  3. "flag"
  4. "fmt"
  5. "os"
  6. "strings"
  7. "text/tabwriter"
  8. "time"
  9. "github.com/go-kit/kit/log/term"
  10. metrics "github.com/rcrowley/go-metrics"
  11. tmtypes "github.com/tendermint/tendermint/types"
  12. "github.com/tendermint/tmlibs/log"
  13. "github.com/tendermint/tools/tm-monitor/monitor"
  14. )
  15. var version = "0.2.1"
  16. var logger = log.NewNopLogger()
  17. type statistics struct {
  18. BlockTimeSample metrics.Histogram
  19. TxThroughputSample metrics.Histogram
  20. BlockLatency metrics.Histogram
  21. }
  22. func main() {
  23. var duration, txsRate, connections int
  24. var verbose bool
  25. flag.IntVar(&connections, "c", 1, "Connections to keep open per endpoint")
  26. flag.IntVar(&duration, "T", 10, "Exit after the specified amount of time in seconds")
  27. flag.IntVar(&txsRate, "r", 1000, "Txs per second to send in a connection")
  28. flag.BoolVar(&verbose, "v", false, "Verbose output")
  29. flag.Usage = func() {
  30. fmt.Println(`Tendermint blockchain benchmarking tool.
  31. Usage:
  32. tm-bench [-c 1] [-T 10] [-r 1000] [endpoints]
  33. Examples:
  34. tm-bench localhost:46657`)
  35. fmt.Println("Flags:")
  36. flag.PrintDefaults()
  37. }
  38. flag.Parse()
  39. if flag.NArg() == 0 {
  40. flag.Usage()
  41. os.Exit(1)
  42. }
  43. if verbose {
  44. // Color errors red
  45. colorFn := func(keyvals ...interface{}) term.FgBgColor {
  46. for i := 1; i < len(keyvals); i += 2 {
  47. if _, ok := keyvals[i].(error); ok {
  48. return term.FgBgColor{Fg: term.White, Bg: term.Red}
  49. }
  50. }
  51. return term.FgBgColor{}
  52. }
  53. logger = log.NewTMLoggerWithColorFn(log.NewSyncWriter(os.Stdout), colorFn)
  54. }
  55. fmt.Printf("Running %ds test @ %s\n", duration, flag.Arg(0))
  56. endpoints := strings.Split(flag.Arg(0), ",")
  57. blockCh := make(chan tmtypes.Header, 100)
  58. blockLatencyCh := make(chan float64, 100)
  59. nodes := startNodes(endpoints, blockCh, blockLatencyCh)
  60. transacters := startTransacters(endpoints, connections, txsRate)
  61. stats := &statistics{
  62. BlockTimeSample: metrics.NewHistogram(metrics.NewUniformSample(1000)),
  63. TxThroughputSample: metrics.NewHistogram(metrics.NewUniformSample(1000)),
  64. BlockLatency: metrics.NewHistogram(metrics.NewUniformSample(1000)),
  65. }
  66. lastBlockHeight := -1
  67. durationTimer := time.After(time.Duration(duration) * time.Second)
  68. ticker := time.NewTicker(1 * time.Second)
  69. var blocks, txs int
  70. for {
  71. select {
  72. case b := <-blockCh:
  73. if lastBlockHeight < b.Height {
  74. blocks++
  75. txs += b.NumTxs
  76. lastBlockHeight = b.Height
  77. }
  78. case l := <-blockLatencyCh:
  79. stats.BlockLatency.Update(int64(l))
  80. case <-ticker.C:
  81. stats.BlockTimeSample.Update(int64(blocks))
  82. stats.TxThroughputSample.Update(int64(txs))
  83. blocks = 0
  84. txs = 0
  85. case <-durationTimer:
  86. for _, t := range transacters {
  87. t.Stop()
  88. }
  89. printStatistics(stats)
  90. for _, n := range nodes {
  91. n.Stop()
  92. }
  93. return
  94. }
  95. }
  96. }
  97. func startNodes(endpoints []string, blockCh chan<- tmtypes.Header, blockLatencyCh chan<- float64) []*monitor.Node {
  98. nodes := make([]*monitor.Node, len(endpoints))
  99. for i, e := range endpoints {
  100. n := monitor.NewNode(e)
  101. n.SetLogger(logger.With("node", e))
  102. n.SendBlocksTo(blockCh)
  103. n.SendBlockLatenciesTo(blockLatencyCh)
  104. if err := n.Start(); err != nil {
  105. fmt.Println(err)
  106. os.Exit(1)
  107. }
  108. nodes[i] = n
  109. }
  110. return nodes
  111. }
  112. func startTransacters(endpoints []string, connections int, txsRate int) []*transacter {
  113. transacters := make([]*transacter, len(endpoints))
  114. for i, e := range endpoints {
  115. t := newTransacter(e, connections, txsRate)
  116. t.SetLogger(logger)
  117. if err := t.Start(); err != nil {
  118. fmt.Println(err)
  119. os.Exit(1)
  120. }
  121. transacters[i] = t
  122. }
  123. return transacters
  124. }
  125. func printStatistics(stats *statistics) {
  126. w := tabwriter.NewWriter(os.Stdout, 0, 0, 5, ' ', 0)
  127. fmt.Fprintln(w, "Stats\tAvg\tStdev\tMax\t")
  128. fmt.Fprintln(w, fmt.Sprintf("Block latency\t%.2fms\t%.2fms\t%dms\t",
  129. stats.BlockLatency.Mean()/1000000.0,
  130. stats.BlockLatency.StdDev()/1000000.0,
  131. stats.BlockLatency.Max()/1000000))
  132. fmt.Fprintln(w, fmt.Sprintf("Blocks/sec\t%.3f\t%.3f\t%d\t",
  133. stats.BlockTimeSample.Mean(),
  134. stats.BlockTimeSample.StdDev(),
  135. stats.BlockTimeSample.Max()))
  136. fmt.Fprintln(w, fmt.Sprintf("Txs/sec\t%.0f\t%.0f\t%d\t",
  137. stats.TxThroughputSample.Mean(),
  138. stats.TxThroughputSample.StdDev(),
  139. stats.TxThroughputSample.Max()))
  140. w.Flush()
  141. }