github.com/evdatsion/aphelion-dpos-bft@v0.32.1/tools/tm-bench/main.go (about)

     1  package main
     2  
     3  import (
     4  	"flag"
     5  	"fmt"
     6  	"os"
     7  	"strings"
     8  	"sync"
     9  	"time"
    10  
    11  	"github.com/go-kit/kit/log/term"
    12  
    13  	cmn "github.com/evdatsion/aphelion-dpos-bft/libs/common"
    14  	"github.com/evdatsion/aphelion-dpos-bft/libs/log"
    15  	tmrpc "github.com/evdatsion/aphelion-dpos-bft/rpc/client"
    16  )
    17  
    18  var logger = log.NewNopLogger()
    19  
    20  func main() {
    21  	var durationInt, txsRate, connections, txSize int
    22  	var verbose bool
    23  	var outputFormat, broadcastTxMethod string
    24  
    25  	flagSet := flag.NewFlagSet("tm-bench", flag.ExitOnError)
    26  	flagSet.IntVar(&connections, "c", 1, "Connections to keep open per endpoint")
    27  	flagSet.IntVar(&durationInt, "T", 10, "Exit after the specified amount of time in seconds")
    28  	flagSet.IntVar(&txsRate, "r", 1000, "Txs per second to send in a connection")
    29  	flagSet.IntVar(&txSize, "s", 250, "The size of a transaction in bytes, must be greater than or equal to 40.")
    30  	flagSet.StringVar(&outputFormat, "output-format", "plain", "Output format: plain or json")
    31  	flagSet.StringVar(&broadcastTxMethod, "broadcast-tx-method", "async", "Broadcast method: async (no guarantees; fastest), sync (ensures tx is checked) or commit (ensures tx is checked and committed; slowest)")
    32  	flagSet.BoolVar(&verbose, "v", false, "Verbose output")
    33  
    34  	flagSet.Usage = func() {
    35  		fmt.Println(`Tendermint blockchain benchmarking tool.
    36  
    37  Usage:
    38  	tm-bench [-c 1] [-T 10] [-r 1000] [-s 250] [endpoints] [-output-format <plain|json> [-broadcast-tx-method <async|sync|commit>]]
    39  
    40  Examples:
    41  	tm-bench localhost:26657`)
    42  		fmt.Println("Flags:")
    43  		flagSet.PrintDefaults()
    44  	}
    45  
    46  	flagSet.Parse(os.Args[1:])
    47  
    48  	if flagSet.NArg() == 0 {
    49  		flagSet.Usage()
    50  		os.Exit(1)
    51  	}
    52  
    53  	if verbose {
    54  		if outputFormat == "json" {
    55  			printErrorAndExit("Verbose mode not supported with json output.")
    56  		}
    57  		// Color errors red
    58  		colorFn := func(keyvals ...interface{}) term.FgBgColor {
    59  			for i := 1; i < len(keyvals); i += 2 {
    60  				if _, ok := keyvals[i].(error); ok {
    61  					return term.FgBgColor{Fg: term.White, Bg: term.Red}
    62  				}
    63  			}
    64  			return term.FgBgColor{}
    65  		}
    66  		logger = log.NewTMLoggerWithColorFn(log.NewSyncWriter(os.Stdout), colorFn)
    67  
    68  		fmt.Printf("Running %ds test @ %s\n", durationInt, flagSet.Arg(0))
    69  	}
    70  
    71  	if txSize < 40 {
    72  		printErrorAndExit("The size of a transaction must be greater than or equal to 40.")
    73  	}
    74  
    75  	if broadcastTxMethod != "async" &&
    76  		broadcastTxMethod != "sync" &&
    77  		broadcastTxMethod != "commit" {
    78  		printErrorAndExit("broadcast-tx-method should be either 'sync', 'async' or 'commit'.")
    79  	}
    80  
    81  	var (
    82  		endpoints     = strings.Split(flagSet.Arg(0), ",")
    83  		client        = tmrpc.NewHTTP(endpoints[0], "/websocket")
    84  		initialHeight = latestBlockHeight(client)
    85  	)
    86  	logger.Info("Latest block height", "h", initialHeight)
    87  
    88  	transacters := startTransacters(
    89  		endpoints,
    90  		connections,
    91  		txsRate,
    92  		txSize,
    93  		"broadcast_tx_"+broadcastTxMethod,
    94  	)
    95  
    96  	// Stop upon receiving SIGTERM or CTRL-C.
    97  	cmn.TrapSignal(logger, func() {
    98  		for _, t := range transacters {
    99  			t.Stop()
   100  		}
   101  	})
   102  
   103  	// Wait until transacters have begun until we get the start time.
   104  	timeStart := time.Now()
   105  	logger.Info("Time last transacter started", "t", timeStart)
   106  
   107  	duration := time.Duration(durationInt) * time.Second
   108  
   109  	timeEnd := timeStart.Add(duration)
   110  	logger.Info("End time for calculation", "t", timeEnd)
   111  
   112  	<-time.After(duration)
   113  	for i, t := range transacters {
   114  		t.Stop()
   115  		numCrashes := countCrashes(t.connsBroken)
   116  		if numCrashes != 0 {
   117  			fmt.Printf("%d connections crashed on transacter #%d\n", numCrashes, i)
   118  		}
   119  	}
   120  
   121  	logger.Debug("Time all transacters stopped", "t", time.Now())
   122  
   123  	stats, err := calculateStatistics(
   124  		client,
   125  		initialHeight,
   126  		timeStart,
   127  		durationInt,
   128  	)
   129  	if err != nil {
   130  		printErrorAndExit(err.Error())
   131  	}
   132  
   133  	printStatistics(stats, outputFormat)
   134  }
   135  
   136  func latestBlockHeight(client tmrpc.Client) int64 {
   137  	status, err := client.Status()
   138  	if err != nil {
   139  		fmt.Fprintln(os.Stderr, err)
   140  		os.Exit(1)
   141  	}
   142  	return status.SyncInfo.LatestBlockHeight
   143  }
   144  
   145  func countCrashes(crashes []bool) int {
   146  	count := 0
   147  	for i := 0; i < len(crashes); i++ {
   148  		if crashes[i] {
   149  			count++
   150  		}
   151  	}
   152  	return count
   153  }
   154  
   155  func startTransacters(
   156  	endpoints []string,
   157  	connections,
   158  	txsRate int,
   159  	txSize int,
   160  	broadcastTxMethod string,
   161  ) []*transacter {
   162  	transacters := make([]*transacter, len(endpoints))
   163  
   164  	wg := sync.WaitGroup{}
   165  	wg.Add(len(endpoints))
   166  	for i, e := range endpoints {
   167  		t := newTransacter(e, connections, txsRate, txSize, broadcastTxMethod)
   168  		t.SetLogger(logger)
   169  		go func(i int) {
   170  			defer wg.Done()
   171  			if err := t.Start(); err != nil {
   172  				fmt.Fprintln(os.Stderr, err)
   173  				os.Exit(1)
   174  			}
   175  			transacters[i] = t
   176  		}(i)
   177  	}
   178  	wg.Wait()
   179  
   180  	return transacters
   181  }
   182  
   183  func printErrorAndExit(err string) {
   184  	fmt.Fprintln(os.Stderr, err)
   185  	os.Exit(1)
   186  }