github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/cmd/roachtest/sqlsmith.go (about) 1 // Copyright 2019 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package main 12 13 import ( 14 "context" 15 "fmt" 16 "math/rand" 17 "os" 18 "path/filepath" 19 "strings" 20 "time" 21 22 "github.com/cockroachdb/cockroach/pkg/internal/sqlsmith" 23 "github.com/cockroachdb/cockroach/pkg/util/randutil" 24 ) 25 26 func registerSQLSmith(r *testRegistry) { 27 setups := map[string]sqlsmith.Setup{ 28 "empty": sqlsmith.Setups["empty"], 29 "seed": sqlsmith.Setups["seed"], 30 "rand-tables": sqlsmith.Setups["rand-tables"], 31 "tpch-sf1": func(r *rand.Rand) string { 32 return `RESTORE TABLE tpch.* FROM 'gs://cockroach-fixtures/workload/tpch/scalefactor=1/backup' WITH into_db = 'defaultdb';` 33 }, 34 "tpcc": func(r *rand.Rand) string { 35 const version = "version=2.1.0,fks=true,interleaved=false,seed=1,warehouses=1" 36 var sb strings.Builder 37 for _, t := range []string{ 38 "customer", 39 "district", 40 "history", 41 "item", 42 "new_order", 43 "order", 44 "order_line", 45 "stock", 46 "warehouse", 47 } { 48 fmt.Fprintf(&sb, "RESTORE TABLE tpcc.%s FROM 'gs://cockroach-fixtures/workload/tpcc/%[2]s/%[1]s' WITH into_db = 'defaultdb';\n", t, version) 49 } 50 return sb.String() 51 }, 52 } 53 settings := map[string]sqlsmith.SettingFunc{ 54 "default": sqlsmith.Settings["default"], 55 "no-mutations": sqlsmith.Settings["no-mutations"], 56 "no-ddl": sqlsmith.Settings["no-ddl"], 57 } 58 59 runSQLSmith := func(ctx context.Context, t *test, c *cluster, setupName, settingName string) { 60 // Set up a statement logger for easy reproduction. We only 61 // want to log successful statements and statements that 62 // produced a final error or panic. 63 smithLog, err := os.Create(filepath.Join(t.artifactsDir, "sqlsmith.log")) 64 if err != nil { 65 t.Fatalf("could not create sqlsmith.log: %v", err) 66 } 67 defer smithLog.Close() 68 logStmt := func(stmt string) { 69 stmt = strings.TrimSpace(stmt) 70 if stmt == "" { 71 return 72 } 73 fmt.Fprint(smithLog, stmt) 74 if !strings.HasSuffix(stmt, ";") { 75 fmt.Fprint(smithLog, ";") 76 } 77 fmt.Fprint(smithLog, "\n\n") 78 } 79 80 rng, seed := randutil.NewPseudoRand() 81 c.l.Printf("seed: %d", seed) 82 83 c.Put(ctx, cockroach, "./cockroach") 84 c.Start(ctx, t) 85 86 setupFunc, ok := setups[setupName] 87 if !ok { 88 t.Fatalf("unknown setup %s", setupName) 89 } 90 settingFunc, ok := settings[settingName] 91 if !ok { 92 t.Fatalf("unknown setting %s", settingName) 93 } 94 95 setup := setupFunc(rng) 96 setting := settingFunc(rng) 97 98 conn := c.Conn(ctx, 1) 99 t.Status("executing setup") 100 c.l.Printf("setup:\n%s", setup) 101 if _, err := conn.Exec(setup); err != nil { 102 t.Fatal(err) 103 } else { 104 logStmt(setup) 105 } 106 107 const timeout = time.Minute 108 setStmtTimeout := fmt.Sprintf("SET statement_timeout='%s';", timeout.String()) 109 t.Status("setting statement_timeout") 110 c.l.Printf("statement timeout:\n%s", setStmtTimeout) 111 if _, err := conn.Exec(setStmtTimeout); err != nil { 112 t.Fatal(err) 113 } 114 logStmt(setStmtTimeout) 115 116 smither, err := sqlsmith.NewSmither(conn, rng, setting.Options...) 117 if err != nil { 118 t.Fatal(err) 119 } 120 121 t.Status("smithing") 122 until := time.After(t.spec.Timeout / 2) 123 done := ctx.Done() 124 for i := 1; ; i++ { 125 if i%10000 == 0 { 126 t.Status("smithing: ", i, " statements completed") 127 } 128 select { 129 case <-done: 130 return 131 case <-until: 132 return 133 default: 134 } 135 stmt := smither.Generate() 136 err := func() error { 137 done := make(chan error, 1) 138 go func(context.Context) { 139 // At the moment, CockroachDB doesn't support pgwire query 140 // cancellation which is needed for correct handling of context 141 // cancellation, so instead of using a context with timeout, we opt 142 // in for using CRDB's 'statement_timeout'. 143 // TODO(yuzefovich): once #41335 is implemented, go back to using a 144 // context with timeout. 145 _, err := conn.Exec(stmt) 146 if err == nil { 147 logStmt(stmt) 148 } 149 done <- err 150 }(ctx) 151 select { 152 case <-time.After(timeout * 2): 153 // SQLSmith generates queries that either perform full table scans of 154 // large tables or backup/restore operations that timeout. These 155 // should not cause an issue to be raised, as they most likely are 156 // just timing out. 157 // TODO (rohany): once #45463 and #45461 have been resolved, return 158 // to calling t.Fatalf here. 159 c.l.Printf("query timed out, but did not cancel execution:\n%s;", stmt) 160 return nil 161 case err := <-done: 162 return err 163 } 164 }() 165 if err != nil { 166 es := err.Error() 167 if strings.Contains(es, "internal error") { 168 logStmt(stmt) 169 t.Fatalf("error: %s\nstmt:\n%s;", err, stmt) 170 } else if strings.Contains(es, "communication error") { 171 // A communication error can be because 172 // a non-gateway node has crashed. 173 logStmt(stmt) 174 t.Fatalf("error: %s\nstmt:\n%s;", err, stmt) 175 } 176 // Ignore other errors because they happen so 177 // frequently (due to sqlsmith not crafting 178 // executable queries 100% of the time) and are 179 // never interesting. 180 } 181 182 // Ping the gateway to make sure it didn't crash. 183 if err := conn.PingContext(ctx); err != nil { 184 logStmt(stmt) 185 t.Fatalf("ping: %v\nprevious sql:\n%s;", err, stmt) 186 } 187 } 188 } 189 190 register := func(setup, setting string) { 191 r.Add(testSpec{ 192 Name: fmt.Sprintf("sqlsmith/setup=%s/setting=%s", setup, setting), 193 // NB: sqlsmith failures should never block a release. 194 Owner: OwnerSQLExec, 195 Cluster: makeClusterSpec(4), 196 MinVersion: "v20.2.0", 197 Timeout: time.Minute * 20, 198 Run: func(ctx context.Context, t *test, c *cluster) { 199 runSQLSmith(ctx, t, c, setup, setting) 200 }, 201 }) 202 } 203 204 for setup := range setups { 205 for setting := range settings { 206 register(setup, setting) 207 } 208 } 209 setups["seed-vec"] = sqlsmith.Setups["seed-vec"] 210 settings["ddl-nodrop"] = sqlsmith.Settings["ddl-nodrop"] 211 settings["vec"] = sqlsmith.SettingVectorize 212 register("seed-vec", "vec") 213 register("tpcc", "ddl-nodrop") 214 }