209 lines
5.2 KiB
Go
Raw Normal View History

package main
2017-03-13 19:10:51 +04:00
import (
"encoding/json"
2017-03-13 19:10:51 +04:00
"flag"
"fmt"
"os"
"strings"
"time"
"github.com/go-kit/kit/log/term"
2017-03-17 00:56:22 +04:00
metrics "github.com/rcrowley/go-metrics"
2017-07-28 18:13:39 -04:00
"text/tabwriter"
2017-03-17 00:56:22 +04:00
tmtypes "github.com/tendermint/tendermint/types"
2017-07-28 18:13:39 -04:00
"github.com/tendermint/tmlibs/log"
"github.com/tendermint/tools/tm-monitor/monitor"
2017-03-13 19:10:51 +04:00
)
2018-04-03 13:42:57 +02:00
var version = "0.3.0"
2017-03-13 19:10:51 +04:00
var logger = log.NewNopLogger()
2017-03-17 00:56:22 +04:00
type statistics struct {
BlockTimeSample metrics.Histogram
TxThroughputSample metrics.Histogram
BlockLatency metrics.Histogram
}
2017-03-13 19:10:51 +04:00
func main() {
2017-03-17 13:13:06 +04:00
var duration, txsRate, connections int
var verbose bool
var outputFormat string
2017-03-13 19:10:51 +04:00
2017-03-17 13:13:06 +04:00
flag.IntVar(&connections, "c", 1, "Connections to keep open per endpoint")
2017-03-13 19:10:51 +04: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 19:10:51 +04:00
flag.Usage = func() {
2017-03-17 01:12:37 +04:00
fmt.Println(`Tendermint blockchain benchmarking tool.
2017-03-13 19:10:51 +04:00
Usage:
tm-bench [-c 1] [-T 10] [-r 1000] [endpoints] [-output-format <plain|json>]
2017-03-13 19:10:51 +04: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.Println("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 18:13:39 -04:00
logger = log.NewTMLoggerWithColorFn(log.NewSyncWriter(os.Stdout), colorFn)
fmt.Printf("Running %ds test @ %s\n", duration, flag.Arg(0))
}
2017-03-13 19:10:51 +04:00
endpoints := strings.Split(flag.Arg(0), ",")
2017-03-17 00:56:22 +04:00
blockCh := make(chan tmtypes.Header, 100)
blockLatencyCh := make(chan float64, 100)
nodes := startNodes(endpoints, blockCh, blockLatencyCh)
2017-03-17 13:13:06 +04:00
transacters := startTransacters(endpoints, connections, txsRate)
2017-03-17 00:56:22 +04:00
stats := &statistics{
BlockTimeSample: metrics.NewHistogram(metrics.NewUniformSample(1000)),
TxThroughputSample: metrics.NewHistogram(metrics.NewUniformSample(1000)),
BlockLatency: metrics.NewHistogram(metrics.NewUniformSample(1000)),
2017-03-13 19:10:51 +04:00
}
2018-04-03 12:48:33 +02:00
lastBlockHeight := int64(-1)
2017-03-17 00:56:22 +04:00
durationTimer := time.After(time.Duration(duration) * time.Second)
ticker := time.NewTicker(1 * time.Second)
2018-04-03 12:48:33 +02:00
var blocks int
var txs int64
2017-03-17 00:56:22 +04:00
for {
select {
case b := <-blockCh:
if lastBlockHeight < b.Height {
blocks++
txs += b.NumTxs
lastBlockHeight = b.Height
}
case l := <-blockLatencyCh:
stats.BlockLatency.Update(int64(l))
case <-ticker.C:
stats.BlockTimeSample.Update(int64(blocks))
2018-04-03 12:48:33 +02:00
stats.TxThroughputSample.Update(txs)
2017-03-17 00:56:22 +04:00
blocks = 0
txs = 0
case <-durationTimer:
for _, t := range transacters {
t.Stop()
}
printStatistics(stats, outputFormat)
2017-03-17 00:56:22 +04:00
for _, n := range nodes {
n.Stop()
}
return
2017-03-13 19:10:51 +04:00
}
}
}
2017-03-17 00:56:22 +04:00
func startNodes(endpoints []string, blockCh chan<- tmtypes.Header, blockLatencyCh chan<- float64) []*monitor.Node {
nodes := make([]*monitor.Node, len(endpoints))
2017-03-13 19:10:51 +04:00
2017-03-17 00:56:22 +04:00
for i, e := range endpoints {
n := monitor.NewNode(e)
2017-07-28 18:13:39 -04:00
n.SetLogger(logger.With("node", e))
2017-03-17 00:56:22 +04:00
n.SendBlocksTo(blockCh)
n.SendBlockLatenciesTo(blockLatencyCh)
if err := n.Start(); err != nil {
fmt.Println(err)
os.Exit(1)
2017-03-13 19:10:51 +04:00
}
2017-03-17 00:56:22 +04:00
nodes[i] = n
2017-03-13 19:10:51 +04:00
}
2017-03-17 00:56:22 +04:00
return nodes
}
2017-03-17 13:13:06 +04:00
func startTransacters(endpoints []string, connections int, txsRate int) []*transacter {
2017-03-17 00:56:22 +04:00
transacters := make([]*transacter, len(endpoints))
for i, e := range endpoints {
t := newTransacter(e, connections, txsRate)
t.SetLogger(logger)
2017-03-17 00:56:22 +04:00
if err := t.Start(); err != nil {
fmt.Println(err)
os.Exit(1)
2017-03-17 00:56:22 +04:00
}
transacters[i] = t
2017-03-13 19:10:51 +04:00
}
2017-03-17 00:56:22 +04:00
return transacters
2017-03-13 19:10:51 +04:00
}
type Results struct {
BlockLatencyMean float64
BlockLatencyMax int64
BlockLatencyStdDev float64
BlockTimeMean float64
BlockTimeMax int64
BlockTimeStdDev float64
TxThroughputMean float64
TxThroughputMax int64
TxThroughputStdDev float64
}
func printStatistics(stats *statistics, outputFormat string) {
if outputFormat == "json" {
result, _ := json.Marshal(Results{
stats.BlockLatency.Mean() / 1000000.0,
stats.BlockLatency.Max() / 1000000.0,
stats.BlockLatency.StdDev() / 1000000.0,
stats.BlockTimeSample.Mean(),
stats.BlockTimeSample.Max(),
stats.BlockTimeSample.StdDev(),
stats.TxThroughputSample.Mean(),
stats.TxThroughputSample.Max(),
stats.TxThroughputSample.StdDev(),
})
fmt.Println(string(result))
} else {
w := tabwriter.NewWriter(os.Stdout, 0, 0, 5, ' ', 0)
fmt.Fprintln(w, "Stats\tAvg\tStdev\tMax\t")
fmt.Fprintln(w, fmt.Sprintf("Block latency\t%.2fms\t%.2fms\t%dms\t",
stats.BlockLatency.Mean()/1000000.0,
stats.BlockLatency.StdDev()/1000000.0,
stats.BlockLatency.Max()/1000000))
fmt.Fprintln(w, fmt.Sprintf("Blocks/sec\t%.3f\t%.3f\t%d\t",
stats.BlockTimeSample.Mean(),
stats.BlockTimeSample.StdDev(),
stats.BlockTimeSample.Max()))
fmt.Fprintln(w, fmt.Sprintf("Txs/sec\t%.0f\t%.0f\t%d\t",
stats.TxThroughputSample.Mean(),
stats.TxThroughputSample.StdDev(),
stats.TxThroughputSample.Max()))
w.Flush()
}
2017-03-13 19:10:51 +04:00
}