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.

150 lines
3.9 KiB

  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "math"
  6. "os"
  7. "text/tabwriter"
  8. "time"
  9. metrics "github.com/rcrowley/go-metrics"
  10. tmrpc "github.com/tendermint/tendermint/rpc/client"
  11. "github.com/tendermint/tendermint/types"
  12. )
  13. type statistics struct {
  14. TxsThroughput metrics.Histogram `json:"txs_per_sec"`
  15. BlocksThroughput metrics.Histogram `json:"blocks_per_sec"`
  16. }
  17. // calculateStatistics calculates the tx / second, and blocks / second based
  18. // off of the number the transactions and number of blocks that occurred from
  19. // the start block, and the end time.
  20. func calculateStatistics(
  21. client tmrpc.Client,
  22. minHeight int64,
  23. timeStart time.Time,
  24. duration int,
  25. ) (*statistics, error) {
  26. timeEnd := timeStart.Add(time.Duration(duration) * time.Second)
  27. stats := &statistics{
  28. BlocksThroughput: metrics.NewHistogram(metrics.NewUniformSample(1000)),
  29. TxsThroughput: metrics.NewHistogram(metrics.NewUniformSample(1000)),
  30. }
  31. var (
  32. numBlocksPerSec = make(map[int64]int64)
  33. numTxsPerSec = make(map[int64]int64)
  34. )
  35. // because during some seconds blocks won't be created...
  36. for i := int64(0); i < int64(duration); i++ {
  37. numBlocksPerSec[i] = 0
  38. numTxsPerSec[i] = 0
  39. }
  40. blockMetas, err := getBlockMetas(client, minHeight, timeStart, timeEnd)
  41. if err != nil {
  42. return nil, err
  43. }
  44. // iterates from max height to min height
  45. for _, blockMeta := range blockMetas {
  46. // check if block was created after timeStart
  47. if blockMeta.Header.Time.Before(timeStart) {
  48. break
  49. }
  50. // check if block was created before timeEnd
  51. if blockMeta.Header.Time.After(timeEnd) {
  52. continue
  53. }
  54. sec := secondsSinceTimeStart(timeStart, blockMeta.Header.Time)
  55. // increase number of blocks for that second
  56. numBlocksPerSec[sec]++
  57. // increase number of txs for that second
  58. numTxsPerSec[sec] += blockMeta.Header.NumTxs
  59. logger.Debug(fmt.Sprintf("%d txs at block height %d", blockMeta.Header.NumTxs, blockMeta.Header.Height))
  60. }
  61. for i := int64(0); i < int64(duration); i++ {
  62. stats.BlocksThroughput.Update(numBlocksPerSec[i])
  63. stats.TxsThroughput.Update(numTxsPerSec[i])
  64. }
  65. return stats, nil
  66. }
  67. func getBlockMetas(client tmrpc.Client, minHeight int64, timeStart, timeEnd time.Time) ([]*types.BlockMeta, error) {
  68. // get blocks between minHeight and last height
  69. // This returns max(minHeight,(last_height - 20)) to last_height
  70. info, err := client.BlockchainInfo(minHeight, 0)
  71. if err != nil {
  72. return nil, err
  73. }
  74. var (
  75. blockMetas = info.BlockMetas
  76. lastHeight = info.LastHeight
  77. diff = lastHeight - minHeight
  78. offset = len(blockMetas)
  79. )
  80. for offset < int(diff) {
  81. // get blocks between minHeight and last height
  82. info, err := client.BlockchainInfo(minHeight, lastHeight-int64(offset))
  83. if err != nil {
  84. return nil, err
  85. }
  86. blockMetas = append(blockMetas, info.BlockMetas...)
  87. offset = len(blockMetas)
  88. }
  89. return blockMetas, nil
  90. }
  91. func secondsSinceTimeStart(timeStart, timePassed time.Time) int64 {
  92. return int64(math.Round(timePassed.Sub(timeStart).Seconds()))
  93. }
  94. func printStatistics(stats *statistics, outputFormat string) {
  95. if outputFormat == "json" {
  96. result, err := json.Marshal(struct {
  97. TxsThroughput float64 `json:"txs_per_sec_avg"`
  98. BlocksThroughput float64 `json:"blocks_per_sec_avg"`
  99. }{stats.TxsThroughput.Mean(), stats.BlocksThroughput.Mean()})
  100. if err != nil {
  101. fmt.Fprintln(os.Stderr, err)
  102. os.Exit(1)
  103. }
  104. fmt.Println(string(result))
  105. } else {
  106. w := tabwriter.NewWriter(os.Stdout, 0, 0, 5, ' ', 0)
  107. fmt.Fprintln(w, "Stats\tAvg\tStdDev\tMax\tTotal\t")
  108. fmt.Fprintln(
  109. w,
  110. fmt.Sprintf(
  111. "Txs/sec\t%.0f\t%.0f\t%d\t%d\t",
  112. stats.TxsThroughput.Mean(),
  113. stats.TxsThroughput.StdDev(),
  114. stats.TxsThroughput.Max(),
  115. stats.TxsThroughput.Sum(),
  116. ),
  117. )
  118. fmt.Fprintln(
  119. w,
  120. fmt.Sprintf("Blocks/sec\t%.3f\t%.3f\t%d\t%d\t",
  121. stats.BlocksThroughput.Mean(),
  122. stats.BlocksThroughput.StdDev(),
  123. stats.BlocksThroughput.Max(),
  124. stats.BlocksThroughput.Sum(),
  125. ),
  126. )
  127. w.Flush()
  128. }
  129. }