github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/performance/serverbench/bench_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/doltdb"
    32  	"github.com/dolthub/dolt/go/libraries/doltcore/env"
    33  	"github.com/dolthub/dolt/go/libraries/utils/filesys"
    34  	"github.com/dolthub/dolt/go/store/types"
    35  )
    36  
    37  type query string
    38  
    39  type serverTest struct {
    40  	name  string
    41  	setup []query
    42  	bench []query
    43  }
    44  
    45  // usage: `go test -bench .`
    46  func BenchmarkServerExample(b *testing.B) {
    47  
    48  	setup := make([]query, 101)
    49  	setup[0] = "CREATE TABLE bench (pk int PRIMARY KEY AUTO_INCREMENT, c0 int);"
    50  
    51  	q := strings.Builder{}
    52  	q.WriteString("INSERT INTO bench (c0) VALUES (0)")
    53  	i := 1
    54  	for i < 1000 {
    55  		q.WriteString(fmt.Sprintf(",(%d)", i))
    56  		i++
    57  	}
    58  	qs := q.String()
    59  
    60  	for i := range setup {
    61  		if i == 0 {
    62  			continue
    63  		}
    64  		setup[i] = query(qs)
    65  	}
    66  
    67  	benchmarkServer(b, serverTest{
    68  		name:  "smoke bench",
    69  		setup: setup,
    70  		bench: []query{
    71  			"SELECT count(*) FROM bench;",
    72  		},
    73  	})
    74  }
    75  
    76  func benchmarkServer(b *testing.B, test serverTest) {
    77  	var dEnv *env.DoltEnv
    78  	var cfg srv.ServerConfig
    79  	ctx := context.Background()
    80  
    81  	// setup
    82  	dEnv, cfg = getEnvAndConfig(ctx, b)
    83  	executeServerQueries(ctx, b, dEnv, cfg, test.setup)
    84  
    85  	// bench
    86  	f := getProfFile(b)
    87  	err := pprof.StartCPUProfile(f)
    88  	if err != nil {
    89  		b.Fatal(err)
    90  	}
    91  	defer func() {
    92  		pprof.StopCPUProfile()
    93  		if err = f.Close(); err != nil {
    94  			b.Fatal(err)
    95  		}
    96  		fmt.Printf("\twriting CPU profile for %s: %s\n", b.Name(), f.Name())
    97  	}()
    98  
    99  	b.Run(test.name, func(b *testing.B) {
   100  		executeServerQueries(ctx, b, dEnv, cfg, test.bench)
   101  	})
   102  }
   103  
   104  const (
   105  	database = "dolt_bench"
   106  	port     = 1234
   107  
   108  	name  = "name"
   109  	email = "name@fake.horse"
   110  )
   111  
   112  func getEnvAndConfig(ctx context.Context, b *testing.B) (dEnv *env.DoltEnv, cfg srv.ServerConfig) {
   113  	tmp := b.TempDir()
   114  	//b.Logf("db directory: %s", tmp)
   115  	dbDir := path.Join(tmp, database)
   116  	err := os.Mkdir(dbDir, os.ModePerm)
   117  	if err != nil {
   118  		b.Fatal(err)
   119  	}
   120  
   121  	err = os.Chdir(dbDir)
   122  	if err != nil {
   123  		b.Fatal(err)
   124  	}
   125  
   126  	fs, err := filesys.LocalFilesysWithWorkingDir(".")
   127  	if err != nil {
   128  		b.Fatal(err)
   129  	}
   130  
   131  	dEnv = env.Load(ctx, os.UserHomeDir, fs, doltdb.LocalDirDoltDB, "bench")
   132  	err = dEnv.InitRepo(ctx, types.Format_7_18, name, email)
   133  	if err != nil {
   134  		b.Fatal(err)
   135  	}
   136  
   137  	yaml := []byte(fmt.Sprintf(`
   138  log_level: warning
   139  
   140  behavior:
   141   read_only: false
   142  
   143  user:
   144   name: "root"
   145   password: ""
   146  
   147  databases:
   148   - name: "%s"
   149     path: "%s"
   150  
   151  listener:
   152   host: localhost
   153   port: %d
   154   max_connections: 128
   155   read_timeout_millis: 28800000
   156   write_timeout_millis: 28800000
   157  `, database, dbDir, port))
   158  
   159  	cfg, err = srv.NewYamlConfig(yaml)
   160  	if err != nil {
   161  		b.Fatal(err)
   162  	}
   163  
   164  	return dEnv, cfg
   165  }
   166  
   167  func getProfFile(b *testing.B) *os.File {
   168  	_, testFile, _, _ := runtime.Caller(0)
   169  
   170  	f, err := os.Create(path.Join(path.Dir(testFile), b.Name()+".out"))
   171  	if err != nil {
   172  		b.Fatal(err)
   173  	}
   174  	return f
   175  }
   176  
   177  func executeServerQueries(ctx context.Context, b *testing.B, dEnv *env.DoltEnv, cfg srv.ServerConfig, queries []query) {
   178  	serverController := srv.CreateServerController()
   179  
   180  	eg, ctx := errgroup.WithContext(ctx)
   181  
   182  	//b.Logf("Starting server with Config %v\n", srv.ConfigInfo(cfg))
   183  	eg.Go(func() (err error) {
   184  		startErr, closeErr := srv.Serve(ctx, "", cfg, serverController, dEnv)
   185  		if startErr != nil {
   186  			return startErr
   187  		}
   188  		if closeErr != nil {
   189  			return closeErr
   190  		}
   191  		return nil
   192  	})
   193  	if err := serverController.WaitForStart(); err != nil {
   194  		b.Fatal(err)
   195  	}
   196  
   197  	for _, q := range queries {
   198  		if err := executeQuery(cfg, q); err != nil {
   199  			b.Fatal(err)
   200  		}
   201  	}
   202  
   203  	serverController.StopServer()
   204  	if err := serverController.WaitForClose(); err != nil {
   205  		b.Fatal(err)
   206  	}
   207  	if err := eg.Wait(); err != nil {
   208  		b.Fatal(err)
   209  	}
   210  }
   211  
   212  func executeQuery(cfg srv.ServerConfig, q query) error {
   213  	cs := srv.ConnectionString(cfg) + database
   214  	conn, err := dbr.Open("mysql", cs, nil)
   215  	if err != nil {
   216  		return err
   217  	}
   218  
   219  	rows, err := conn.Query(string(q))
   220  	if err != nil {
   221  		return err
   222  	}
   223  
   224  	for {
   225  		if err = rows.Err(); err != nil {
   226  			return err
   227  		}
   228  		if ok := rows.Next(); !ok {
   229  			break
   230  		}
   231  	}
   232  
   233  	return rows.Err()
   234  }