|
|
- package main
-
- import (
- "encoding/json"
- "fmt"
- "math"
- "os"
- "text/tabwriter"
- "time"
-
- metrics "github.com/rcrowley/go-metrics"
- tmrpc "github.com/tendermint/tendermint/rpc/client"
- "github.com/tendermint/tendermint/types"
- )
-
- type statistics struct {
- TxsThroughput metrics.Histogram `json:"txs_per_sec"`
- BlocksThroughput metrics.Histogram `json:"blocks_per_sec"`
- }
-
- // calculateStatistics calculates the tx / second, and blocks / second based
- // off of the number the transactions and number of blocks that occurred from
- // the start block, and the end time.
- func calculateStatistics(
- client tmrpc.Client,
- minHeight int64,
- timeStart time.Time,
- duration int,
- ) (*statistics, error) {
- timeEnd := timeStart.Add(time.Duration(duration) * time.Second)
-
- stats := &statistics{
- BlocksThroughput: metrics.NewHistogram(metrics.NewUniformSample(1000)),
- TxsThroughput: metrics.NewHistogram(metrics.NewUniformSample(1000)),
- }
-
- var (
- numBlocksPerSec = make(map[int64]int64)
- numTxsPerSec = make(map[int64]int64)
- )
-
- // because during some seconds blocks won't be created...
- for i := int64(0); i < int64(duration); i++ {
- numBlocksPerSec[i] = 0
- numTxsPerSec[i] = 0
- }
-
- blockMetas, err := getBlockMetas(client, minHeight, timeStart, timeEnd)
- if err != nil {
- return nil, err
- }
-
- // iterates from max height to min height
- for _, blockMeta := range blockMetas {
- // check if block was created after timeStart
- if blockMeta.Header.Time.Before(timeStart) {
- break
- }
-
- // check if block was created before timeEnd
- if blockMeta.Header.Time.After(timeEnd) {
- continue
- }
- sec := secondsSinceTimeStart(timeStart, blockMeta.Header.Time)
-
- // increase number of blocks for that second
- numBlocksPerSec[sec]++
-
- // increase number of txs for that second
- numTxsPerSec[sec] += blockMeta.Header.NumTxs
- logger.Debug(fmt.Sprintf("%d txs at block height %d", blockMeta.Header.NumTxs, blockMeta.Header.Height))
- }
-
- for i := int64(0); i < int64(duration); i++ {
- stats.BlocksThroughput.Update(numBlocksPerSec[i])
- stats.TxsThroughput.Update(numTxsPerSec[i])
- }
-
- return stats, nil
- }
-
- func getBlockMetas(client tmrpc.Client, minHeight int64, timeStart, timeEnd time.Time) ([]*types.BlockMeta, error) {
- // get blocks between minHeight and last height
- // This returns max(minHeight,(last_height - 20)) to last_height
- info, err := client.BlockchainInfo(minHeight, 0)
- if err != nil {
- return nil, err
- }
-
- var (
- blockMetas = info.BlockMetas
- lastHeight = info.LastHeight
- diff = lastHeight - minHeight
- offset = len(blockMetas)
- )
-
- for offset < int(diff) {
- // get blocks between minHeight and last height
- info, err := client.BlockchainInfo(minHeight, lastHeight-int64(offset))
- if err != nil {
- return nil, err
- }
- blockMetas = append(blockMetas, info.BlockMetas...)
- offset = len(blockMetas)
- }
-
- return blockMetas, nil
- }
-
- func secondsSinceTimeStart(timeStart, timePassed time.Time) int64 {
- return int64(math.Round(timePassed.Sub(timeStart).Seconds()))
- }
-
- func printStatistics(stats *statistics, outputFormat string) {
- if outputFormat == "json" {
- result, err := json.Marshal(struct {
- TxsThroughput float64 `json:"txs_per_sec_avg"`
- BlocksThroughput float64 `json:"blocks_per_sec_avg"`
- }{stats.TxsThroughput.Mean(), stats.BlocksThroughput.Mean()})
-
- if err != nil {
- fmt.Fprintln(os.Stderr, err)
- os.Exit(1)
- }
- fmt.Println(string(result))
- } else {
- w := tabwriter.NewWriter(os.Stdout, 0, 0, 5, ' ', 0)
- fmt.Fprintln(w, "Stats\tAvg\tStdDev\tMax\tTotal\t")
- fmt.Fprintln(
- w,
- fmt.Sprintf(
- "Txs/sec\t%.0f\t%.0f\t%d\t%d\t",
- stats.TxsThroughput.Mean(),
- stats.TxsThroughput.StdDev(),
- stats.TxsThroughput.Max(),
- stats.TxsThroughput.Sum(),
- ),
- )
- fmt.Fprintln(
- w,
- fmt.Sprintf("Blocks/sec\t%.3f\t%.3f\t%d\t%d\t",
- stats.BlocksThroughput.Mean(),
- stats.BlocksThroughput.StdDev(),
- stats.BlocksThroughput.Max(),
- stats.BlocksThroughput.Sum(),
- ),
- )
- w.Flush()
- }
- }
|