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  }