github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/performance/replicationbench/replica_test.go (about) 1 // Copyright 2021 Dolthub, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package serverbench 16 17 import ( 18 "context" 19 "fmt" 20 "os" 21 "path" 22 "runtime" 23 "runtime/pprof" 24 "strings" 25 "testing" 26 27 "github.com/gocraft/dbr/v2" 28 "golang.org/x/sync/errgroup" 29 30 srv "github.com/dolthub/dolt/go/cmd/dolt/commands/sqlserver" 31 "github.com/dolthub/dolt/go/libraries/doltcore/dtestutils/testcommands" 32 "github.com/dolthub/dolt/go/libraries/doltcore/env" 33 "github.com/dolthub/dolt/go/libraries/doltcore/servercfg" 34 "github.com/dolthub/dolt/go/libraries/doltcore/sqle/dsess" 35 "github.com/dolthub/dolt/go/libraries/utils/svcs" 36 ) 37 38 type query string 39 40 type serverTest struct { 41 name string 42 setup []query 43 bench []query 44 } 45 46 // usage: `go test -bench .` 47 func BenchmarkPushOnWrite(b *testing.B) { 48 49 setup := make([]query, 1) 50 setup[0] = "CREATE TABLE bench (a int, b int, c int);" 51 52 q := strings.Builder{} 53 q.WriteString("INSERT INTO bench (a, b, c) VALUES (0, 0, 0)") 54 i := 1 55 for i < 1000 { 56 q.WriteString(fmt.Sprintf(",(%d, %d, %d)", i, i, i)) 57 i++ 58 } 59 qs := q.String() 60 61 bench := make([]query, 100) 62 commit := query("select dolt_commit('-am', 'cm')") 63 i = 0 64 for i < len(bench) { 65 bench[i] = query(qs) 66 bench[i+1] = commit 67 i += 2 68 } 69 70 benchmarkServer(b, serverTest{ 71 name: "smoke bench", 72 setup: setup, 73 bench: bench, 74 }) 75 } 76 77 func benchmarkServer(b *testing.B, test serverTest) { 78 var dEnv *env.DoltEnv 79 var cfg servercfg.ServerConfig 80 ctx := context.Background() 81 82 // setup 83 dEnv, cfg = getEnvAndConfig(ctx, b) 84 executeServerQueries(ctx, b, dEnv, cfg, test.setup) 85 86 // bench 87 f := getProfFile(b) 88 err := pprof.StartCPUProfile(f) 89 if err != nil { 90 b.Fatal(err) 91 } 92 defer func() { 93 pprof.StopCPUProfile() 94 if err = f.Close(); err != nil { 95 b.Fatal(err) 96 } 97 fmt.Printf("\twriting CPU profile for %s: %s\n", b.Name(), f.Name()) 98 }() 99 100 b.Run(test.name, func(b *testing.B) { 101 executeServerQueries(ctx, b, dEnv, cfg, test.bench) 102 }) 103 } 104 105 const ( 106 database = "dolt_bench" 107 port = 1234 108 109 name = "name" 110 email = "name@fake.horse" 111 ) 112 113 func getEnvAndConfig(ctx context.Context, b *testing.B) (dEnv *env.DoltEnv, cfg servercfg.ServerConfig) { 114 multiSetup := testcommands.NewMultiRepoTestSetup(b.Fatal) 115 116 multiSetup.NewDB("dolt_bench") 117 multiSetup.NewRemote("remote1") 118 119 writerName := multiSetup.DbNames[0] 120 121 localCfg, ok := multiSetup.GetEnv(writerName).Config.GetConfig(env.LocalConfig) 122 if !ok { 123 b.Fatal("local config does not exist") 124 } 125 localCfg.SetStrings(map[string]string{dsess.ReplicateToRemote: "remote1"}) 126 127 yaml := []byte(fmt.Sprintf(` 128 log_level: warning 129 130 behavior: 131 read_only: false 132 133 user: 134 name: "root" 135 password: "" 136 137 databases: 138 - name: "%s" 139 path: "%s" 140 141 listener: 142 host: localhost 143 port: %d 144 max_connections: 128 145 read_timeout_millis: 28800000 146 write_timeout_millis: 28800000 147 `, writerName, multiSetup.DbPaths[writerName], port)) 148 149 cfg, err := servercfg.NewYamlConfig(yaml) 150 if err != nil { 151 b.Fatal(err) 152 } 153 154 return multiSetup.GetEnv(writerName), cfg 155 } 156 157 func getProfFile(b *testing.B) *os.File { 158 _, testFile, _, _ := runtime.Caller(0) 159 160 f, err := os.Create(path.Join(path.Dir(testFile), b.Name()+".out")) 161 if err != nil { 162 b.Fatal(err) 163 } 164 return f 165 } 166 167 func executeServerQueries(ctx context.Context, b *testing.B, dEnv *env.DoltEnv, cfg servercfg.ServerConfig, queries []query) { 168 sc := svcs.NewController() 169 170 eg, ctx := errgroup.WithContext(ctx) 171 172 //b.Logf("Starting server with Config %v\n", srv.ConfigInfo(cfg)) 173 eg.Go(func() (err error) { 174 startErr, closeErr := srv.Serve(ctx, "", cfg, sc, dEnv) 175 if startErr != nil { 176 return startErr 177 } 178 if closeErr != nil { 179 return closeErr 180 } 181 return nil 182 }) 183 if err := sc.WaitForStart(); err != nil { 184 b.Fatal(err) 185 } 186 187 for _, q := range queries { 188 if err := executeQuery(cfg, q); err != nil { 189 b.Fatal(err) 190 } 191 } 192 193 sc.Stop() 194 if err := sc.WaitForStop(); err != nil { 195 b.Fatal(err) 196 } 197 if err := eg.Wait(); err != nil { 198 b.Fatal(err) 199 } 200 } 201 202 func executeQuery(cfg servercfg.ServerConfig, q query) error { 203 cs := servercfg.ConnectionString(cfg, database) 204 conn, err := dbr.Open("mysql", cs, nil) 205 if err != nil { 206 return err 207 } 208 209 rows, err := conn.Query(string(q)) 210 if err != nil { 211 return err 212 } 213 214 for { 215 if err = rows.Err(); err != nil { 216 return err 217 } 218 if ok := rows.Next(); !ok { 219 break 220 } 221 } 222 223 return rows.Err() 224 }