github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/cli/cmd/bench/pgbench.go (about)

     1  /*
     2  Copyright (C) 2022-2023 ApeCloud Co., Ltd
     3  
     4  This file is part of KubeBlocks project
     5  
     6  This program is free software: you can redistribute it and/or modify
     7  it under the terms of the GNU Affero General Public License as published by
     8  the Free Software Foundation, either version 3 of the License, or
     9  (at your option) any later version.
    10  
    11  This program is distributed in the hope that it will be useful
    12  but WITHOUT ANY WARRANTY; without even the implied warranty of
    13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    14  GNU Affero General Public License for more details.
    15  
    16  You should have received a copy of the GNU Affero General Public License
    17  along with this program.  If not, see <http://www.gnu.org/licenses/>.
    18  */
    19  
    20  package bench
    21  
    22  import (
    23  	"context"
    24  	"fmt"
    25  
    26  	"github.com/spf13/cobra"
    27  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    28  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    29  	"k8s.io/apimachinery/pkg/runtime"
    30  	"k8s.io/cli-runtime/pkg/genericiooptions"
    31  	cmdutil "k8s.io/kubectl/pkg/cmd/util"
    32  	"k8s.io/kubectl/pkg/util/templates"
    33  
    34  	"github.com/apecloud/kubebench/api/v1alpha1"
    35  
    36  	"github.com/1aal/kubeblocks/pkg/cli/cluster"
    37  	"github.com/1aal/kubeblocks/pkg/cli/types"
    38  )
    39  
    40  const (
    41  	pgBenchDriver = "postgresql"
    42  )
    43  
    44  var pgbenchExample = templates.Examples(`
    45  	# pgbench run on a cluster, that will exec for all steps, cleanup, prepare and run
    46  	kbcli bench pgbench mytest --cluster pgcluster --database postgres --user xxx --password xxx
    47  	
    48  	# pgbench run on a cluster with cleanup, only cleanup by deleting the testdata
    49  	kbcli bench pgbench cleanup mytest --cluster pgcluster --database postgres --user xxx --password xxx
    50  	
    51  	# pgbench run on a cluster with prepare, just prepare by creating the testdata
    52  	kbcli bench pgbench prepare mytest --cluster pgcluster --database postgres --user xxx --password xxx
    53  	
    54  	# pgbench run on a cluster with run, just run by running the test
    55  	kbcli bench pgbench run mytest --cluster pgcluster --database postgres --user xxx --password xxx
    56  	
    57  	# pgbench run on a cluster with  thread and  client counts
    58  	kbcli bench sysbench mytest --cluster pgcluster --user xxx --password xxx --database xxx --clients 5 --threads 5
    59  	
    60  	# pgbench run on a cluster with specified transactions
    61  	kbcli bench pgbench mytest --cluster pgcluster --database postgres --user xxx --password xxx --transactions 1000
    62  	
    63  	# pgbench run on a cluster with specified seconds
    64  	kbcli bench pgbench mytest --cluster pgcluster --database postgres --user xxx --password xxx --duration 60
    65  	
    66  	# pgbench run on a cluster with 'select' only
    67  	kbcli bench pgbench mytest --cluster pgcluster --database postgres --user xxx --password xxx --select
    68  `)
    69  
    70  type PgBenchOptions struct {
    71  	Scale        int   // specify the scale factor for the benchmark test
    72  	Clients      []int // specify the number of clients to run
    73  	Threads      int   // specify the number of threads per client
    74  	Transactions int   // specify the number of transactions per client
    75  	Duration     int   // specify the duration of benchmark test in seconds
    76  	Select       bool  // specify to run SELECT-only transactions
    77  
    78  	BenchBaseOptions
    79  }
    80  
    81  func NewPgBenchCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command {
    82  	o := &PgBenchOptions{
    83  		BenchBaseOptions: BenchBaseOptions{
    84  			IOStreams: streams,
    85  			factory:   f,
    86  		},
    87  	}
    88  
    89  	cmd := &cobra.Command{
    90  		Use:     "pgbench [Step] [BenchmarkName]",
    91  		Short:   "Run pgbench against a PostgreSQL cluster",
    92  		Example: pgbenchExample,
    93  		Run: func(cmd *cobra.Command, args []string) {
    94  			cmdutil.CheckErr(o.Complete(args))
    95  			cmdutil.CheckErr(o.Validate())
    96  			cmdutil.CheckErr(o.Run())
    97  		},
    98  	}
    99  
   100  	o.BenchBaseOptions.AddFlags(cmd)
   101  	cmd.Flags().IntVar(&o.Scale, "scale", 1, "The scale factor to use for pgbench")
   102  	cmd.Flags().IntSliceVar(&o.Clients, "clients", []int{1}, "The number of clients to use for pgbench")
   103  	cmd.Flags().IntVar(&o.Threads, "threads", 1, "The number of threads to use for pgbench")
   104  	cmd.Flags().IntVar(&o.Transactions, "transactions", 0, "The number of transactions to run for pgbench")
   105  	cmd.Flags().IntVar(&o.Duration, "duration", 60, "The seconds to run pgbench for")
   106  	cmd.Flags().BoolVar(&o.Select, "select", false, "Run pgbench with select only")
   107  
   108  	return cmd
   109  }
   110  
   111  func (o *PgBenchOptions) Complete(args []string) error {
   112  	var err error
   113  	var driver string
   114  	var host string
   115  	var port int
   116  
   117  	if err = o.BenchBaseOptions.BaseComplete(); err != nil {
   118  		return err
   119  	}
   120  
   121  	o.Step, o.name = parseStepAndName(args, "pgbench")
   122  
   123  	o.namespace, _, err = o.factory.ToRawKubeConfigLoader().Namespace()
   124  	if err != nil {
   125  		return err
   126  	}
   127  
   128  	if o.dynamic, err = o.factory.DynamicClient(); err != nil {
   129  		return err
   130  	}
   131  
   132  	if o.client, err = o.factory.KubernetesClientSet(); err != nil {
   133  		return err
   134  	}
   135  
   136  	if o.ClusterName != "" {
   137  		clusterGetter := cluster.ObjectsGetter{
   138  			Client:    o.client,
   139  			Dynamic:   o.dynamic,
   140  			Name:      o.ClusterName,
   141  			Namespace: o.namespace,
   142  			GetOptions: cluster.GetOptions{
   143  				WithClusterDef:     true,
   144  				WithService:        true,
   145  				WithPod:            true,
   146  				WithEvent:          true,
   147  				WithPVC:            true,
   148  				WithDataProtection: true,
   149  			},
   150  		}
   151  		if o.ClusterObjects, err = clusterGetter.Get(); err != nil {
   152  			return err
   153  		}
   154  		driver, host, port, err = getDriverAndHostAndPort(o.Cluster, o.Services)
   155  		if err != nil {
   156  			return err
   157  		}
   158  	}
   159  
   160  	if o.Driver == "" {
   161  		o.Driver = driver
   162  	}
   163  
   164  	if o.Host == "" && o.Port == 0 {
   165  		o.Host = host
   166  		o.Port = port
   167  	}
   168  
   169  	return nil
   170  }
   171  
   172  func (o *PgBenchOptions) Validate() error {
   173  	if err := o.BaseValidate(); err != nil {
   174  		return err
   175  	}
   176  
   177  	if o.Driver != pgBenchDriver {
   178  		return fmt.Errorf("pgbench only supports drivers in [%s], current cluster driver is %s", pgBenchDriver, o.Driver)
   179  	}
   180  
   181  	if len(o.Clients) == 0 {
   182  		return fmt.Errorf("clients should be specified")
   183  	}
   184  
   185  	if o.User == "" {
   186  		return fmt.Errorf("user is required")
   187  	}
   188  
   189  	if o.Database == "" {
   190  		return fmt.Errorf("database is required")
   191  	}
   192  
   193  	return nil
   194  }
   195  
   196  func (o *PgBenchOptions) Run() error {
   197  	pgbench := v1alpha1.Pgbench{
   198  		TypeMeta: metav1.TypeMeta{
   199  			Kind:       "Pgbench",
   200  			APIVersion: types.PgBenchGVR().GroupVersion().String(),
   201  		},
   202  		ObjectMeta: metav1.ObjectMeta{
   203  			Namespace: o.namespace,
   204  			Name:      o.name,
   205  		},
   206  		Spec: v1alpha1.PgbenchSpec{
   207  			Scale:        o.Scale,
   208  			Clients:      o.Clients,
   209  			Threads:      o.Threads,
   210  			SelectOnly:   o.Select,
   211  			Transactions: o.Transactions,
   212  			Duration:     o.Duration,
   213  			BenchCommon: v1alpha1.BenchCommon{
   214  				Tolerations: o.Tolerations,
   215  				ExtraArgs:   o.ExtraArgs,
   216  				Step:        o.Step,
   217  				Target: v1alpha1.Target{
   218  					Host:     o.Host,
   219  					Port:     o.Port,
   220  					User:     o.User,
   221  					Password: o.Password,
   222  					Database: o.Database,
   223  				},
   224  			},
   225  		},
   226  	}
   227  
   228  	obj := &unstructured.Unstructured{
   229  		Object: map[string]interface{}{},
   230  	}
   231  	data, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&pgbench)
   232  	if err != nil {
   233  		return err
   234  	}
   235  	obj.SetUnstructuredContent(data)
   236  
   237  	obj, err = o.dynamic.Resource(types.PgBenchGVR()).Namespace(o.namespace).Create(context.TODO(), obj, metav1.CreateOptions{})
   238  	if err != nil {
   239  		return err
   240  	}
   241  
   242  	fmt.Fprintf(o.Out, "%s %s created\n", obj.GetKind(), obj.GetName())
   243  
   244  	return nil
   245  }