tendermint/tm-bench/main.go

208 lines
5.1 KiB
Go
Raw Normal View History

package main
2017-03-13 08:10:51 -07:00
import (
"encoding/json"
2017-03-13 08:10:51 -07:00
"flag"
"fmt"
"os"
"strings"
"time"
"github.com/go-kit/kit/log/term"
2017-03-16 13:56:22 -07:00
metrics "github.com/rcrowley/go-metrics"
2017-07-28 15:13:39 -07:00
"text/tabwriter"
tmrpc "github.com/tendermint/tendermint/rpc/client"
2017-07-28 15:13:39 -07:00
"github.com/tendermint/tmlibs/log"
2017-03-13 08:10:51 -07:00
)
2018-04-03 04:42:57 -07:00
var version = "0.3.0"
2017-03-13 08:10:51 -07:00
var logger = log.NewNopLogger()
2017-03-16 13:56:22 -07:00
type statistics struct {
TxsThroughput metrics.Histogram `json:"txs_per_sec"`
BlocksThroughput metrics.Histogram `json:"blocks_per_sec"`
2017-03-16 13:56:22 -07:00
}
2017-03-13 08:10:51 -07:00
func main() {
2017-03-17 02:13:06 -07:00
var duration, txsRate, connections int
var verbose bool
var outputFormat string
2017-03-13 08:10:51 -07:00
2017-03-17 02:13:06 -07:00
flag.IntVar(&connections, "c", 1, "Connections to keep open per endpoint")
2017-03-13 08:10:51 -07:00
flag.IntVar(&duration, "T", 10, "Exit after the specified amount of time in seconds")
flag.IntVar(&txsRate, "r", 1000, "Txs per second to send in a connection")
flag.StringVar(&outputFormat, "output-format", "plain", "Output format: plain or json")
flag.BoolVar(&verbose, "v", false, "Verbose output")
2017-03-13 08:10:51 -07:00
flag.Usage = func() {
2017-03-16 14:12:37 -07:00
fmt.Println(`Tendermint blockchain benchmarking tool.
2017-03-13 08:10:51 -07:00
Usage:
tm-bench [-c 1] [-T 10] [-r 1000] [endpoints] [-output-format <plain|json>]
2017-03-13 08:10:51 -07:00
Examples:
tm-bench localhost:46657`)
fmt.Println("Flags:")
flag.PrintDefaults()
}
flag.Parse()
if flag.NArg() == 0 {
flag.Usage()
os.Exit(1)
}
if verbose {
if outputFormat == "json" {
fmt.Fprintln(os.Stderr, "Verbose mode not supported with json output.")
os.Exit(1)
}
// Color errors red
colorFn := func(keyvals ...interface{}) term.FgBgColor {
for i := 1; i < len(keyvals); i += 2 {
if _, ok := keyvals[i].(error); ok {
return term.FgBgColor{Fg: term.White, Bg: term.Red}
}
}
return term.FgBgColor{}
}
2017-07-28 15:13:39 -07:00
logger = log.NewTMLoggerWithColorFn(log.NewSyncWriter(os.Stdout), colorFn)
fmt.Printf("Running %ds test @ %s\n", duration, flag.Arg(0))
}
2017-03-13 08:10:51 -07:00
endpoints := strings.Split(flag.Arg(0), ",")
2017-03-16 13:56:22 -07:00
client := tmrpc.NewHTTP(endpoints[0], "/websocket")
2017-03-16 13:56:22 -07:00
minHeight := latestBlockHeight(client)
logger.Info("Latest block height", "h", minHeight)
// record time start
timeStart := time.Now()
logger.Info("Time started", "t", timeStart)
2017-03-16 13:56:22 -07:00
2017-03-17 02:13:06 -07:00
transacters := startTransacters(endpoints, connections, txsRate)
2017-03-16 13:56:22 -07:00
select {
case <-time.After(time.Duration(duration) * time.Second):
for _, t := range transacters {
t.Stop()
}
timeStop := time.Now()
logger.Info("Time stopped", "t", timeStop)
stats := calculateStatistics(client, minHeight, timeStart, timeStop)
printStatistics(stats, outputFormat)
return
}
}
func latestBlockHeight(client tmrpc.Client) int64 {
status, err := client.Status()
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
return status.SyncInfo.LatestBlockHeight
}
func calculateStatistics(client tmrpc.Client, minHeight int64, timeStart, timeStop time.Time) *statistics {
2017-03-16 13:56:22 -07:00
stats := &statistics{
BlocksThroughput: metrics.NewHistogram(metrics.NewUniformSample(1000)),
TxsThroughput: metrics.NewHistogram(metrics.NewUniformSample(1000)),
2017-03-13 08:10:51 -07:00
}
// get blocks between minHeight and last height
info, err := client.BlockchainInfo(minHeight, 0)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
numBlocksPerSec := make(map[int64]int64)
numTxsPerSec := make(map[int64]int64)
for _, blockMeta := range info.BlockMetas {
// check if block was created before timeStop
if blockMeta.Header.Time.After(timeStop) {
break
}
2017-03-16 13:56:22 -07:00
sec := secondsSinceTimeStart(timeStart, blockMeta.Header.Time)
2017-03-16 13:56:22 -07:00
// increase number of blocks for that second
if _, ok := numBlocksPerSec[sec]; !ok {
numBlocksPerSec[sec] = 0
2017-03-13 08:10:51 -07:00
}
numBlocksPerSec[sec]++
// increase number of txs for that second
if _, ok := numTxsPerSec[sec]; !ok {
numTxsPerSec[sec] = 0
}
numTxsPerSec[sec] += blockMeta.Header.NumTxs
2017-03-13 08:10:51 -07:00
}
for _, n := range numBlocksPerSec {
stats.BlocksThroughput.Update(n)
}
2017-03-13 08:10:51 -07:00
for _, n := range numTxsPerSec {
stats.TxsThroughput.Update(n)
2017-03-13 08:10:51 -07:00
}
return stats
}
func secondsSinceTimeStart(timeStart, timePassed time.Time) int64 {
return int64(timePassed.Sub(timeStart).Seconds())
2017-03-16 13:56:22 -07:00
}
2017-03-17 02:13:06 -07:00
func startTransacters(endpoints []string, connections int, txsRate int) []*transacter {
2017-03-16 13:56:22 -07:00
transacters := make([]*transacter, len(endpoints))
for i, e := range endpoints {
t := newTransacter(e, connections, txsRate)
t.SetLogger(logger)
2017-03-16 13:56:22 -07:00
if err := t.Start(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
2017-03-16 13:56:22 -07:00
}
transacters[i] = t
2017-03-13 08:10:51 -07:00
}
2017-03-16 13:56:22 -07:00
return transacters
2017-03-13 08:10:51 -07:00
}
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\t")
fmt.Fprintln(w, fmt.Sprintf("Txs/sec\t%.0f\t%.0f\t%d\t",
stats.TxsThroughput.Mean(),
stats.TxsThroughput.StdDev(),
stats.TxsThroughput.Max()))
fmt.Fprintln(w, fmt.Sprintf("Blocks/sec\t%.3f\t%.3f\t%d\t",
stats.BlocksThroughput.Mean(),
stats.BlocksThroughput.StdDev(),
stats.BlocksThroughput.Max()))
w.Flush()
}
2017-03-13 08:10:51 -07:00
}