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.

173 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.3.0"
  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 := int64(-1)
  67. durationTimer := time.After(time.Duration(duration) * time.Second)
  68. ticker := time.NewTicker(1 * time.Second)
  69. var blocks int
  70. var txs int64
  71. for {
  72. select {
  73. case b := <-blockCh:
  74. if lastBlockHeight < b.Height {
  75. blocks++
  76. txs += b.NumTxs
  77. lastBlockHeight = b.Height
  78. }
  79. case l := <-blockLatencyCh:
  80. stats.BlockLatency.Update(int64(l))
  81. case <-ticker.C:
  82. stats.BlockTimeSample.Update(int64(blocks))
  83. stats.TxThroughputSample.Update(txs)
  84. blocks = 0
  85. txs = 0
  86. case <-durationTimer:
  87. for _, t := range transacters {
  88. t.Stop()
  89. }
  90. printStatistics(stats)
  91. for _, n := range nodes {
  92. n.Stop()
  93. }
  94. return
  95. }
  96. }
  97. }
  98. func startNodes(endpoints []string, blockCh chan<- tmtypes.Header, blockLatencyCh chan<- float64) []*monitor.Node {
  99. nodes := make([]*monitor.Node, len(endpoints))
  100. for i, e := range endpoints {
  101. n := monitor.NewNode(e)
  102. n.SetLogger(logger.With("node", e))
  103. n.SendBlocksTo(blockCh)
  104. n.SendBlockLatenciesTo(blockLatencyCh)
  105. if err := n.Start(); err != nil {
  106. fmt.Println(err)
  107. os.Exit(1)
  108. }
  109. nodes[i] = n
  110. }
  111. return nodes
  112. }
  113. func startTransacters(endpoints []string, connections int, txsRate int) []*transacter {
  114. transacters := make([]*transacter, len(endpoints))
  115. for i, e := range endpoints {
  116. t := newTransacter(e, connections, txsRate)
  117. t.SetLogger(logger)
  118. if err := t.Start(); err != nil {
  119. fmt.Println(err)
  120. os.Exit(1)
  121. }
  122. transacters[i] = t
  123. }
  124. return transacters
  125. }
  126. func printStatistics(stats *statistics) {
  127. w := tabwriter.NewWriter(os.Stdout, 0, 0, 5, ' ', 0)
  128. fmt.Fprintln(w, "Stats\tAvg\tStdev\tMax\t")
  129. fmt.Fprintln(w, fmt.Sprintf("Block latency\t%.2fms\t%.2fms\t%dms\t",
  130. stats.BlockLatency.Mean()/1000000.0,
  131. stats.BlockLatency.StdDev()/1000000.0,
  132. stats.BlockLatency.Max()/1000000))
  133. fmt.Fprintln(w, fmt.Sprintf("Blocks/sec\t%.3f\t%.3f\t%d\t",
  134. stats.BlockTimeSample.Mean(),
  135. stats.BlockTimeSample.StdDev(),
  136. stats.BlockTimeSample.Max()))
  137. fmt.Fprintln(w, fmt.Sprintf("Txs/sec\t%.0f\t%.0f\t%d\t",
  138. stats.TxThroughputSample.Mean(),
  139. stats.TxThroughputSample.StdDev(),
  140. stats.TxThroughputSample.Max()))
  141. w.Flush()
  142. }