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

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()
}
}