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 }