github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/performance/benchmarks/benchmarks.go (about)

     1  // Copyright 2019 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 main
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"io/ioutil"
    21  	"log"
    22  	"os"
    23  	"path/filepath"
    24  	"testing"
    25  
    26  	"github.com/dolthub/dolt/go/cmd/dolt/commands"
    27  	"github.com/dolthub/dolt/go/cmd/dolt/commands/tblcmds"
    28  	"github.com/dolthub/dolt/go/libraries/doltcore/dbfactory"
    29  	"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
    30  	"github.com/dolthub/dolt/go/libraries/doltcore/env"
    31  	"github.com/dolthub/dolt/go/libraries/utils/filesys"
    32  	"github.com/dolthub/dolt/go/libraries/utils/test"
    33  	"github.com/dolthub/dolt/go/store/types"
    34  )
    35  
    36  const (
    37  	testHomeDir = "/user/tester"
    38  )
    39  
    40  type doltCommandFunc func(ctx context.Context, commandStr string, args []string, dEnv *env.DoltEnv) int
    41  
    42  func removeTempDoltDataDir(fs filesys.Filesys) {
    43  	cwd, err := os.Getwd()
    44  	if err != nil {
    45  		log.Fatal(err)
    46  	}
    47  	doltDir := filepath.Join(cwd, dbfactory.DoltDir)
    48  	exists, _ := fs.Exists(doltDir)
    49  	if exists {
    50  		err := fs.Delete(doltDir, true)
    51  		if err != nil {
    52  			log.Fatal(err)
    53  		}
    54  	}
    55  }
    56  
    57  func getWorkingDir(fs filesys.Filesys) string {
    58  	workingDir := test.TestDir(testHomeDir)
    59  	err := fs.MkDirs(workingDir)
    60  	if err != nil {
    61  		log.Fatal(err)
    62  	}
    63  	return workingDir
    64  }
    65  
    66  func createTestEnvWithFS(fs filesys.Filesys, workingDir string) *env.DoltEnv {
    67  	removeTempDoltDataDir(fs)
    68  	testHomeDirFunc := func() (string, error) { return workingDir, nil }
    69  	const name = "test mcgibbins"
    70  	const email = "bigfakeytester@fake.horse"
    71  	dEnv := env.Load(context.Background(), testHomeDirFunc, fs, doltdb.LocalDirDoltDB, "test")
    72  	err := dEnv.InitRepo(context.Background(), types.Format_7_18, name, email)
    73  	if err != nil {
    74  		panic("Failed to initialize environment")
    75  	}
    76  	return dEnv
    77  }
    78  
    79  // BenchmarkDoltImport returns a function that runs benchmarks for importing
    80  // a test dataset into Dolt
    81  func BenchmarkDoltImport(rows int, cols []*SeedColumn, format string) func(b *testing.B) {
    82  	fs := filesys.LocalFS
    83  	wd := getWorkingDir(fs)
    84  	return func(b *testing.B) {
    85  		doltImport(b, fs, rows, cols, wd, format)
    86  	}
    87  }
    88  
    89  // BenchmarkDoltExport returns a function that runs benchmarks for exporting
    90  // a test dataset out of Dolt
    91  func BenchmarkDoltExport(rows int, cols []*SeedColumn, format string) func(b *testing.B) {
    92  	fs := filesys.LocalFS
    93  	wd := getWorkingDir(fs)
    94  	return func(b *testing.B) {
    95  		doltExport(b, fs, rows, cols, wd, format)
    96  	}
    97  }
    98  
    99  // BenchmarkDoltSQLSelect returns a function that runs benchmarks for executing a sql query
   100  // against a Dolt table
   101  func BenchmarkDoltSQLSelect(rows int, cols []*SeedColumn, format string) func(b *testing.B) {
   102  	fs := filesys.LocalFS
   103  	wd := getWorkingDir(fs)
   104  	return func(b *testing.B) {
   105  		doltSQLSelect(b, fs, rows, cols, wd, format)
   106  	}
   107  }
   108  
   109  func doltImport(b *testing.B, fs filesys.Filesys, rows int, cols []*SeedColumn, workingDir, format string) {
   110  	pathToImportFile := filepath.Join(workingDir, fmt.Sprintf("testData%s", format))
   111  
   112  	oldStdin := os.Stdin
   113  	defer func() { os.Stdin = oldStdin }()
   114  
   115  	commandFunc, commandStr, args, dEnv := getBenchmarkingTools(fs, rows, cols, workingDir, pathToImportFile, format)
   116  
   117  	runBenchmark(b, commandFunc, commandStr, args, dEnv)
   118  }
   119  
   120  func doltExport(b *testing.B, fs filesys.Filesys, rows int, cols []*SeedColumn, workingDir, format string) {
   121  	pathToImportFile := filepath.Join(workingDir, fmt.Sprintf("testData%s", format))
   122  	oldStdin := os.Stdin
   123  
   124  	commandFunc, commandStr, args, dEnv := getBenchmarkingTools(fs, rows, cols, workingDir, pathToImportFile, format)
   125  
   126  	// import
   127  	status := commandFunc(context.Background(), commandStr, args, dEnv)
   128  	if status != 0 {
   129  		log.Fatalf("failed to import table successfully with exit code %d \n", status)
   130  	}
   131  
   132  	// revert stdin
   133  	os.Stdin = oldStdin
   134  
   135  	args = []string{"-f", "testTable", pathToImportFile}
   136  	runBenchmark(b, tblcmds.ExportCmd{}.Exec, "dolt table export", args, dEnv)
   137  }
   138  
   139  func doltSQLSelect(b *testing.B, fs filesys.Filesys, rows int, cols []*SeedColumn, workingDir, format string) {
   140  	testTable := "testTable"
   141  	pathToImportFile := filepath.Join(workingDir, fmt.Sprintf("testData%s", format))
   142  
   143  	oldStdin := os.Stdin
   144  
   145  	commandFunc, commandStr, args, dEnv := getBenchmarkingTools(fs, rows, cols, workingDir, pathToImportFile, format)
   146  
   147  	// import
   148  	status := commandFunc(context.Background(), commandStr, args, dEnv)
   149  	if status != 0 {
   150  		log.Fatalf("failed to import table successfully with exit code %d \n", status)
   151  	}
   152  
   153  	// revert stdin
   154  	os.Stdin = oldStdin
   155  
   156  	args = []string{"-q", fmt.Sprintf("select count(*) from %s", testTable)}
   157  	runBenchmark(b, commands.SqlCmd{}.Exec, "dolt sql", args, dEnv)
   158  }
   159  
   160  func runBenchmark(b *testing.B, commandFunc doltCommandFunc, commandStr string, args []string, dEnv *env.DoltEnv) {
   161  	b.ResetTimer()
   162  	for i := 0; i < b.N; i++ {
   163  		status := commandFunc(context.Background(), commandStr, args, dEnv)
   164  		if status != 0 {
   165  			log.Fatalf("running benchmark failed with exit code... %d \n", status)
   166  		}
   167  	}
   168  }
   169  
   170  func getBenchmarkingTools(fs filesys.Filesys, rows int, cols []*SeedColumn, workingDir, pathToImportFile, format string) (commandFunc doltCommandFunc, commandStr string, args []string, dEnv *env.DoltEnv) {
   171  	testTable := "testTable"
   172  	sch := NewSeedSchema(rows, cols, format)
   173  
   174  	switch format {
   175  	case csvExt:
   176  		dEnv = setupDEnvImport(fs, sch, workingDir, testTable, "", pathToImportFile)
   177  		args = []string{"-c", "-f", testTable, pathToImportFile}
   178  		commandStr = "dolt table import"
   179  		commandFunc = tblcmds.ImportCmd{}.Exec
   180  	case sqlExt:
   181  		dEnv = setupDEnvImport(fs, sch, workingDir, testTable, "", pathToImportFile)
   182  		args = []string{}
   183  		commandStr = "dolt sql"
   184  		commandFunc = commands.SqlCmd{}.Exec
   185  
   186  		stdin := getStdinForSQLBenchmark(fs, pathToImportFile)
   187  		os.Stdin = stdin
   188  	case jsonExt:
   189  		pathToSchemaFile := filepath.Join(workingDir, fmt.Sprintf("testSchema%s", format))
   190  		dEnv = setupDEnvImport(fs, sch, workingDir, testTable, pathToSchemaFile, pathToImportFile)
   191  		args = []string{"-c", "-f", "-s", pathToSchemaFile, testTable, pathToImportFile}
   192  		commandStr = "dolt table import"
   193  		commandFunc = tblcmds.ImportCmd{}.Exec
   194  	default:
   195  		log.Fatalf("cannot import file, unsupported file format %s \n", format)
   196  	}
   197  
   198  	return commandFunc, commandStr, args, dEnv
   199  }
   200  
   201  func setupDEnvImport(fs filesys.Filesys, sch *SeedSchema, workingDir, tableName, pathToSchemaFile, pathToImportFile string) *env.DoltEnv {
   202  	wc, err := fs.OpenForWrite(pathToImportFile, os.ModePerm)
   203  	if err != nil {
   204  		log.Fatal(err)
   205  	}
   206  	defer wc.Close()
   207  
   208  	ds := NewDSImpl(wc, sch, seedRandom, tableName)
   209  
   210  	if pathToSchemaFile != "" {
   211  		// write schema file
   212  		err := fs.WriteFile(pathToSchemaFile, sch.Bytes())
   213  		if err != nil {
   214  			panic("unable to write data file to filesystem")
   215  		}
   216  	}
   217  
   218  	ds.GenerateData()
   219  	return createTestEnvWithFS(fs, workingDir)
   220  }
   221  
   222  func getStdinForSQLBenchmark(fs filesys.Filesys, pathToImportFile string) *os.File {
   223  	content, err := fs.ReadFile(pathToImportFile)
   224  	if err != nil {
   225  		log.Fatal(err)
   226  	}
   227  
   228  	tmpfile, err := ioutil.TempFile("", "temp")
   229  	if err != nil {
   230  		log.Fatal(err)
   231  	}
   232  	defer os.Remove(tmpfile.Name()) // clean up
   233  
   234  	if _, err := tmpfile.Write(content); err != nil {
   235  		log.Fatal(err)
   236  	}
   237  	if err := tmpfile.Close(); err != nil {
   238  		log.Fatal(err)
   239  	}
   240  
   241  	f, err := os.Open(tmpfile.Name())
   242  	if err != nil {
   243  		log.Fatal(err)
   244  	}
   245  
   246  	return f
   247  }