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  }