github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/cli/cmd/bench/tpcc.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 "strings" 26 27 "github.com/spf13/cobra" 28 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 29 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 30 "k8s.io/apimachinery/pkg/runtime" 31 "k8s.io/cli-runtime/pkg/genericiooptions" 32 cmdutil "k8s.io/kubectl/pkg/cmd/util" 33 "k8s.io/kubectl/pkg/util/templates" 34 35 "github.com/apecloud/kubebench/api/v1alpha1" 36 37 "github.com/1aal/kubeblocks/pkg/cli/cluster" 38 "github.com/1aal/kubeblocks/pkg/cli/types" 39 ) 40 41 var ( 42 tpccDriverMap = map[string]string{ 43 "mysql": "mysql", 44 "postgresql": "postgres", 45 } 46 tpccSupportedDrivers = []string{"mysql", "postgres"} 47 ) 48 49 var tpccExample = templates.Examples(` 50 # tpcc on a cluster, that will exec for all steps, cleanup, prepare and run 51 kbcli bench tpcc mytest --cluster mycluster --user xxx --password xxx --database mydb 52 53 # tpcc on a cluster with cleanup, only cleanup by deleting the testdata 54 kbcli bench tpcc cleanup mytest --cluster mycluster --user xxx --password xxx --database mydb 55 56 # tpcc on a cluster with prepare, just prepare by creating the testdata 57 kbcli bench tpcc prepare mytest --cluster mycluster --user xxx --password xxx --database mydb 58 59 # tpcc on a cluster with run, just run by running the test 60 kbcli bench tpcc run mytest --cluster mycluster --user xxx --password xxx --database mydb 61 62 # tpcc on a cluster with warehouse counts, which is the overall database size scaling parameter 63 kbcli bench tpcc mytest --cluster mycluster --user xxx --password xxx --database mydb --warehouses 100 64 65 # tpcc on a cluster with thread counts 66 kbcli bench tpcc mytest --cluster mycluster --user xxx --password xxx --database mydb --threads 4,8 67 68 # tpcc on a cluster with transactions counts 69 kbcli bench tpcc mytest --cluster mycluster --user xxx --password xxx --database mydb --transactions 1000 70 71 # tpcc on a cluster with duration 10 minutes 72 kbcli bench tpcc mytest --cluster mycluster --user xxx --password xxx --database mydb --duration 10 73 `) 74 75 type TpccOptions struct { 76 WareHouses int // specify the overall database size scaling parameter 77 Threads []int // specify the number of threads to use 78 Transactions int // specify the number of transactions that each thread should run 79 Duration int // specify the number of minutes to run 80 LimitTxPerMin int // limit the number of transactions to run per minute, 0 means no limit 81 NewOrder int // specify the percentage of transactions that should be new orders 82 Payment int // specify the percentage of transactions that should be payments 83 OrderStatus int // specify the percentage of transactions that should be order status 84 Delivery int // specify the percentage of transactions that should be delivery 85 StockLevel int // specify the percentage of transactions that should be stock level 86 87 BenchBaseOptions 88 } 89 90 func NewTpccCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command { 91 o := &TpccOptions{ 92 BenchBaseOptions: BenchBaseOptions{ 93 factory: f, 94 IOStreams: streams, 95 }, 96 } 97 cmd := &cobra.Command{ 98 Use: "tpcc [Step] [BenchmarkName]", 99 Short: "Run tpcc benchmark", 100 Example: tpccExample, 101 Run: func(cmd *cobra.Command, args []string) { 102 cmdutil.CheckErr(o.Complete(args)) 103 cmdutil.CheckErr(o.Validate()) 104 cmdutil.CheckErr(o.Run()) 105 }, 106 } 107 108 o.AddFlags(cmd) 109 cmd.Flags().IntVar(&o.WareHouses, "warehouses", 1, "specify the overall database size scaling parameter") 110 cmd.Flags().IntSliceVar(&o.Threads, "threads", []int{1}, "specify the number of threads to use") 111 cmd.Flags().IntVar(&o.Transactions, "transactions", 0, "specify the number of transactions that each thread should run") 112 cmd.Flags().IntVar(&o.Duration, "duration", 1, "specify the number of minutes to run") 113 cmd.Flags().IntVar(&o.LimitTxPerMin, "limit-tx-per-min", 0, "limit the number of transactions to run per minute, 0 means no limit") 114 cmd.Flags().IntVar(&o.NewOrder, "new-order", 45, "specify the percentage of transactions that should be new orders") 115 cmd.Flags().IntVar(&o.Payment, "payment", 43, "specify the percentage of transactions that should be payments") 116 cmd.Flags().IntVar(&o.OrderStatus, "order-status", 4, "specify the percentage of transactions that should be order status") 117 cmd.Flags().IntVar(&o.Delivery, "delivery", 4, "specify the percentage of transactions that should be delivery") 118 cmd.Flags().IntVar(&o.StockLevel, "stock-level", 4, "specify the percentage of transactions that should be stock level") 119 120 return cmd 121 } 122 123 func (o *TpccOptions) Complete(args []string) error { 124 var err error 125 var driver string 126 var host string 127 var port int 128 129 if err = o.BenchBaseOptions.BaseComplete(); err != nil { 130 return err 131 } 132 133 o.Step, o.name = parseStepAndName(args, "tpcc") 134 135 o.namespace, _, err = o.factory.ToRawKubeConfigLoader().Namespace() 136 if err != nil { 137 return err 138 } 139 140 if o.dynamic, err = o.factory.DynamicClient(); err != nil { 141 return err 142 } 143 144 if o.client, err = o.factory.KubernetesClientSet(); err != nil { 145 return err 146 } 147 148 if o.ClusterName != "" { 149 clusterGetter := cluster.ObjectsGetter{ 150 Client: o.client, 151 Dynamic: o.dynamic, 152 Name: o.ClusterName, 153 Namespace: o.namespace, 154 GetOptions: cluster.GetOptions{ 155 WithClusterDef: true, 156 WithService: true, 157 WithPod: true, 158 WithEvent: true, 159 WithPVC: true, 160 WithDataProtection: true, 161 }, 162 } 163 if o.ClusterObjects, err = clusterGetter.Get(); err != nil { 164 return err 165 } 166 driver, host, port, err = getDriverAndHostAndPort(o.Cluster, o.Services) 167 if err != nil { 168 return err 169 } 170 } 171 172 // don't overwrite the driver if it's already set 173 if v, ok := tpccDriverMap[driver]; ok && o.Driver == "" { 174 o.Driver = v 175 } 176 177 // don't overwrite the host and port if they are already set 178 if o.Host == "" && o.Port == 0 { 179 o.Host = host 180 o.Port = port 181 } 182 183 return nil 184 } 185 186 func (o *TpccOptions) Validate() error { 187 if err := o.BaseValidate(); err != nil { 188 return err 189 } 190 191 var supported bool 192 for _, v := range tpccDriverMap { 193 if o.Driver == v { 194 supported = true 195 break 196 } 197 } 198 if !supported { 199 return fmt.Errorf("tpcc now only supports drivers in [%s], current cluster driver is %s", 200 strings.Join(tpccSupportedDrivers, ","), o.Driver) 201 } 202 203 if o.User == "" { 204 return fmt.Errorf("user is required") 205 } 206 207 if o.Database == "" { 208 return fmt.Errorf("database is required") 209 } 210 211 if o.WareHouses < 1 { 212 return fmt.Errorf("warehouses must be greater than 0") 213 } 214 215 if o.Duration <= 0 { 216 return fmt.Errorf("duration must be greater than 0") 217 } 218 219 if o.NewOrder+o.Payment+o.OrderStatus+o.Delivery+o.StockLevel != 100 { 220 return fmt.Errorf("the sum of new-order, payment, order-status, delivery and stock-level must be 100") 221 } 222 223 return nil 224 } 225 226 func (o *TpccOptions) Run() error { 227 tpcc := v1alpha1.Tpcc{ 228 TypeMeta: metav1.TypeMeta{ 229 Kind: "Tpcc", 230 APIVersion: types.TpccGVR().GroupVersion().String(), 231 }, 232 ObjectMeta: metav1.ObjectMeta{ 233 Name: o.name, 234 Namespace: o.namespace, 235 }, 236 Spec: v1alpha1.TpccSpec{ 237 WareHouses: o.WareHouses, 238 Threads: o.Threads, 239 Transactions: o.Transactions, 240 Duration: o.Duration, 241 LimitTxPerMin: o.LimitTxPerMin, 242 NewOrder: o.NewOrder, 243 Payment: o.Payment, 244 OrderStatus: o.OrderStatus, 245 Delivery: o.Delivery, 246 StockLevel: o.StockLevel, 247 BenchCommon: v1alpha1.BenchCommon{ 248 ExtraArgs: o.ExtraArgs, 249 Step: o.Step, 250 Tolerations: o.Tolerations, 251 Target: v1alpha1.Target{ 252 Driver: o.Driver, 253 Host: o.Host, 254 Port: o.Port, 255 User: o.User, 256 Password: o.Password, 257 Database: o.Database, 258 }, 259 }, 260 }, 261 } 262 263 obj := &unstructured.Unstructured{ 264 Object: map[string]interface{}{}, 265 } 266 data, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&tpcc) 267 if err != nil { 268 return err 269 } 270 obj.SetUnstructuredContent(data) 271 272 obj, err = o.dynamic.Resource(types.TpccGVR()).Namespace(o.namespace).Create(context.TODO(), obj, metav1.CreateOptions{}) 273 if err != nil { 274 return err 275 } 276 277 fmt.Fprintf(o.Out, "%s %s created\n", obj.GetKind(), obj.GetName()) 278 return nil 279 }