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 }