github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/ccl/workloadccl/fixture_test.go (about) 1 // Copyright 2018 The Cockroach Authors. 2 // 3 // Licensed as a CockroachDB Enterprise file under the Cockroach Community 4 // License (the "License"); you may not use this file except in compliance with 5 // the License. You may obtain a copy of the License at 6 // 7 // https://github.com/cockroachdb/cockroach/blob/master/licenses/CCL.txt 8 9 package workloadccl_test 10 11 import ( 12 "context" 13 "fmt" 14 "net/http/httptest" 15 "os" 16 "strconv" 17 "strings" 18 "testing" 19 "time" 20 21 "cloud.google.com/go/storage" 22 "github.com/cockroachdb/cockroach/pkg/base" 23 _ "github.com/cockroachdb/cockroach/pkg/ccl" 24 "github.com/cockroachdb/cockroach/pkg/ccl/workloadccl" 25 "github.com/cockroachdb/cockroach/pkg/sql/stats" 26 "github.com/cockroachdb/cockroach/pkg/testutils" 27 "github.com/cockroachdb/cockroach/pkg/testutils/serverutils" 28 "github.com/cockroachdb/cockroach/pkg/testutils/sqlutils" 29 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 30 "github.com/cockroachdb/cockroach/pkg/util/timeutil" 31 "github.com/cockroachdb/cockroach/pkg/workload" 32 "github.com/spf13/pflag" 33 "github.com/stretchr/testify/require" 34 "golang.org/x/oauth2/google" 35 "google.golang.org/api/option" 36 ) 37 38 const fixtureTestGenRows = 10 39 40 type fixtureTestGen struct { 41 flags workload.Flags 42 val string 43 empty string 44 } 45 46 func makeTestWorkload() workload.Flagser { 47 g := &fixtureTestGen{} 48 g.flags.FlagSet = pflag.NewFlagSet(`fx`, pflag.ContinueOnError) 49 g.flags.StringVar(&g.val, `val`, `default`, `The value for each row`) 50 g.flags.StringVar(&g.empty, `empty`, ``, `An empty flag`) 51 return g 52 } 53 54 var fixtureTestMeta = workload.Meta{ 55 Name: `fixture`, 56 New: func() workload.Generator { 57 return makeTestWorkload() 58 }, 59 } 60 61 func init() { 62 workload.Register(fixtureTestMeta) 63 } 64 65 func (fixtureTestGen) Meta() workload.Meta { return fixtureTestMeta } 66 func (g fixtureTestGen) Flags() workload.Flags { return g.flags } 67 func (g fixtureTestGen) Tables() []workload.Table { 68 return []workload.Table{{ 69 Name: `fx`, 70 Schema: `(key INT PRIMARY KEY, value INT)`, 71 InitialRows: workload.Tuples( 72 fixtureTestGenRows, 73 func(rowIdx int) []interface{} { 74 return []interface{}{rowIdx, g.val} 75 }, 76 ), 77 Stats: []workload.JSONStatistic{ 78 // Use stats that *don't* match reality, so we can test that these 79 // stats were injected and not calculated by CREATE STATISTICS. 80 workload.MakeStat([]string{"key"}, 100, 100, 0), 81 workload.MakeStat([]string{"value"}, 100, 1, 5), 82 }, 83 }} 84 } 85 86 func TestFixture(t *testing.T) { 87 defer leaktest.AfterTest(t)() 88 ctx := context.Background() 89 90 gcsBucket := os.Getenv(`GS_BUCKET`) 91 gcsKey := os.Getenv(`GS_JSONKEY`) 92 if gcsBucket == "" || gcsKey == "" { 93 t.Skip("GS_BUCKET and GS_JSONKEY env vars must be set") 94 } 95 96 source, err := google.JWTConfigFromJSON([]byte(gcsKey), storage.ScopeReadWrite) 97 if err != nil { 98 t.Fatalf(`%+v`, err) 99 } 100 gcs, err := storage.NewClient(ctx, 101 option.WithScopes(storage.ScopeReadWrite), 102 option.WithTokenSource(source.TokenSource(ctx))) 103 if err != nil { 104 t.Fatalf(`%+v`, err) 105 } 106 defer func() { _ = gcs.Close() }() 107 108 s, db, _ := serverutils.StartServer(t, base.TestServerArgs{}) 109 defer s.Stopper().Stop(ctx) 110 sqlDB := sqlutils.MakeSQLRunner(db) 111 sqlDB.Exec(t, `SET CLUSTER SETTING cloudstorage.gs.default.key = $1`, gcsKey) 112 113 gen := makeTestWorkload() 114 flag := fmt.Sprintf(`val=%d`, timeutil.Now().UnixNano()) 115 if err := gen.Flags().Parse([]string{"--" + flag}); err != nil { 116 t.Fatalf(`%+v`, err) 117 } 118 119 config := workloadccl.FixtureConfig{ 120 GCSBucket: gcsBucket, 121 GCSPrefix: fmt.Sprintf(`TestFixture-%d`, timeutil.Now().UnixNano()), 122 } 123 124 if _, err := workloadccl.GetFixture(ctx, gcs, config, gen); !testutils.IsError(err, `fixture not found`) { 125 t.Fatalf(`expected "fixture not found" error but got: %+v`, err) 126 } 127 128 fixtures, err := workloadccl.ListFixtures(ctx, gcs, config) 129 if err != nil { 130 t.Fatalf(`%+v`, err) 131 } 132 if len(fixtures) != 0 { 133 t.Errorf(`expected no fixtures but got: %+v`, fixtures) 134 } 135 136 const filesPerNode = 1 137 fixture, err := workloadccl.MakeFixture(ctx, db, gcs, config, gen, filesPerNode) 138 if err != nil { 139 t.Fatalf(`%+v`, err) 140 } 141 142 _, err = workloadccl.MakeFixture(ctx, db, gcs, config, gen, filesPerNode) 143 if !testutils.IsError(err, `already exists`) { 144 t.Fatalf(`expected 'already exists' error got: %+v`, err) 145 } 146 147 fixtures, err = workloadccl.ListFixtures(ctx, gcs, config) 148 if err != nil { 149 t.Fatalf(`%+v`, err) 150 } 151 if len(fixtures) != 1 || !strings.Contains(fixtures[0], flag) { 152 t.Errorf(`expected exactly one %s fixture but got: %+v`, flag, fixtures) 153 } 154 155 sqlDB.Exec(t, `CREATE DATABASE test`) 156 if _, err := workloadccl.RestoreFixture(ctx, db, fixture, `test`, false); err != nil { 157 t.Fatalf(`%+v`, err) 158 } 159 sqlDB.CheckQueryResults(t, 160 `SELECT count(*) FROM test.fx`, [][]string{{strconv.Itoa(fixtureTestGenRows)}}) 161 } 162 163 func TestImportFixture(t *testing.T) { 164 defer leaktest.AfterTest(t)() 165 ctx := context.Background() 166 167 defer func(oldRefreshInterval, oldAsOf time.Duration) { 168 stats.DefaultRefreshInterval = oldRefreshInterval 169 stats.DefaultAsOfTime = oldAsOf 170 }(stats.DefaultRefreshInterval, stats.DefaultAsOfTime) 171 stats.DefaultRefreshInterval = time.Millisecond 172 stats.DefaultAsOfTime = 10 * time.Millisecond 173 174 s, db, _ := serverutils.StartServer(t, base.TestServerArgs{}) 175 defer s.Stopper().Stop(ctx) 176 sqlDB := sqlutils.MakeSQLRunner(db) 177 178 sqlDB.Exec(t, `SET CLUSTER SETTING sql.stats.automatic_collection.enabled=true`) 179 180 gen := makeTestWorkload() 181 flag := fmt.Sprintf(`val=%d`, timeutil.Now().UnixNano()) 182 if err := gen.Flags().Parse([]string{"--" + flag}); err != nil { 183 t.Fatalf(`%+v`, err) 184 } 185 186 const filesPerNode = 1 187 188 sqlDB.Exec(t, `CREATE DATABASE ingest`) 189 _, err := workloadccl.ImportFixture( 190 ctx, db, gen, `ingest`, filesPerNode, false, /* injectStats */ 191 ``, /* csvServer */ 192 ) 193 require.NoError(t, err) 194 sqlDB.CheckQueryResults(t, 195 `SELECT count(*) FROM ingest.fx`, [][]string{{strconv.Itoa(fixtureTestGenRows)}}) 196 197 // Since we did not inject stats, the IMPORT should have triggered 198 // automatic stats collection. 199 sqlDB.CheckQueryResultsRetry(t, 200 `SELECT statistics_name, column_names, row_count, distinct_count, null_count 201 FROM [SHOW STATISTICS FOR TABLE ingest.fx]`, 202 [][]string{ 203 {"__auto__", "{key}", "10", "10", "0"}, 204 {"__auto__", "{value}", "10", "1", "0"}, 205 }) 206 } 207 208 func TestImportFixtureCSVServer(t *testing.T) { 209 defer leaktest.AfterTest(t)() 210 ctx := context.Background() 211 ts := httptest.NewServer(workload.CSVMux(workload.Registered())) 212 defer ts.Close() 213 214 s, db, _ := serverutils.StartServer(t, base.TestServerArgs{UseDatabase: `d`}) 215 defer s.Stopper().Stop(ctx) 216 sqlDB := sqlutils.MakeSQLRunner(db) 217 218 gen := makeTestWorkload() 219 flag := fmt.Sprintf(`val=%d`, timeutil.Now().UnixNano()) 220 if err := gen.Flags().Parse([]string{"--" + flag}); err != nil { 221 t.Fatalf(`%+v`, err) 222 } 223 224 const filesPerNode = 1 225 const noInjectStats = false 226 sqlDB.Exec(t, `CREATE DATABASE d`) 227 _, err := workloadccl.ImportFixture( 228 ctx, db, gen, `d`, filesPerNode, noInjectStats, ts.URL, 229 ) 230 require.NoError(t, err) 231 sqlDB.CheckQueryResults(t, 232 `SELECT count(*) FROM d.fx`, [][]string{{strconv.Itoa(fixtureTestGenRows)}}) 233 }