vitess.io/vitess@v0.16.2/go/cmd/vtbench/vtbench.go (about) 1 /* 2 Copyright 2019 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package main 18 19 import ( 20 "context" 21 "fmt" 22 "strings" 23 "time" 24 25 "github.com/spf13/pflag" 26 27 "vitess.io/vitess/go/acl" 28 "vitess.io/vitess/go/exit" 29 "vitess.io/vitess/go/vt/dbconfigs" 30 "vitess.io/vitess/go/vt/grpccommon" 31 "vitess.io/vitess/go/vt/log" 32 "vitess.io/vitess/go/vt/logutil" 33 "vitess.io/vitess/go/vt/servenv" 34 "vitess.io/vitess/go/vtbench" 35 36 // Import and register the gRPC vtgateconn client 37 _ "vitess.io/vitess/go/vt/vtgate/grpcvtgateconn" 38 // Import and register the gRPC tabletconn client 39 _ "vitess.io/vitess/go/vt/vttablet/grpctabletconn" 40 ) 41 42 /* 43 44 Vtbench is a simple load testing client to compare workloads in 45 Vitess across the various client/server protocols. 46 47 There are a number of command line options to control the behavior, 48 but as a basic example, the three supported client protocols are: 49 50 Mysql protocol to vtgate: 51 vtbench \ 52 --protocol mysql \ 53 --host vtgate-host.my.domain \ 54 --port 15306 \ 55 --user db_username \ 56 --db-credentials-file ./vtbench_db_creds.json \ 57 --db @replica \ 58 --sql "select * from loadtest_table where id=123456789" \ 59 --threads 10 \ 60 --count 10 61 62 GRPC to vtgate: 63 vtbench \ 64 --protocol grpc-vtgate \ 65 --host vtgate-host.my.domain \ 66 --port 15999 \ 67 --db @replica \ 68 $VTTABLET_GRPC_ARGS \ 69 --sql "select * from loadtest_table where id=123456789" \ 70 --threads 10 \ 71 --count 10 72 73 GRPC to vttablet: 74 vtbench \ 75 --protocol grpc-vttablet \ 76 --host tablet-loadtest-00-80.my.domain \ 77 --port 15999 \ 78 --db loadtest/00-80@replica \ 79 --sql "select * from loadtest_table where id=123456789" \ 80 --threads 10 \ 81 --count 10 82 83 */ 84 85 var ( 86 host, unixSocket, user, db, sql string 87 port int 88 protocol = "mysql" 89 deadline = 5 * time.Minute 90 threads = 2 91 count = 1000 92 ) 93 94 func initFlags(fs *pflag.FlagSet) { 95 fs.StringVar(&host, "host", host, "VTGate host(s) in the form 'host1,host2,...'") 96 fs.IntVar(&port, "port", port, "VTGate port") 97 fs.StringVar(&unixSocket, "unix_socket", unixSocket, "VTGate unix socket") 98 fs.StringVar(&protocol, "protocol", protocol, "Client protocol, either mysql (default), grpc-vtgate, or grpc-vttablet") 99 fs.StringVar(&user, "user", user, "Username to connect using mysql (password comes from the db-credentials-file)") 100 fs.StringVar(&db, "db", db, "Database name to use when connecting / running the queries (e.g. @replica, keyspace, keyspace/shard etc)") 101 102 fs.DurationVar(&deadline, "deadline", deadline, "Maximum duration for the test run (default 5 minutes)") 103 fs.StringVar(&sql, "sql", sql, "SQL statement to execute") 104 fs.IntVar(&threads, "threads", threads, "Number of parallel threads to run") 105 fs.IntVar(&count, "count", count, "Number of queries per thread") 106 107 grpccommon.RegisterFlags(fs) 108 log.RegisterFlags(fs) 109 logutil.RegisterFlags(fs) 110 acl.RegisterFlags(fs) 111 servenv.RegisterMySQLServerFlags(fs) 112 } 113 114 func main() { 115 servenv.OnParseFor("vtbench", func(fs *pflag.FlagSet) { 116 logger := logutil.NewConsoleLogger() 117 fs.SetOutput(logutil.NewLoggerWriter(logger)) 118 119 initFlags(fs) 120 _ = fs.Set("logtostderr", "true") 121 }) 122 123 servenv.ParseFlags("vtbench") 124 125 defer exit.Recover() 126 127 clientProto := vtbench.MySQL 128 switch protocol { 129 case "", "mysql": 130 clientProto = vtbench.MySQL 131 case "grpc-vtgate": 132 clientProto = vtbench.GRPCVtgate 133 case "grpc-vttablet": 134 clientProto = vtbench.GRPCVttablet 135 default: 136 log.Exitf("invalid client protocol %s", protocol) 137 } 138 139 if (host != "" || port != 0) && unixSocket != "" { 140 log.Exitf("can't specify both host:port and unix_socket") 141 } 142 143 if host != "" && port == 0 { 144 log.Exitf("must specify port when using host") 145 } 146 147 if host == "" && port != 0 { 148 log.Exitf("must specify host when using port") 149 } 150 151 if host == "" && port == 0 && unixSocket == "" { 152 log.Exitf("vtbench requires either host/port or unix_socket") 153 } 154 155 if sql == "" { 156 log.Exitf("must specify sql") 157 } 158 159 var password string 160 if clientProto == vtbench.MySQL { 161 var err error 162 _, password, err = dbconfigs.GetCredentialsServer().GetUserAndPassword(user) 163 if err != nil { 164 log.Exitf("error reading password for user %v from file: %v", user, err) 165 } 166 } 167 168 connParams := vtbench.ConnParams{ 169 Hosts: strings.Split(host, ","), 170 Port: port, 171 UnixSocket: unixSocket, 172 Protocol: clientProto, 173 DB: db, 174 Username: user, 175 Password: password, 176 } 177 178 b := vtbench.NewBench(threads, count, connParams, sql) 179 180 ctx, cancel := context.WithTimeout(context.Background(), deadline) 181 defer cancel() 182 183 fmt.Printf("Initializing test with %s protocol / %d threads / %d iterations\n", 184 b.ConnParams.Protocol.String(), b.Threads, b.Count) 185 err := b.Run(ctx) 186 if err != nil { 187 log.Exitf("error in test: %v", err) 188 } 189 190 fmt.Printf("Average Rows Returned: %d\n", b.Rows.Get()/int64(b.Threads*b.Count)) 191 fmt.Printf("Average Query Time: %v\n", time.Duration(b.Timings.Time()/b.Timings.Count())) 192 fmt.Printf("Total Test Time: %v\n", b.TotalTime) 193 fmt.Printf("QPS (Per Thread): %v\n", float64(b.Count)/b.TotalTime.Seconds()) 194 fmt.Printf("QPS (Total): %v\n", float64(b.Count*b.Threads)/b.TotalTime.Seconds()) 195 196 last := int64(0) 197 198 histograms := b.Timings.Histograms() 199 h := histograms["query"] 200 buckets := h.Buckets() 201 fmt.Printf("Query Timings:\n") 202 for i, bucket := range h.Cutoffs() { 203 count := buckets[i] 204 if count != 0 { 205 fmt.Printf("%v-%v: %v\n", time.Duration(last), time.Duration(bucket), count) 206 } 207 last = bucket 208 } 209 }