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 }