From dd596e02b2b32a40c049b17ebe167526d3f9baaa Mon Sep 17 00:00:00 2001 From: Dmitry Kropachev Date: Mon, 24 Apr 2023 16:02:54 -0400 Subject: [PATCH 1/4] Add makefile with check and fix stages --- .github/workflows/go.yml | 6 +++- .gitignore | 3 +- .golangci.yml | 64 ++++++++++++++++++++++++++++++++++++++++ Makefile | 45 ++++++++++++++++++++++++++++ 4 files changed, 116 insertions(+), 2 deletions(-) create mode 100644 .golangci.yml create mode 100644 Makefile diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index b622483c..186df93a 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -8,7 +8,7 @@ on: jobs: build: - name: Build + name: Lint Test and Build runs-on: ubuntu-latest steps: - name: @@ -19,6 +19,10 @@ jobs: with: go-version: 1.14 + - name: Linting + run: | + make check + - name: Unit Tests run: | go test -v -race ./... diff --git a/.gitignore b/.gitignore index 1167e381..74d6fea4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ cmd/gemini/dist/ /gemini -.idea \ No newline at end of file +.idea +bin/ diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 00000000..7c58c516 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,64 @@ +issues: + exclude: + - Error return value of `.*.Unlock` is not checked + - Error return value of `.*.Completed` is not checked +linters: + disable-all: true + enable: + - errcheck + - govet + - ineffassign + - unused + - goheader + - goimports + - revive + - misspell + - gofumpt + - gosimple + - staticcheck + - lll + - errorlint +run: + deadline: 10m + modules-download-mode: readonly +linters-settings: + govet: + check-shadowing: true + settings: + shadow: + strict: true + enable-all: true + lll: + line-length: 180 + goheader: + values: + regexp: + spaces: \s+ + two-digits: \d\d + template: 'Copyright 20{{two-digits}} ScyllaDB + + +Licensed under the Apache License, Version 2.0 (the "License"); + +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at + + +{{spaces}}http://www.apache.org/licenses/LICENSE-2.0 + + +Unless required by applicable law or agreed to in writing, software + +distributed under the License is distributed on an "AS IS" BASIS, + +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and + +limitations under the License.' + gofumpt: + lang-version: "1.12" + extra-rules: true + goimports: + local-prefixes: github.com/scylladb/gemini \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..219b8a19 --- /dev/null +++ b/Makefile @@ -0,0 +1,45 @@ +GOPACKAGES := $(shell go list ./...) +MAKEFILE_PATH := $(abspath $(dir $(abspath $(lastword $(MAKEFILE_LIST))))) + +GO111MODULE := on +# GO_UPGRADE - do not remove this comment, used by scripts/go-upgrade.sh +GOVERSION ?= 1.12 +GOOS := $(shell uname | tr '[:upper:]' '[:lower:]') +GOARCH := $(shell go env GOARCH) + +ifndef GOBIN +export GOBIN := $(MAKEFILE_PATH)/bin +endif + +define dl_tgz + @mkdir -p $(GOBIN) 2>/dev/null + + @if [ ! -f "$(GOBIN)/$(1)" ]; then \ + echo "Downloading $(GOBIN)/$(1)"; \ + curl --progress-bar -L $(2) | tar zxf - --wildcards --strip 1 -C $(GOBIN) '*/$(1)'; \ + chmod +x "$(GOBIN)/$(1)"; \ + fi +endef + +$(GOBIN)/golangci-lint: GOLANGCI_VERSION = 1.51.1 +$(GOBIN)/golangci-lint: Makefile + $(call dl_tgz,golangci-lint,https://github.com/golangci/golangci-lint/releases/download/v$(GOLANGCI_VERSION)/golangci-lint-$(GOLANGCI_VERSION)-$(GOOS)-amd64.tar.gz) + +.PHONY: check-golangci +check-golangci: $(GOBIN)/golangci-lint + @echo $(GOPACKAGES) | sed -e 's/github.com\/scylladb\/gemini/./g' | \ + xargs $(GOBIN)/golangci-lint run --max-issues-per-linter=0 --max-same-issues=0 + +# fix-golangci Automated fix for golangci-lint errors. +.PHONY: fix-golangci +fix-golangci: $(GOBIN)/golangci-lint + @echo $(GOPACKAGES) | sed -e 's/github.com\/scylladb\/gemini/./g' | \ + xargs $(GOBIN)/golangci-lint run --fix + +# check Run all static code analysis. (use make fix to attempt automatic fix) +.PHONY: check +check: check-golangci + +# fix make all static code analysis tools to fix the issues +.PHONY: fix +fix: fix-golangci From b85a24290d06bdc7cf0878c6bd8bffaca51e26e2 Mon Sep 17 00:00:00 2001 From: Dmitry Kropachev Date: Mon, 24 Apr 2023 16:03:30 -0400 Subject: [PATCH 2/4] Fix datautils test --- datautils.go | 2 +- datautils_slow_test.go | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/datautils.go b/datautils.go index cb92f328..ecb1893f 100644 --- a/datautils.go +++ b/datautils.go @@ -36,7 +36,7 @@ func randStringWithTime(rnd *rand.Rand, len int, t time.Time) string { // Pad some extra random data buff := make([]byte, len-buf.Len()) - rnd.Read(buff) + _, _ = rnd.Read(buff) buf.WriteString(base64.StdEncoding.EncodeToString(buff)) return buf.String()[:len] diff --git a/datautils_slow_test.go b/datautils_slow_test.go index 74857f6d..be3d7052 100644 --- a/datautils_slow_test.go +++ b/datautils_slow_test.go @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +//go:build slow // +build slow package gemini @@ -26,7 +27,10 @@ func TestNonEmptyRandString(t *testing.T) { // TODO: Figure out why this is so horribly slow... tt := time.Now() f := func(len int32) bool { - r := nonEmptyRandStringWithTime(rnd, int(len), tt) + if len < 0 { + len = -len + } + r := randStringWithTime(rnd, int(len), tt) return r != "" } cfg := &quick.Config{MaxCount: 10} From 823ead2fc1e22d664095713e3a8be586dd3c6d2a Mon Sep 17 00:00:00 2001 From: Dmitry Kropachev Date: Mon, 24 Apr 2023 17:14:29 -0400 Subject: [PATCH 3/4] Address linter complaints --- .golangci.yml | 1 - auth/auth.go | 5 +- auth/auth_test.go | 3 +- cmd/gemini/generators.go | 15 +- cmd/gemini/jobs.go | 109 ++++++++++-- cmd/gemini/main.go | 3 +- cmd/gemini/pump.go | 4 +- cmd/gemini/root.go | 121 ++++++++----- cmd/gemini/schema.go | 3 +- cmd/gemini/status.go | 8 +- cmd/gemini/strategies_test.go | 16 +- datautils.go | 3 +- datautils_test.go | 3 +- generator.go | 20 +-- generator_test.go | 3 +- inflight/inflight.go | 2 +- murmur/murmur.go | 3 +- murmur/murmur_test.go | 4 +- murmur/murmur_unsafe.go | 1 + partition.go | 3 +- replication/replication_test.go | 3 +- routing_key_test.go | 3 +- schema.go | 143 ++++++++-------- schema_test.go | 16 +- store/cqlstore.go | 20 ++- store/helpers.go | 4 +- store/store.go | 12 +- tableopts/options.go | 5 +- tableopts/options_test.go | 5 +- types.go | 291 +++++++++++++++++--------------- types_test.go | 39 ++--- utils.go | 19 +++ 32 files changed, 525 insertions(+), 365 deletions(-) create mode 100644 utils.go diff --git a/.golangci.yml b/.golangci.yml index 7c58c516..7fc6b32f 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -11,7 +11,6 @@ linters: - unused - goheader - goimports - - revive - misspell - gofumpt - gosimple diff --git a/auth/auth.go b/auth/auth.go index e2957641..ec8ab4e1 100644 --- a/auth/auth.go +++ b/auth/auth.go @@ -4,14 +4,13 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - package auth import ( @@ -22,7 +21,7 @@ import ( // BuildAuthenticator : Returns a new gocql.PasswordAuthenticator // if both username and password are provided. -func BuildAuthenticator(username string, password string) (*gocql.PasswordAuthenticator, error) { +func BuildAuthenticator(username, password string) (*gocql.PasswordAuthenticator, error) { if username == "" && password == "" { return nil, nil } diff --git a/auth/auth_test.go b/auth/auth_test.go index 2bc64a02..d674f802 100644 --- a/auth/auth_test.go +++ b/auth/auth_test.go @@ -4,14 +4,13 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - package auth import ( diff --git a/cmd/gemini/generators.go b/cmd/gemini/generators.go index b9ec4ce8..b1b78a54 100644 --- a/cmd/gemini/generators.go +++ b/cmd/gemini/generators.go @@ -4,24 +4,31 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - package main import ( "context" - "github.com/scylladb/gemini" "go.uber.org/zap" + + "github.com/scylladb/gemini" ) -func createGenerators(ctx context.Context, schema *gemini.Schema, schemaConfig gemini.SchemaConfig, distributionFunc gemini.DistributionFunc, actors, distributionSize uint64, logger *zap.Logger) []*gemini.Generator { +func createGenerators( + ctx context.Context, + schema *gemini.Schema, + schemaConfig gemini.SchemaConfig, + distributionFunc gemini.DistributionFunc, + _, distributionSize uint64, + logger *zap.Logger, +) []*gemini.Generator { partitionRangeConfig := gemini.PartitionRangeConfig{ MaxBlobLength: schemaConfig.MaxBlobLength, MinBlobLength: schemaConfig.MinBlobLength, diff --git a/cmd/gemini/jobs.go b/cmd/gemini/jobs.go index 6469c8bc..5081ef85 100644 --- a/cmd/gemini/jobs.go +++ b/cmd/gemini/jobs.go @@ -20,16 +20,31 @@ import ( "fmt" "time" - "github.com/scylladb/gemini" - "github.com/scylladb/gemini/store" "go.uber.org/zap" "golang.org/x/exp/rand" "golang.org/x/sync/errgroup" + + "github.com/scylladb/gemini" + "github.com/scylladb/gemini/store" ) // MutationJob continuously applies mutations against the database // for as long as the pump is active. -func MutationJob(ctx context.Context, pump <-chan heartBeat, schema *gemini.Schema, schemaCfg gemini.SchemaConfig, table *gemini.Table, s store.Store, r *rand.Rand, p gemini.PartitionRangeConfig, g *gemini.Generator, c chan Status, mode string, warmup time.Duration, logger *zap.Logger) error { +func MutationJob( + ctx context.Context, + pump <-chan heartBeat, + schema *gemini.Schema, + schemaCfg gemini.SchemaConfig, + table *gemini.Table, + s store.Store, + r *rand.Rand, + p gemini.PartitionRangeConfig, + g *gemini.Generator, + c chan Status, + _ string, + _ time.Duration, + logger *zap.Logger, +) error { schemaConfig := &schemaCfg logger = logger.Named("mutation_job") testStatus := Status{} @@ -74,7 +89,21 @@ func MutationJob(ctx context.Context, pump <-chan heartBeat, schema *gemini.Sche // ValidationJob continuously applies validations against the database // for as long as the pump is active. -func ValidationJob(ctx context.Context, pump <-chan heartBeat, schema *gemini.Schema, schemaCfg gemini.SchemaConfig, table *gemini.Table, s store.Store, r *rand.Rand, p gemini.PartitionRangeConfig, g *gemini.Generator, c chan Status, mode string, warmup time.Duration, logger *zap.Logger) error { +func ValidationJob( + ctx context.Context, + pump <-chan heartBeat, + schema *gemini.Schema, + schemaCfg gemini.SchemaConfig, + table *gemini.Table, + s store.Store, + r *rand.Rand, + p gemini.PartitionRangeConfig, + g *gemini.Generator, + c chan Status, + _ string, + _ time.Duration, + logger *zap.Logger, +) error { schemaConfig := &schemaCfg logger = logger.Named("validation_job") logger.Info("starting validation loop") @@ -121,7 +150,21 @@ func ValidationJob(ctx context.Context, pump <-chan heartBeat, schema *gemini.Sc // WarmupJob continuously applies mutations against the database // for as long as the pump is active or the supplied duration expires. -func WarmupJob(ctx context.Context, pump <-chan heartBeat, schema *gemini.Schema, schemaCfg gemini.SchemaConfig, table *gemini.Table, s store.Store, r *rand.Rand, p gemini.PartitionRangeConfig, g *gemini.Generator, c chan Status, mode string, warmup time.Duration, logger *zap.Logger) error { +func WarmupJob( + ctx context.Context, + _ <-chan heartBeat, + schema *gemini.Schema, + schemaCfg gemini.SchemaConfig, + table *gemini.Table, + s store.Store, + r *rand.Rand, + p gemini.PartitionRangeConfig, + g *gemini.Generator, + c chan Status, + _ string, + warmup time.Duration, + logger *zap.Logger, +) error { schemaConfig := &schemaCfg testStatus := Status{} var i int @@ -153,7 +196,18 @@ func WarmupJob(ctx context.Context, pump <-chan heartBeat, schema *gemini.Schema } } -func job(ctx context.Context, f testJob, actors uint64, schema *gemini.Schema, schemaConfig gemini.SchemaConfig, s store.Store, pump *Pump, generators []*gemini.Generator, result chan Status, logger *zap.Logger) error { +func job( + ctx context.Context, + f testJob, + actors uint64, + schema *gemini.Schema, + schemaConfig gemini.SchemaConfig, + s store.Store, + pump *Pump, + generators []*gemini.Generator, + result chan Status, + logger *zap.Logger, +) error { g, gCtx := errgroup.WithContext(ctx) partitionRangeConfig := gemini.PartitionRangeConfig{ MaxBlobLength: schemaConfig.MaxBlobLength, @@ -175,7 +229,17 @@ func job(ctx context.Context, f testJob, actors uint64, schema *gemini.Schema, s return g.Wait() } -func ddl(ctx context.Context, schema *gemini.Schema, sc *gemini.SchemaConfig, table *gemini.Table, s store.Store, r *rand.Rand, p gemini.PartitionRangeConfig, testStatus *Status, logger *zap.Logger) error { +func ddl( + ctx context.Context, + schema *gemini.Schema, + sc *gemini.SchemaConfig, + table *gemini.Table, + s store.Store, + r *rand.Rand, + p gemini.PartitionRangeConfig, + testStatus *Status, + logger *zap.Logger, +) error { if sc.CQLFeature != gemini.CQL_FEATURE_ALL { logger.Debug("ddl statements disabled") return nil @@ -210,7 +274,7 @@ func ddl(ctx context.Context, schema *gemini.Schema, sc *gemini.SchemaConfig, ta if w := logger.Check(zap.DebugLevel, "ddl statement"); w != nil { w.Write(zap.String("pretty_cql", ddlStmt.PrettyCQL())) } - if err := s.Mutate(ctx, ddlQuery); err != nil { + if err = s.Mutate(ctx, ddlQuery); err != nil { e := JobError{ Timestamp: time.Now(), Message: "DDL failed: " + err.Error(), @@ -227,7 +291,19 @@ func ddl(ctx context.Context, schema *gemini.Schema, sc *gemini.SchemaConfig, ta return nil } -func mutation(ctx context.Context, schema *gemini.Schema, _ *gemini.SchemaConfig, table *gemini.Table, s store.Store, r *rand.Rand, p gemini.PartitionRangeConfig, g *gemini.Generator, testStatus *Status, deletes bool, logger *zap.Logger) error { +func mutation( + ctx context.Context, + schema *gemini.Schema, + _ *gemini.SchemaConfig, + table *gemini.Table, + s store.Store, + r *rand.Rand, + p gemini.PartitionRangeConfig, + g *gemini.Generator, + testStatus *Status, + deletes bool, + logger *zap.Logger, +) error { mutateStmt, err := schema.GenMutateStmt(table, g, r, p, deletes) if err != nil { logger.Error("Failed! Mutation statement generation failed", zap.Error(err)) @@ -250,7 +326,7 @@ func mutation(ctx context.Context, schema *gemini.Schema, _ *gemini.SchemaConfig if w := logger.Check(zap.DebugLevel, "mutation statement"); w != nil { w.Write(zap.String("pretty_cql", mutateStmt.PrettyCQL())) } - if err := s.Mutate(ctx, mutateQuery, mutateValues...); err != nil { + if err = s.Mutate(ctx, mutateQuery, mutateValues...); err != nil { e := JobError{ Timestamp: time.Now(), Message: "Mutation failed: " + err.Error(), @@ -266,7 +342,18 @@ func mutation(ctx context.Context, schema *gemini.Schema, _ *gemini.SchemaConfig return nil } -func validation(ctx context.Context, schema *gemini.Schema, sc *gemini.SchemaConfig, table *gemini.Table, s store.Store, r *rand.Rand, p gemini.PartitionRangeConfig, g *gemini.Generator, testStatus *Status, logger *zap.Logger) (string, error) { +func validation( + ctx context.Context, + schema *gemini.Schema, + sc *gemini.SchemaConfig, + table *gemini.Table, + s store.Store, + r *rand.Rand, + p gemini.PartitionRangeConfig, + g *gemini.Generator, + _ *Status, + logger *zap.Logger, +) (string, error) { checkStmt := schema.GenCheckStmt(table, g, r, p) if checkStmt == nil { if w := logger.Check(zap.DebugLevel, "no statement generated"); w != nil { diff --git a/cmd/gemini/main.go b/cmd/gemini/main.go index bfb11900..b7e1ea1b 100644 --- a/cmd/gemini/main.go +++ b/cmd/gemini/main.go @@ -4,14 +4,13 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - package main import ( diff --git a/cmd/gemini/pump.go b/cmd/gemini/pump.go index 282da9c6..7b9b1712 100644 --- a/cmd/gemini/pump.go +++ b/cmd/gemini/pump.go @@ -4,14 +4,13 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - package main import ( @@ -22,7 +21,6 @@ import ( ) type Pump struct { - ctx context.Context ch chan heartBeat logger *zap.Logger } diff --git a/cmd/gemini/root.go b/cmd/gemini/root.go index 91a83bec..d6e810d5 100644 --- a/cmd/gemini/root.go +++ b/cmd/gemini/root.go @@ -32,11 +32,6 @@ import ( "github.com/hailocab/go-hostpool" "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus/promhttp" - "github.com/scylladb/gemini" - "github.com/scylladb/gemini/auth" - "github.com/scylladb/gemini/replication" - "github.com/scylladb/gemini/store" - "github.com/scylladb/gemini/tableopts" "github.com/spf13/cobra" "go.uber.org/zap" "go.uber.org/zap/zapcore" @@ -44,6 +39,12 @@ import ( "golang.org/x/net/context" "golang.org/x/sync/errgroup" "gonum.org/v1/gonum/stat/distuv" + + "github.com/scylladb/gemini" + "github.com/scylladb/gemini/auth" + "github.com/scylladb/gemini/replication" + "github.com/scylladb/gemini/store" + "github.com/scylladb/gemini/tableopts" ) var ( @@ -95,8 +96,8 @@ var ( testClusterHostSelectionPolicy string oracleClusterHostSelectionPolicy string useServerSideTimestamps bool - requestTimeout time.Duration - connectTimeout time.Duration + requestTimeout time.Duration + connectTimeout time.Duration ) const ( @@ -120,7 +121,21 @@ func interactive() bool { return !nonInteractive } -type testJob func(context.Context, <-chan heartBeat, *gemini.Schema, gemini.SchemaConfig, *gemini.Table, store.Store, *rand.Rand, gemini.PartitionRangeConfig, *gemini.Generator, chan Status, string, time.Duration, *zap.Logger) error +type testJob func( + context.Context, + <-chan heartBeat, + *gemini.Schema, + gemini.SchemaConfig, + *gemini.Table, + store.Store, + *rand.Rand, + gemini.PartitionRangeConfig, + *gemini.Generator, + chan Status, + string, + time.Duration, + *zap.Logger, +) error func readSchema(confFile string) (*gemini.Schema, error) { byteValue, err := ioutil.ReadFile(confFile) @@ -153,7 +168,7 @@ func (cb createBuilder) ToCql() (stmt string, names []string) { func run(cmd *cobra.Command, args []string) error { logger := createLogger(level) - defer logger.Sync() + defer gemini.IgnoreError(logger.Sync) cons, err := gocql.ParseConsistencyWrapper(consistency) if err != nil { @@ -175,7 +190,7 @@ func run(cmd *cobra.Command, args []string) error { _ = http.ListenAndServe(bind, nil) }() - if err := printSetup(); err != nil { + if err = printSetup(); err != nil { return errors.Wrapf(err, "unable to print setup") } distFunc, err := createDistributionFunc(partitionKeyDistribution, math.MaxUint64, seed, stdDistMean, oneStdDev) @@ -187,15 +202,14 @@ func run(cmd *cobra.Command, args []string) error { if err != nil { return err } - defer outFile.Sync() + defer gemini.IgnoreError(outFile.Sync) schemaConfig := createSchemaConfig(logger) - if err := schemaConfig.Valid(); err != nil { + if err = schemaConfig.Valid(); err != nil { return errors.Wrap(err, "invalid schema configuration") } var schema *gemini.Schema if len(schemaFile) > 0 { - var err error schema, err = readSchema(schemaFile) if err != nil { return errors.Wrap(err, "cannot create schema") @@ -221,34 +235,34 @@ func run(cmd *cobra.Command, args []string) error { case "stdout": tracingFile = os.Stdout default: - tf, err := createFile(tracingOutFile, os.Stdout) - if err != nil { - return err + tf, ioErr := createFile(tracingOutFile, os.Stdout) + if ioErr != nil { + return ioErr } tracingFile = tf - defer tracingFile.Sync() + defer gemini.IgnoreError(tracingFile.Sync) } } - store := store.New(schema, testCluster, oracleCluster, storeConfig, tracingFile, logger) - defer store.Close() + st := store.New(schema, testCluster, oracleCluster, storeConfig, tracingFile, logger) + defer gemini.IgnoreError(st.Close) if dropSchema && mode != readMode { for _, stmt := range schema.GetDropSchema() { logger.Debug(stmt) - if err := store.Mutate(context.Background(), createBuilder{stmt: stmt}); err != nil { + if err = st.Mutate(context.Background(), createBuilder{stmt: stmt}); err != nil { return errors.Wrap(err, "unable to drop schema") } } } testKeyspace, oracleKeyspace := schema.GetCreateKeyspaces() - if err := store.Create(context.Background(), createBuilder{stmt: testKeyspace}, createBuilder{stmt: oracleKeyspace}); err != nil { + if err = st.Create(context.Background(), createBuilder{stmt: testKeyspace}, createBuilder{stmt: oracleKeyspace}); err != nil { return errors.Wrap(err, "unable to create keyspace") } for _, stmt := range schema.GetCreateSchema() { logger.Debug(stmt) - if err := store.Mutate(context.Background(), createBuilder{stmt: stmt}); err != nil { + if err = st.Mutate(context.Background(), createBuilder{stmt: stmt}); err != nil { return errors.Wrap(err, "unable to create schema") } } @@ -256,7 +270,7 @@ func run(cmd *cobra.Command, args []string) error { result := make(chan Status, 10000) ctx, done := context.WithTimeout(context.Background(), duration+warmup) g, gCtx := errgroup.WithContext(ctx) - var graceful = make(chan os.Signal, 1) + graceful := make(chan os.Signal, 1) signal.Notify(graceful, syscall.SIGTERM, syscall.SIGINT) g.Go(func() error { select { @@ -281,10 +295,10 @@ func run(cmd *cobra.Command, args []string) error { resCh := make(chan *Status, 1) g.Go(func() error { defer done() - res, err := sampleStatus(gCtx, result, sp, logger) + res, sampleErr := sampleStatus(gCtx, result, sp, logger) sp.Stop() resCh <- res - return err + return sampleErr }) time.AfterFunc(duration+warmup, func() { defer done() @@ -292,7 +306,7 @@ func run(cmd *cobra.Command, args []string) error { }) if warmup > 0 { - if err := job(gCtx, WarmupJob, concurrency, schema, schemaConfig, store, pump, generators, result, logger); err != nil { + if err = job(gCtx, WarmupJob, concurrency, schema, schemaConfig, st, pump, generators, result, logger); err != nil { logger.Error("warmup encountered an error", zap.Error(err)) } } @@ -304,10 +318,10 @@ func run(cmd *cobra.Command, args []string) error { for id := range testJobs { tJob := testJobs[id] g.Go(func() error { - return job(gCtx, tJob, concurrency, schema, schemaConfig, store, pump, generators, result, logger) + return job(gCtx, tJob, concurrency, schema, schemaConfig, st, pump, generators, result, logger) }) } - if err := g.Wait(); err != nil { + if err = g.Wait(); err != nil { logger.Debug("error detected", zap.Error(err)) } } @@ -395,7 +409,11 @@ func createLogger(level string) *zap.Logger { return logger } -func createClusters(consistency gocql.Consistency, testHostSelectionPolicy gocql.HostSelectionPolicy, oracleHostSelectionPolicy gocql.HostSelectionPolicy, logger *zap.Logger) (*gocql.ClusterConfig, *gocql.ClusterConfig) { +func createClusters( + consistency gocql.Consistency, + testHostSelectionPolicy, oracleHostSelectionPolicy gocql.HostSelectionPolicy, + logger *zap.Logger, +) (*gocql.ClusterConfig, *gocql.ClusterConfig) { retryPolicy := &gocql.ExponentialBackoffRetryPolicy{ Min: time.Second, Max: 60 * time.Second, @@ -489,13 +507,14 @@ var rootCmd = &cobra.Command{ } func init() { - rootCmd.Version = version + ", commit " + commit + ", date " + date rootCmd.Flags().StringSliceVarP(&testClusterHost, "test-cluster", "t", []string{}, "Host names or IPs of the test cluster that is system under test") - rootCmd.MarkFlagRequired("test-cluster") + _ = rootCmd.MarkFlagRequired("test-cluster") rootCmd.Flags().StringVarP(&testClusterUsername, "test-username", "", "", "Username for the test cluster") rootCmd.Flags().StringVarP(&testClusterPassword, "test-password", "", "", "Password for the test cluster") - rootCmd.Flags().StringSliceVarP(&oracleClusterHost, "oracle-cluster", "o", []string{}, "Host names or IPs of the oracle cluster that provides correct answers. If omitted no oracle will be used") + rootCmd.Flags().StringSliceVarP( + &oracleClusterHost, "oracle-cluster", "o", []string{}, + "Host names or IPs of the oracle cluster that provides correct answers. If omitted no oracle will be used") rootCmd.Flags().StringVarP(&oracleClusterUsername, "oracle-username", "", "", "Username for the oracle cluster") rootCmd.Flags().StringVarP(&oracleClusterPassword, "oracle-password", "", "", "Password for the oracle cluster") rootCmd.Flags().StringVarP(&schemaFile, "schema", "", "", "Schema JSON config file") @@ -510,8 +529,14 @@ func init() { rootCmd.Flags().StringVarP(&outFileArg, "outfile", "", "", "Specify the name of the file where the results should go") rootCmd.Flags().StringVarP(&bind, "bind", "b", ":2112", "Specify the interface and port which to bind prometheus metrics on. Default is ':2112'") rootCmd.Flags().DurationVarP(&warmup, "warmup", "", 30*time.Second, "Specify the warmup perid as a duration for example 30s or 10h") - rootCmd.Flags().StringVarP(&replicationStrategy, "replication-strategy", "", "simple", "Specify the desired replication strategy as either the coded short hand simple|network to get the default for each type or provide the entire specification in the form {'class':'....'}") - rootCmd.Flags().StringVarP(&oracleReplicationStrategy, "oracle-replication-strategy", "", "simple", "Specify the desired replication strategy of the oracle cluster as either the coded short hand simple|network to get the default for each type or provide the entire specification in the form {'class':'....'}") + rootCmd.Flags().StringVarP( + &replicationStrategy, "replication-strategy", "", "simple", + "Specify the desired replication strategy as either the coded short hand simple|network to get the default for each type or provide "+ + "the entire specification in the form {'class':'....'}") + rootCmd.Flags().StringVarP( + &oracleReplicationStrategy, "oracle-replication-strategy", "", "simple", + "Specify the desired replication strategy of the oracle cluster as either the coded short hand simple|network to get the default for each "+ + "type or provide the entire specification in the form {'class':'....'}") rootCmd.Flags().StringArrayVarP(&tableOptions, "table-options", "", []string{}, "Repeatable argument to set table options to be added to the created tables") rootCmd.Flags().StringVarP(&consistency, "consistency", "", "QUORUM", "Specify the desired consistency as ANY|ONE|TWO|THREE|QUORUM|LOCAL_QUORUM|EACH_QUORUM|LOCAL_ONE") rootCmd.Flags().IntVarP(&maxTables, "max-tables", "", 1, "Maximum number of generated tables") @@ -525,19 +550,33 @@ func init() { rootCmd.Flags().StringVarP(&cqlFeatures, "cql-features", "", "basic", "Specify the type of cql features to use, basic|normal|all") rootCmd.Flags().StringVarP(&level, "level", "", "info", "Specify the logging level, debug|info|warn|error|dpanic|panic|fatal") rootCmd.Flags().IntVarP(&maxRetriesMutate, "max-mutation-retries", "", 2, "Maximum number of attempts to apply a mutation") - rootCmd.Flags().DurationVarP(&maxRetriesMutateSleep, "max-mutation-retries-backoff", "", 10*time.Millisecond, "Duration between attempts to apply a mutation for example 10ms or 1s") + rootCmd.Flags().DurationVarP( + &maxRetriesMutateSleep, "max-mutation-retries-backoff", "", 10*time.Millisecond, + "Duration between attempts to apply a mutation for example 10ms or 1s") rootCmd.Flags().Uint64VarP(&pkBufferReuseSize, "partition-key-buffer-reuse-size", "", 100, "Number of reused buffered partition keys") rootCmd.Flags().Uint64VarP(&partitionCount, "token-range-slices", "", 10000, "Number of slices to divide the token space into") - rootCmd.Flags().StringVarP(&partitionKeyDistribution, "partition-key-distribution", "", "uniform", "Specify the distribution from which to draw partition keys, supported values are currently uniform|normal|zipf") + rootCmd.Flags().StringVarP( + &partitionKeyDistribution, "partition-key-distribution", "", "uniform", + "Specify the distribution from which to draw partition keys, supported values are currently uniform|normal|zipf") rootCmd.Flags().Float64VarP(&normalDistMean, "normal-dist-mean", "", stdDistMean, "Mean of the normal distribution") rootCmd.Flags().Float64VarP(&normalDistSigma, "normal-dist-sigma", "", oneStdDev, "Sigma of the normal distribution, defaults to one standard deviation ~0.341") - rootCmd.Flags().StringVarP(&tracingOutFile, "tracing-outfile", "", "", "Specify the file to which tracing information gets written. Two magic names are available, 'stdout' and 'stderr'. By default tracing is disabled.") + rootCmd.Flags().StringVarP( + &tracingOutFile, "tracing-outfile", "", "", + "Specify the file to which tracing information gets written. Two magic names are available, 'stdout' and 'stderr'. By default tracing is disabled.") rootCmd.Flags().BoolVarP(&useCounters, "use-counters", "", false, "Ensure that at least one table is a counter table") - rootCmd.Flags().IntVarP(&asyncObjectStabilizationAttempts, "async-objects-stabilization-attempts", "", 10, "Maximum number of attempts to validate result sets from MV and SI") - rootCmd.Flags().DurationVarP(&asyncObjectStabilizationDelay, "async-objects-stabilization-backoff", "", 10*time.Millisecond, "Duration between attempts to validate result sets from MV and SI for example 10ms or 1s") + rootCmd.Flags().IntVarP( + &asyncObjectStabilizationAttempts, "async-objects-stabilization-attempts", "", 10, + "Maximum number of attempts to validate result sets from MV and SI") + rootCmd.Flags().DurationVarP( + &asyncObjectStabilizationDelay, "async-objects-stabilization-backoff", "", 10*time.Millisecond, + "Duration between attempts to validate result sets from MV and SI for example 10ms or 1s") rootCmd.Flags().BoolVarP(&useLWT, "use-lwt", "", false, "Emit LWT based updates") - rootCmd.Flags().StringVarP(&oracleClusterHostSelectionPolicy, "oracle-host-selection-policy", "", "round-robin", "Host selection policy used by the driver for the oracle cluster: round-robin|host-pool|token-aware") - rootCmd.Flags().StringVarP(&testClusterHostSelectionPolicy, "test-host-selection-policy", "", "round-robin", "Host selection policy used by the driver for the test cluster: round-robin|host-pool|token-aware") + rootCmd.Flags().StringVarP( + &oracleClusterHostSelectionPolicy, "oracle-host-selection-policy", "", "round-robin", + "Host selection policy used by the driver for the oracle cluster: round-robin|host-pool|token-aware") + rootCmd.Flags().StringVarP( + &testClusterHostSelectionPolicy, "test-host-selection-policy", "", "round-robin", + "Host selection policy used by the driver for the test cluster: round-robin|host-pool|token-aware") rootCmd.Flags().BoolVarP(&useServerSideTimestamps, "use-server-timestamps", "", false, "Use server-side generated timestamps for writes") rootCmd.Flags().DurationVarP(&requestTimeout, "request-timeout", "", 30*time.Second, "Duration of waiting request execution") rootCmd.Flags().DurationVarP(&connectTimeout, "connect-timeout", "", 30*time.Second, "Duration of waiting connection established") diff --git a/cmd/gemini/schema.go b/cmd/gemini/schema.go index 0ea18900..3b4094d7 100644 --- a/cmd/gemini/schema.go +++ b/cmd/gemini/schema.go @@ -17,9 +17,10 @@ package main import ( "strings" + "go.uber.org/zap" + "github.com/scylladb/gemini" "github.com/scylladb/gemini/replication" - "go.uber.org/zap" ) func createSchemaConfig(logger *zap.Logger) gemini.SchemaConfig { diff --git a/cmd/gemini/status.go b/cmd/gemini/status.go index b129f145..1635cc04 100644 --- a/cmd/gemini/status.go +++ b/cmd/gemini/status.go @@ -4,14 +4,13 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - package main import ( @@ -22,16 +21,17 @@ import ( "sync" "github.com/pkg/errors" - "github.com/scylladb/gemini" "go.uber.org/zap" + + "github.com/scylladb/gemini" ) type Status struct { + Errors []JobError `json:"errors,omitempty"` WriteOps int `json:"write_ops"` WriteErrors int `json:"write_errors"` ReadOps int `json:"read_ops"` ReadErrors int `json:"read_errors"` - Errors []JobError `json:"errors,omitempty"` } func (r *Status) Merge(s Status) { diff --git a/cmd/gemini/strategies_test.go b/cmd/gemini/strategies_test.go index 76755952..cba4dcea 100644 --- a/cmd/gemini/strategies_test.go +++ b/cmd/gemini/strategies_test.go @@ -1,3 +1,17 @@ +// Copyright 2019 ScyllaDB +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package main import ( @@ -11,7 +25,7 @@ import ( ) func TestGetReplicationStrategy(t *testing.T) { - var tests = map[string]struct { + tests := map[string]struct { strategy string expected string }{ diff --git a/datautils.go b/datautils.go index ecb1893f..a0ca497a 100644 --- a/datautils.go +++ b/datautils.go @@ -4,14 +4,13 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - package gemini import ( diff --git a/datautils_test.go b/datautils_test.go index bb7ba495..1d3b0238 100644 --- a/datautils_test.go +++ b/datautils_test.go @@ -20,6 +20,5 @@ import ( "golang.org/x/exp/rand" ) +//nolint:unused var rnd = rand.New(rand.NewSource(uint64(time.Now().UnixNano()))) - -var bench_r string diff --git a/generator.go b/generator.go index cc99d026..c5fdf070 100644 --- a/generator.go +++ b/generator.go @@ -4,24 +4,24 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - package gemini import ( "context" - "github.com/scylladb/gemini/inflight" - "github.com/scylladb/gemini/murmur" "go.uber.org/zap" "golang.org/x/exp/rand" "golang.org/x/sync/errgroup" + + "github.com/scylladb/gemini/inflight" + "github.com/scylladb/gemini/murmur" ) // TokenIndex represents the position of a token in the token ring. @@ -39,19 +39,19 @@ type DistributionFunc func() TokenIndex type Generator struct { ctx context.Context - partitions []*Partition - partitionCount uint64 + logger *zap.Logger table *Table + idxFunc DistributionFunc + partitions []*Partition partitionsConfig PartitionRangeConfig + partitionCount uint64 seed uint64 - idxFunc DistributionFunc - logger *zap.Logger } type GeneratorConfig struct { + PartitionsDistributionFunc DistributionFunc PartitionsRangeConfig PartitionRangeConfig PartitionsCount uint64 - PartitionsDistributionFunc DistributionFunc Seed uint64 PkUsedBufferSize uint64 } @@ -102,8 +102,8 @@ func (g Generator) GetOld() (ValueWithToken, bool) { } type ValueWithToken struct { - Token uint64 Value Value + Token uint64 } // GiveOld returns the supplied value for later reuse unless the value diff --git a/generator_test.go b/generator_test.go index bb227a4e..879c9a62 100644 --- a/generator_test.go +++ b/generator_test.go @@ -4,14 +4,13 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - package gemini import ( diff --git a/inflight/inflight.go b/inflight/inflight.go index 7f8b74e1..fb51d167 100644 --- a/inflight/inflight.go +++ b/inflight/inflight.go @@ -135,7 +135,7 @@ func (s *syncU64set) shrink() { } for key, val := range s.values { - if val == true { + if val { newValues[key] = val } } diff --git a/murmur/murmur.go b/murmur/murmur.go index 33ba69df..59b57889 100644 --- a/murmur/murmur.go +++ b/murmur/murmur.go @@ -4,14 +4,13 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - package murmur const ( diff --git a/murmur/murmur_test.go b/murmur/murmur_test.go index 0af60522..560698a7 100644 --- a/murmur/murmur_test.go +++ b/murmur/murmur_test.go @@ -4,14 +4,13 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - package murmur import ( @@ -64,7 +63,6 @@ func TestFmix(t *testing.T) { } }) } - } func TestMurmur3H1_CassandraSign(t *testing.T) { diff --git a/murmur/murmur_unsafe.go b/murmur/murmur_unsafe.go index e2567f0d..66d0ca0f 100644 --- a/murmur/murmur_unsafe.go +++ b/murmur/murmur_unsafe.go @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +//go:build !appengine // +build !appengine package murmur diff --git a/partition.go b/partition.go index b6f6ac98..c8bb294e 100644 --- a/partition.go +++ b/partition.go @@ -4,14 +4,13 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - package gemini import ( diff --git a/replication/replication_test.go b/replication/replication_test.go index 9ce7a4dc..b065c174 100644 --- a/replication/replication_test.go +++ b/replication/replication_test.go @@ -4,14 +4,13 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - package replication import ( diff --git a/routing_key_test.go b/routing_key_test.go index d7ef78e4..f29ce44e 100644 --- a/routing_key_test.go +++ b/routing_key_test.go @@ -4,14 +4,13 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - package gemini import ( diff --git a/schema.go b/schema.go index 89fa3789..416655fd 100644 --- a/schema.go +++ b/schema.go @@ -4,14 +4,13 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - package gemini import ( @@ -24,10 +23,11 @@ import ( "github.com/gocql/gocql" "github.com/pkg/errors" - "github.com/scylladb/gemini/replication" - "github.com/scylladb/gemini/tableopts" "github.com/scylladb/gocqlx/v2/qb" "golang.org/x/exp/rand" + + "github.com/scylladb/gemini/replication" + "github.com/scylladb/gemini/tableopts" ) type CQLFeature int @@ -37,7 +37,7 @@ const ( CQL_FEATURE_NORMAL CQL_FEATURE_ALL - KnownIssuesJsonWithTuples = "https://github.com/scylladb/scylla/issues/3708" + KnownIssuesJSONWithTuples = "https://github.com/scylladb/scylla/issues/3708" ) type Value []interface{} @@ -67,20 +67,20 @@ type SchemaConfig struct { } var ( - SchemaConfigInvalidPK = errors.New("max number of partition keys must be bigger than min number of partition keys") - SchemaConfigInvalidCK = errors.New("max number of clustering keys must be bigger than min number of clustering keys") - SchemaConfigInvalidCols = errors.New("max number of columns must be bigger than min number of columns") + ErrSchemaConfigInvalidPK = errors.New("max number of partition keys must be bigger than min number of partition keys") + ErrSchemaConfigInvalidCK = errors.New("max number of clustering keys must be bigger than min number of clustering keys") + ErrSchemaConfigInvalidCols = errors.New("max number of columns must be bigger than min number of columns") ) func (sc *SchemaConfig) Valid() error { if sc.MaxPartitionKeys <= sc.MinPartitionKeys { - return SchemaConfigInvalidPK + return ErrSchemaConfigInvalidPK } if sc.MaxClusteringKeys <= sc.MinClusteringKeys { - return SchemaConfigInvalidCK + return ErrSchemaConfigInvalidCK } if sc.MaxColumns <= sc.MinClusteringKeys { - return SchemaConfigInvalidCols + return ErrSchemaConfigInvalidCols } return nil } @@ -114,14 +114,14 @@ func (sc *SchemaConfig) GetMinColumns() int { } type Keyspace struct { - Name string `json:"name"` Replication *replication.Replication `json:"replication"` OracleReplication *replication.Replication `json:"oracle_replication"` + Name string `json:"name"` } type ColumnDef struct { - Name string `json:"name"` Type Type `json:"type"` + Name string `json:"name"` } type Type interface { @@ -150,8 +150,8 @@ func (c Columns) Names() []string { return names } -func (cs Columns) ToJSONMap(values map[string]interface{}, r *rand.Rand, p PartitionRangeConfig) map[string]interface{} { - for _, k := range cs { +func (c Columns) ToJSONMap(values map[string]interface{}, r *rand.Rand, p PartitionRangeConfig) map[string]interface{} { + for _, k := range c { switch t := k.Type.(type) { case SimpleType: if t != TYPE_BLOB { @@ -162,7 +162,7 @@ func (cs Columns) ToJSONMap(values map[string]interface{}, r *rand.Rand, p Parti if ok { values[k.Name] = "0x" + v } - case TupleType: + case *TupleType: vv := t.GenValue(r, p) for i, val := range vv { if t.Types[i] == TYPE_BLOB { @@ -248,7 +248,7 @@ func (t *Table) GetCreateTypes(keyspace Keyspace) []string { var stmts []string for _, column := range t.Columns { switch c := column.Type.(type) { - case UDTType: + case *UDTType: createType := "CREATE TYPE IF NOT EXISTS %s.%s (%s)" var typs []string for name, typ := range c.Types { @@ -271,7 +271,7 @@ func (atb *AlterTableBuilder) ToCql() (string, []string) { func (t *Table) addColumn(keyspace string, sc *SchemaConfig) ([]*Stmt, func(), error) { var stmts []*Stmt column := ColumnDef{Name: genColumnName("col", len(t.Columns)+1), Type: genColumnType(len(t.Columns)+1, sc)} - if c, ok := column.Type.(UDTType); ok { + if c, ok := column.Type.(*UDTType); ok { createType := "CREATE TYPE IF NOT EXISTS %s.%s (%s);" var typs []string for name, typ := range c.Types { @@ -301,6 +301,7 @@ func (t *Table) addColumn(keyspace string, sc *SchemaConfig) ([]*Stmt, func(), e }, nil } +//nolint:unused func (t *Table) alterColumn(keyspace string) ([]*Stmt, func(), error) { var stmts []*Stmt idx := rand.Intn(len(t.Columns)) @@ -349,10 +350,10 @@ func (t *Table) dropColumn(keyspace string) ([]*Stmt, func(), error) { } type MaterializedView struct { + NonPrimaryKey ColumnDef Name string `json:"name"` PartitionKeys Columns `json:"partition_keys"` ClusteringKeys Columns `json:"clustering_keys"` - NonPrimaryKey ColumnDef } type Stmt struct { @@ -427,12 +428,12 @@ func GenSchema(sc SchemaConfig) *Schema { numTables := 1 + rand.Intn(sc.GetMaxTables()) for i := 0; i < numTables; i++ { table := createTable(sc, fmt.Sprintf("table%d", i+1)) - builder.Table(&table) + builder.Table(table) } return builder.Build() } -func createTable(sc SchemaConfig, tableName string) Table { +func createTable(sc SchemaConfig, tableName string) *Table { var partitionKeys []ColumnDef numPartitionKeys := rand.Intn(sc.GetMaxPartitionKeys()-sc.GetMinPartitionKeys()) + sc.GetMinPartitionKeys() for i := 0; i < numPartitionKeys; i++ { @@ -448,7 +449,7 @@ func createTable(sc SchemaConfig, tableName string) Table { PartitionKeys: partitionKeys, ClusteringKeys: clusteringKeys, KnownIssues: map[string]bool{ - KnownIssuesJsonWithTuples: true, + KnownIssuesJSONWithTuples: true, }, } for _, option := range sc.TableOptions { @@ -458,7 +459,7 @@ func createTable(sc SchemaConfig, tableName string) Table { columns := []ColumnDef{ { Name: genColumnName("col", 0), - Type: CounterType{ + Type: &CounterType{ Value: 0, }, }, @@ -477,14 +478,14 @@ func createTable(sc SchemaConfig, tableName string) Table { var mvs []MaterializedView if sc.CQLFeature > CQL_FEATURE_BASIC && numClusteringKeys > 0 { - mvs = createMaterializedViews(table, partitionKeys, clusteringKeys, columns) + mvs = createMaterializedViews(table.Name, partitionKeys, clusteringKeys, columns) } table.Columns = columns table.MaterializedViews = mvs table.Indexes = indexes } - return table + return &table } func createIndexes(tableName string, numColumns int, columns []ColumnDef) []IndexDef { @@ -510,7 +511,7 @@ func createIndexes(tableName string, numColumns int, columns []ColumnDef) []Inde return indexes } -func createMaterializedViews(table Table, partitionKeys []ColumnDef, clusteringKeys []ColumnDef, columns []ColumnDef) []MaterializedView { +func createMaterializedViews(tableName string, partitionKeys, clusteringKeys, columns []ColumnDef) []MaterializedView { validMVColumn := func() (ColumnDef, error) { validCols := make([]ColumnDef, 0, len(columns)) for _, col := range columns { @@ -543,7 +544,7 @@ func createMaterializedViews(table Table, partitionKeys []ColumnDef, clusteringK col, } mv := MaterializedView{ - Name: fmt.Sprintf("%s_mv_%d", table.Name, i), + Name: fmt.Sprintf("%s_mv_%d", tableName, i), PartitionKeys: append(cols, partitionKeys...), ClusteringKeys: clusteringKeys, NonPrimaryKey: col, @@ -573,14 +574,12 @@ func (s *Schema) GetCreateSchema() []string { var ( mvPartitionKeys []string mvPrimaryKeysNotNull []string - mvClusteringKeys []string ) for _, pk := range mv.PartitionKeys { mvPartitionKeys = append(mvPartitionKeys, pk.Name) mvPrimaryKeysNotNull = append(mvPrimaryKeysNotNull, fmt.Sprintf("%s IS NOT NULL", pk.Name)) } for _, ck := range mv.ClusteringKeys { - mvClusteringKeys = append(mvClusteringKeys, ck.Name) mvPrimaryKeysNotNull = append(mvPrimaryKeysNotNull, fmt.Sprintf("%s IS NOT NULL", ck.Name)) } var createMaterializedView string @@ -607,9 +606,7 @@ func (s *Schema) genInsertStmt(t *Table, g *Generator, r *rand.Rand, p Partition } func (s *Schema) updateStmt(t *Table, g *Generator, r *rand.Rand, p PartitionRangeConfig) (*Stmt, error) { - var ( - typs []Type - ) + var typs []Type builder := qb.Update(s.Keyspace.Name + "." + t.Name) for _, pk := range t.PartitionKeys { builder = builder.Where(qb.Eq(pk.Name)) @@ -631,9 +628,9 @@ func (s *Schema) updateStmt(t *Table, g *Generator, r *rand.Rand, p PartitionRan ) for _, cdef := range t.Columns { switch t := cdef.Type.(type) { - case TupleType: + case *TupleType: builder = builder.SetTuple(cdef.Name, len(t.Types)) - case CounterType: + case *CounterType: builder = builder.SetLit(cdef.Name, cdef.Name+"+1") continue default: @@ -652,9 +649,7 @@ func (s *Schema) updateStmt(t *Table, g *Generator, r *rand.Rand, p PartitionRan } func (s *Schema) insertStmt(t *Table, g *Generator, r *rand.Rand, p PartitionRangeConfig) (*Stmt, error) { - var ( - typs []Type - ) + var typs []Type builder := qb.Insert(s.Keyspace.Name + "." + t.Name) for _, pk := range t.PartitionKeys { builder = builder.Columns(pk.Name) @@ -672,7 +667,7 @@ func (s *Schema) insertStmt(t *Table, g *Generator, r *rand.Rand, p PartitionRan } for _, cdef := range t.Columns { switch t := cdef.Type.(type) { - case TupleType: + case *TupleType: builder = builder.TupleColumn(cdef.Name, len(t.Types)) default: builder = builder.Columns(cdef.Name) @@ -693,8 +688,9 @@ func (s *Schema) insertStmt(t *Table, g *Generator, r *rand.Rand, p PartitionRan }, nil } -func (s *Schema) genInsertJsonStmt(t *Table, g *Generator, r *rand.Rand, p PartitionRangeConfig) (*Stmt, error) { - if isCounterTable(t) { +func (s *Schema) genInsertJSONStmt(table *Table, g *Generator, r *rand.Rand, p PartitionRangeConfig) (*Stmt, error) { + var v string + if isCounterTable(table) { return nil, nil } vals, ok := g.Get() @@ -704,22 +700,22 @@ func (s *Schema) genInsertJsonStmt(t *Table, g *Generator, r *rand.Rand, p Parti vs := make([]interface{}, len(vals.Value)) copy(vs, vals.Value) values := make(map[string]interface{}) - for i, pk := range t.PartitionKeys { + for i, pk := range table.PartitionKeys { switch t := pk.Type.(type) { case SimpleType: if t != TYPE_BLOB { values[pk.Name] = vs[i] continue } - v, ok := vs[i].(string) + v, ok = vs[i].(string) if ok { values[pk.Name] = "0x" + v } - case TupleType: + case *TupleType: tupVals := make([]interface{}, len(t.Types)) for j := 0; j < len(t.Types); j++ { if t.Types[j] == TYPE_BLOB { - v, ok := vs[i+j].(string) + v, ok = vs[i+j].(string) if ok { v = "0x" + v } @@ -733,15 +729,15 @@ func (s *Schema) genInsertJsonStmt(t *Table, g *Generator, r *rand.Rand, p Parti panic(fmt.Sprintf("unknown type: %s", t.Name())) } } - values = t.ClusteringKeys.ToJSONMap(values, r, p) - values = t.Columns.ToJSONMap(values, r, p) + values = table.ClusteringKeys.ToJSONMap(values, r, p) + values = table.Columns.ToJSONMap(values, r, p) jsonString, err := json.Marshal(values) if err != nil { return nil, err } - builder := qb.Insert(s.Keyspace.Name + "." + t.Name).Json() + builder := qb.Insert(s.Keyspace.Name + "." + table.Name).Json() return &Stmt{ Query: builder, Values: func() (uint64, []interface{}) { @@ -753,9 +749,7 @@ func (s *Schema) genInsertJsonStmt(t *Table, g *Generator, r *rand.Rand, p Parti } func (s *Schema) genDeleteRows(t *Table, g *Generator, r *rand.Rand, p PartitionRangeConfig) (*Stmt, error) { - var ( - typs []Type - ) + var typs []Type builder := qb.Delete(s.Keyspace.Name + "." + t.Name) for _, pk := range t.PartitionKeys { builder = builder.Where(qb.Eq(pk.Name)) @@ -786,7 +780,7 @@ func (s *Schema) genDeleteRows(t *Table, g *Generator, r *rand.Rand, p Partition func (s *Schema) GenDDLStmt(t *Table, r *rand.Rand, p PartitionRangeConfig, sc *SchemaConfig) ([]*Stmt, func(), error) { switch n := r.Intn(3); n { - //case 0: // Alter column not supported in Cassandra from 3.0.11 + // case 0: // Alter column not supported in Cassandra from 3.0.11 // return t.alterColumn(s.Keyspace.Name) case 1: // Delete column return t.dropColumn(s.Keyspace.Name) @@ -806,12 +800,12 @@ func (s *Schema) GenMutateStmt(t *Table, g *Generator, r *rand.Rand, p Partition case 10, 100: return s.genDeleteRows(t, g, r, p) default: - switch n := rand.Intn(2); n { + switch rand.Intn(2) { case 0: - if t.KnownIssues[KnownIssuesJsonWithTuples] { + if t.KnownIssues[KnownIssuesJSONWithTuples] { return s.genInsertStmt(t, g, r, p) } - return s.genInsertJsonStmt(t, g, r, p) + return s.genInsertJSONStmt(t, g, r, p) default: return s.genInsertStmt(t, g, r, p) } @@ -836,8 +830,7 @@ func (s *Schema) GenCheckStmt(t *Table, g *Generator, r *rand.Rand, p PartitionR return s.genMultiplePartitionClusteringRangeQuery(t, g, r, p) case 4: // Reducing the probability to hit these since they often take a long time to run - n := r.Intn(5) - switch n { + switch r.Intn(5) { case 0: return s.genSingleIndexQuery(t, g, r, p) default: @@ -850,9 +843,9 @@ func (s *Schema) GenCheckStmt(t *Table, g *Generator, r *rand.Rand, p PartitionR func (s *Schema) genSinglePartitionQuery(t *Table, g *Generator, r *rand.Rand, p PartitionRangeConfig) *Stmt { t.mu.RLock() defer t.mu.RUnlock() - var ( - mv_col ColumnDef - mv_values []interface{} + var ( + mvCol ColumnDef + mvValues []interface{} ) tableName := t.Name @@ -861,7 +854,7 @@ func (s *Schema) genSinglePartitionQuery(t *Table, g *Generator, r *rand.Rand, p view := r.Intn(len(t.MaterializedViews)) tableName = t.MaterializedViews[view].Name partitionKeys = t.MaterializedViews[view].PartitionKeys - mv_col = t.MaterializedViews[view].NonPrimaryKey + mvCol = t.MaterializedViews[view].NonPrimaryKey } builder := qb.Select(s.Keyspace.Name + "." + tableName) typs := make([]Type, 0, 10) @@ -873,9 +866,9 @@ func (s *Schema) genSinglePartitionQuery(t *Table, g *Generator, r *rand.Rand, p if !ok { return nil } - if (ColumnDef{}) != mv_col { - mv_values = appendValue(mv_col.Type, r, p, mv_values) - values.Value = append(mv_values, values.Value...) + if (ColumnDef{}) != mvCol { + mvValues = appendValue(mvCol.Type, r, p, mvValues) + values.Value = append(mvValues, values.Value...) } return &Stmt{ @@ -945,9 +938,9 @@ func (s *Schema) genClusteringRangeQuery(t *Table, g *Generator, r *rand.Rand, p defer t.mu.RUnlock() var ( - typs []Type - mv_col ColumnDef - mv_values []interface{} + allTypes []Type + mvCol ColumnDef + mvValues []interface{} ) tableName := t.Name partitionKeys := t.PartitionKeys @@ -957,7 +950,7 @@ func (s *Schema) genClusteringRangeQuery(t *Table, g *Generator, r *rand.Rand, p tableName = t.MaterializedViews[view].Name partitionKeys = t.MaterializedViews[view].PartitionKeys clusteringKeys = t.MaterializedViews[view].ClusteringKeys - mv_col = t.MaterializedViews[view].NonPrimaryKey + mvCol = t.MaterializedViews[view].NonPrimaryKey } builder := qb.Select(s.Keyspace.Name + "." + tableName) vs, ok := g.GetOld() @@ -968,30 +961,30 @@ func (s *Schema) genClusteringRangeQuery(t *Table, g *Generator, r *rand.Rand, p values := vs.Value for _, pk := range partitionKeys { builder = builder.Where(qb.Eq(pk.Name)) - typs = append(typs, pk.Type) + allTypes = append(allTypes, pk.Type) } - if (ColumnDef{}) != mv_col { - mv_values = appendValue(mv_col.Type, r, p, mv_values) - values = append(mv_values, values...) + if (ColumnDef{}) != mvCol { + mvValues = appendValue(mvCol.Type, r, p, mvValues) + values = append(mvValues, values...) } if len(clusteringKeys) > 0 { maxClusteringRels := r.Intn(len(clusteringKeys)) for i := 0; i < maxClusteringRels; i++ { builder = builder.Where(qb.Eq(clusteringKeys[i].Name)) values = appendValue(clusteringKeys[i].Type, r, p, values) - typs = append(typs, clusteringKeys[i].Type) + allTypes = append(allTypes, clusteringKeys[i].Type) } builder = builder.Where(qb.Gt(clusteringKeys[maxClusteringRels].Name)).Where(qb.Lt(clusteringKeys[maxClusteringRels].Name)) values = appendValue(t.ClusteringKeys[maxClusteringRels].Type, r, p, values) values = appendValue(t.ClusteringKeys[maxClusteringRels].Type, r, p, values) - typs = append(typs, clusteringKeys[maxClusteringRels].Type, clusteringKeys[maxClusteringRels].Type) + allTypes = append(allTypes, clusteringKeys[maxClusteringRels].Type, clusteringKeys[maxClusteringRels].Type) } return &Stmt{ Query: builder, Values: func() (uint64, []interface{}) { return 0, values }, - Types: typs, + Types: allTypes, QueryType: SelectRangeStatementType, } } @@ -1129,7 +1122,7 @@ func NewSchemaBuilder() SchemaBuilder { func isCounterTable(t *Table) bool { if len(t.Columns) == 1 { switch t.Columns[0].Type.(type) { - case CounterType: + case *CounterType: return true } } diff --git a/schema_test.go b/schema_test.go index af9d49a9..5fa0d30b 100644 --- a/schema_test.go +++ b/schema_test.go @@ -12,24 +12,25 @@ // See the License for the specific language governing permissions and // limitations under the License. +//nolint:lll package gemini import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/scylladb/gemini/tableopts" ) func TestSchemaConfigValidate(t *testing.T) { - tests := map[string]struct { config *SchemaConfig want error }{ "empty": { config: &SchemaConfig{}, - want: SchemaConfigInvalidPK, + want: ErrSchemaConfigInvalidPK, }, "valid": { config: &SchemaConfig{ @@ -47,14 +48,14 @@ func TestSchemaConfigValidate(t *testing.T) { MaxPartitionKeys: 2, MinPartitionKeys: 3, }, - want: SchemaConfigInvalidPK, + want: ErrSchemaConfigInvalidPK, }, "ck_missing": { config: &SchemaConfig{ MaxPartitionKeys: 3, MinPartitionKeys: 2, }, - want: SchemaConfigInvalidCK, + want: ErrSchemaConfigInvalidCK, }, "min_ck_gt_than_max_ck": { config: &SchemaConfig{ @@ -63,7 +64,7 @@ func TestSchemaConfigValidate(t *testing.T) { MaxClusteringKeys: 2, MinClusteringKeys: 3, }, - want: SchemaConfigInvalidCK, + want: ErrSchemaConfigInvalidCK, }, "columns_missing": { config: &SchemaConfig{ @@ -72,7 +73,7 @@ func TestSchemaConfigValidate(t *testing.T) { MaxClusteringKeys: 3, MinClusteringKeys: 2, }, - want: SchemaConfigInvalidCols, + want: ErrSchemaConfigInvalidCols, }, "min_cols_gt_than_max_cols": { config: &SchemaConfig{ @@ -83,13 +84,14 @@ func TestSchemaConfigValidate(t *testing.T) { MaxColumns: 2, MinColumns: 3, }, - want: SchemaConfigInvalidCols, + want: ErrSchemaConfigInvalidCols, }, } cmp.AllowUnexported() for name, test := range tests { t.Run(name, func(t *testing.T) { got := test.config.Valid() + //nolint:errorlint if got != test.want { t.Fatalf("expected '%s', got '%s'", test.want, got) } diff --git a/store/cqlstore.go b/store/cqlstore.go index 2cf63051..3d941f11 100644 --- a/store/cqlstore.go +++ b/store/cqlstore.go @@ -4,14 +4,13 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - package store import ( @@ -19,25 +18,27 @@ import ( "os" "time" + errs "errors" + "github.com/gocql/gocql" "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" - "github.com/scylladb/gemini" "github.com/scylladb/gocqlx/v2/qb" "go.uber.org/multierr" "go.uber.org/zap" + + "github.com/scylladb/gemini" ) type cqlStore struct { session *gocql.Session schema *gemini.Schema + ops *prometheus.CounterVec + logger *zap.Logger system string maxRetriesMutate int maxRetriesMutateSleep time.Duration useServerSideTimestamps bool - - ops *prometheus.CounterVec - logger *zap.Logger } func (cs *cqlStore) name() string { @@ -89,7 +90,7 @@ func (cs *cqlStore) doMutate(ctx context.Context, builder qb.Builder, ts time.Ti if err := query.Exec(); err != nil { */ if err := query.Exec(); err != nil { - if err == context.DeadlineExceeded { + if errs.Is(err, context.DeadlineExceeded) { if w := cs.logger.Check(zap.DebugLevel, "deadline exceeded for mutation query"); w != nil { w.Write(zap.String("system", cs.system), zap.String("query", queryBody), zap.Error(err)) } @@ -107,13 +108,13 @@ func (cs *cqlStore) load(ctx context.Context, builder qb.Builder, values []inter cs.ops.WithLabelValues(cs.system, opType(builder)).Inc() defer func() { if e := iter.Close(); e != nil { - if e == context.DeadlineExceeded { + if errs.Is(e, context.DeadlineExceeded) { if w := cs.logger.Check(zap.DebugLevel, "deadline exceeded for load query"); w != nil { w.Write(zap.String("system", cs.system), zap.String("query", query), zap.Error(e)) } } if !ignore(e) { - err = multierr.Append(err, errors.Errorf("system failed: %s", e.Error())) + err = multierr.Append(err, errors.Errorf("system failed: %s", err.Error())) } } }() @@ -141,6 +142,7 @@ func ignore(err error) bool { if err == nil { return true } + //nolint:errorlint switch err { case context.Canceled, context.DeadlineExceeded: return true diff --git a/store/helpers.go b/store/helpers.go index 3cacd2e4..ebab92c2 100644 --- a/store/helpers.go +++ b/store/helpers.go @@ -4,14 +4,13 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - package store import ( @@ -21,6 +20,7 @@ import ( "time" "github.com/gocql/gocql" + "github.com/scylladb/gemini" ) diff --git a/store/store.go b/store/store.go index b347585d..143d6e6b 100644 --- a/store/store.go +++ b/store/store.go @@ -4,14 +4,13 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - package store import ( @@ -30,11 +29,12 @@ import ( "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" - "github.com/scylladb/gemini" "github.com/scylladb/go-set/strset" "github.com/scylladb/gocqlx/v2/qb" "go.uber.org/multierr" "gopkg.in/inf.v0" + + "github.com/scylladb/gemini" ) type loader interface { @@ -65,7 +65,7 @@ type Config struct { UseServerSideTimestamps bool } -func New(schema *gemini.Schema, testCluster *gocql.ClusterConfig, oracleCluster *gocql.ClusterConfig, cfg Config, traceOut *os.File, logger *zap.Logger) Store { +func New(schema *gemini.Schema, testCluster, oracleCluster *gocql.ClusterConfig, cfg Config, traceOut *os.File, logger *zap.Logger) Store { ops := promauto.NewCounterVec(prometheus.CounterOpts{ Name: "gemini_cql_requests", Help: "How many CQL requests processed, partitioned by system and CQL query type aka 'method' (batch, delete, insert, update).", @@ -136,11 +136,11 @@ func (n *noOpStore) close() error { type delegatingStore struct { oracleStore storeLoader testStore storeLoader - validations bool logger *zap.Logger + validations bool } -func (ds delegatingStore) Create(ctx context.Context, testBuilder qb.Builder, oracleBuilder qb.Builder) error { +func (ds delegatingStore) Create(ctx context.Context, testBuilder, oracleBuilder qb.Builder) error { ts := time.Now() if err := mutate(ctx, ds.oracleStore, ts, oracleBuilder, []interface{}{}); err != nil { return errors.Wrap(err, "oracle failed store creation") diff --git a/tableopts/options.go b/tableopts/options.go index e21b795f..83e8a3cb 100644 --- a/tableopts/options.go +++ b/tableopts/options.go @@ -4,14 +4,13 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - package tableopts import ( @@ -34,8 +33,8 @@ func (o *SimpleOption) ToCQL() string { } type MapOption struct { - key string val map[string]interface{} + key string } func (o *MapOption) ToCQL() string { diff --git a/tableopts/options_test.go b/tableopts/options_test.go index f487dd8b..a5f28338 100644 --- a/tableopts/options_test.go +++ b/tableopts/options_test.go @@ -4,14 +4,15 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - +// +//nolint:lll package tableopts import ( diff --git a/types.go b/types.go index 0a282195..292b4111 100644 --- a/types.go +++ b/types.go @@ -4,14 +4,13 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - package gemini import ( @@ -69,10 +68,15 @@ var ( TYPE_BLOB: {}, TYPE_DURATION: {}, } - typesForIndex = []SimpleType{TYPE_DECIMAL, TYPE_DOUBLE, TYPE_FLOAT, TYPE_INT, TYPE_SMALLINT, TYPE_TINYINT, TYPE_VARINT} - partitionKeyTypes = []SimpleType{TYPE_INT, TYPE_SMALLINT, TYPE_TINYINT, TYPE_VARINT} - pkTypes = []SimpleType{TYPE_ASCII, TYPE_BIGINT, TYPE_BLOB, TYPE_DATE, TYPE_DECIMAL, TYPE_DOUBLE, TYPE_FLOAT, TYPE_INET, TYPE_INT, TYPE_SMALLINT, TYPE_TEXT /*TYPE_TIME,*/, TYPE_TIMESTAMP, TYPE_TIMEUUID, TYPE_TINYINT, TYPE_UUID, TYPE_VARCHAR, TYPE_VARINT} - types = append(append([]SimpleType{}, pkTypes...), TYPE_BOOLEAN, TYPE_DURATION) + typesForIndex = []SimpleType{TYPE_DECIMAL, TYPE_DOUBLE, TYPE_FLOAT, TYPE_INT, TYPE_SMALLINT, TYPE_TINYINT, TYPE_VARINT} + partitionKeyTypes = []SimpleType{TYPE_INT, TYPE_SMALLINT, TYPE_TINYINT, TYPE_VARINT} + pkTypes = []SimpleType{ + TYPE_ASCII, TYPE_BIGINT, TYPE_BLOB, TYPE_DATE, TYPE_DECIMAL, TYPE_DOUBLE, + TYPE_FLOAT, TYPE_INET, TYPE_INT, TYPE_SMALLINT, TYPE_TEXT /*TYPE_TIME,*/, TYPE_TIMESTAMP, TYPE_TIMEUUID, + TYPE_TINYINT, TYPE_UUID, TYPE_VARCHAR, TYPE_VARINT, + } + types = append(append([]SimpleType{}, pkTypes...), TYPE_BOOLEAN, TYPE_DURATION) + //nolint:unused compatibleColumnTypes = map[SimpleType][]SimpleType{ TYPE_ASCII: { TYPE_TEXT, @@ -251,10 +255,7 @@ func (st SimpleType) CQLType() gocql.TypeInfo { } func (st SimpleType) Indexable() bool { - if st == TYPE_DURATION { - return false - } - return true + return st != TYPE_DURATION } func (st SimpleType) GenValue(r *rand.Rand, p PartitionRangeConfig) []interface{} { @@ -308,47 +309,47 @@ type TupleType struct { Frozen bool `json:"frozen"` } -func (st TupleType) CQLType() gocql.TypeInfo { +func (t *TupleType) CQLType() gocql.TypeInfo { return goCQLTypeMap[gocql.TypeTuple] } -func (tt TupleType) Name() string { - names := make([]string, len(tt.Types), len(tt.Types)) - for i, t := range tt.Types { - names[i] = t.Name() +func (t *TupleType) Name() string { + names := make([]string, len(t.Types)) + for i, tp := range t.Types { + names[i] = tp.Name() } return "Type: " + strings.Join(names, ",") } -func (tt TupleType) CQLDef() string { - names := make([]string, len(tt.Types), len(tt.Types)) - for i, t := range tt.Types { - names[i] = t.CQLDef() +func (t *TupleType) CQLDef() string { + names := make([]string, len(t.Types)) + for i, tp := range t.Types { + names[i] = tp.CQLDef() } - if tt.Frozen { + if t.Frozen { return "frozen>" } return "tuple<" + strings.Join(names, ",") + ">" } -func (tt TupleType) CQLHolder() string { - return "(" + strings.TrimRight(strings.Repeat("?,", len(tt.Types)), ",") + ")" +func (t *TupleType) CQLHolder() string { + return "(" + strings.TrimRight(strings.Repeat("?,", len(t.Types)), ",") + ")" } -func (tt TupleType) CQLPretty(query string, value []interface{}) (string, int) { +func (t *TupleType) CQLPretty(query string, value []interface{}) (string, int) { if len(value) == 0 { return query, 0 } var cnt, tmp int - for i, t := range tt.Types { - query, tmp = t.CQLPretty(query, value[i:]) + for i, tp := range t.Types { + query, tmp = tp.CQLPretty(query, value[i:]) cnt += tmp } return query, cnt } -func (st TupleType) Indexable() bool { - for _, t := range st.Types { +func (t *TupleType) Indexable() bool { + for _, t := range t.Types { if t == TYPE_DURATION { return false } @@ -356,12 +357,12 @@ func (st TupleType) Indexable() bool { return true } -func (tt TupleType) GenValue(r *rand.Rand, p PartitionRangeConfig) []interface{} { - vals := make([]interface{}, 0, len(tt.Types)) - for _, t := range tt.Types { - vals = append(vals, t.GenValue(r, p)...) +func (t *TupleType) GenValue(r *rand.Rand, p PartitionRangeConfig) []interface{} { + out := make([]interface{}, 0, len(t.Types)) + for _, tp := range t.Types { + out = append(out, tp.GenValue(r, p)...) } - return vals + return out } type UDTType struct { @@ -370,32 +371,32 @@ type UDTType struct { Frozen bool `json:"frozen"` } -func (st UDTType) CQLType() gocql.TypeInfo { +func (t *UDTType) CQLType() gocql.TypeInfo { return goCQLTypeMap[gocql.TypeUDT] } -func (tt UDTType) Name() string { - return tt.TypeName +func (t *UDTType) Name() string { + return t.TypeName } -func (tt UDTType) CQLDef() string { - if tt.Frozen { - return "frozen<" + tt.TypeName + ">" +func (t *UDTType) CQLDef() string { + if t.Frozen { + return "frozen<" + t.TypeName + ">" } - return tt.TypeName + return t.TypeName } -func (tt UDTType) CQLHolder() string { +func (t *UDTType) CQLHolder() string { return "?" } -func (tt UDTType) CQLPretty(query string, value []interface{}) (string, int) { +func (t *UDTType) CQLPretty(query string, value []interface{}) (string, int) { if len(value) == 0 { return query, 0 } if s, ok := value[0].(map[string]interface{}); ok { vv := "{" - for k, v := range tt.Types { + for k, v := range t.Types { vv += fmt.Sprintf("%s:?,", k) vv, _ = v.CQLPretty(vv, []interface{}{s[k]}) } @@ -403,11 +404,11 @@ func (tt UDTType) CQLPretty(query string, value []interface{}) (string, int) { vv += "}" return strings.Replace(query, "?", vv, 1), 1 } - panic(fmt.Sprintf("udt pretty, unknown type %v", tt)) + panic(fmt.Sprintf("udt pretty, unknown type %v", t)) } -func (tt UDTType) Indexable() bool { - for _, t := range tt.Types { +func (t *UDTType) Indexable() bool { + for _, t := range t.Types { if t == TYPE_DURATION { return false } @@ -415,9 +416,9 @@ func (tt UDTType) Indexable() bool { return true } -func (tt UDTType) GenValue(r *rand.Rand, p PartitionRangeConfig) []interface{} { +func (t *UDTType) GenValue(r *rand.Rand, p PartitionRangeConfig) []interface{} { vals := make(map[string]interface{}) - for name, typ := range tt.Types { + for name, typ := range t.Types { vals[name] = typ.GenValue(r, p)[0] } return []interface{}{vals} @@ -429,8 +430,8 @@ type BagType struct { Frozen bool `json:"frozen"` } -func (st BagType) CQLType() gocql.TypeInfo { - switch st.Kind { +func (ct *BagType) CQLType() gocql.TypeInfo { + switch ct.Kind { case "set": return goCQLTypeMap[gocql.TypeSet] default: @@ -438,25 +439,25 @@ func (st BagType) CQLType() gocql.TypeInfo { } } -func (ct BagType) Name() string { +func (ct *BagType) Name() string { if ct.Frozen { return "frozen<" + ct.Kind + "<" + ct.Type.Name() + ">>" } return ct.Kind + "<" + ct.Type.Name() + ">" } -func (ct BagType) CQLDef() string { +func (ct *BagType) CQLDef() string { if ct.Frozen { return "frozen<" + ct.Kind + "<" + ct.Type.Name() + ">>" } return ct.Kind + "<" + ct.Type.Name() + ">" } -func (ct BagType) CQLHolder() string { +func (ct *BagType) CQLHolder() string { return "?" } -func (ct BagType) CQLPretty(query string, value []interface{}) (string, int) { +func (ct *BagType) CQLPretty(query string, value []interface{}) (string, int) { if len(value) == 0 { return query, 0 } @@ -475,16 +476,16 @@ func (ct BagType) CQLPretty(query string, value []interface{}) (string, int) { panic(fmt.Sprintf("set cql pretty, unknown type %v", ct)) } -func (ct BagType) GenValue(r *rand.Rand, p PartitionRangeConfig) []interface{} { +func (ct *BagType) GenValue(r *rand.Rand, p PartitionRangeConfig) []interface{} { count := r.Intn(9) + 1 - vals := make([]interface{}, count, count) + out := make([]interface{}, count) for i := 0; i < count; i++ { - vals[i] = ct.Type.GenValue(r, p)[0] + out[i] = ct.Type.GenValue(r, p)[0] } - return []interface{}{vals} + return []interface{}{out} } -func (ct BagType) Indexable() bool { +func (ct *BagType) Indexable() bool { return false } @@ -494,22 +495,22 @@ type MapType struct { Frozen bool `json:"frozen"` } -func (st MapType) CQLType() gocql.TypeInfo { +func (mt *MapType) CQLType() gocql.TypeInfo { return goCQLTypeMap[gocql.TypeMap] } -func (mt MapType) Name() string { +func (mt *MapType) Name() string { if mt.Frozen { return "frozen>" } return "map<" + mt.KeyType.Name() + "," + mt.ValueType.Name() + ">" } -func (mt MapType) CQLHolder() string { +func (mt *MapType) CQLHolder() string { return "?" } -func (mt MapType) CQLPretty(query string, value []interface{}) (string, int) { +func (mt *MapType) CQLPretty(query string, value []interface{}) (string, int) { switch reflect.TypeOf(value[0]).Kind() { case reflect.Map: s := reflect.ValueOf(value[0]).MapRange() @@ -525,7 +526,7 @@ func (mt MapType) CQLPretty(query string, value []interface{}) (string, int) { panic(fmt.Sprintf("map cql pretty, unknown type %v", mt)) } -func (mt MapType) GenValue(r *rand.Rand, p PartitionRangeConfig) []interface{} { +func (mt *MapType) GenValue(r *rand.Rand, p PartitionRangeConfig) []interface{} { count := r.Intn(9) + 1 vals := make(map[interface{}]interface{}) for i := 0; i < count; i++ { @@ -534,14 +535,14 @@ func (mt MapType) GenValue(r *rand.Rand, p PartitionRangeConfig) []interface{} { return []interface{}{vals} } -func (mt MapType) CQLDef() string { +func (mt *MapType) CQLDef() string { if mt.Frozen { return "frozen>" } return "map<" + mt.KeyType.CQLDef() + "," + mt.ValueType.CQLDef() + ">" } -func (mt MapType) Indexable() bool { +func (mt *MapType) Indexable() bool { return false } @@ -549,31 +550,31 @@ type CounterType struct { Value int64 } -func (ct CounterType) CQLType() gocql.TypeInfo { +func (ct *CounterType) CQLType() gocql.TypeInfo { return goCQLTypeMap[gocql.TypeMap] } -func (ct CounterType) Name() string { +func (ct *CounterType) Name() string { return "counter" } -func (ct CounterType) CQLHolder() string { +func (ct *CounterType) CQLHolder() string { return "?" } -func (ct CounterType) CQLPretty(query string, value []interface{}) (string, int) { +func (ct *CounterType) CQLPretty(query string, value []interface{}) (string, int) { return strings.Replace(query, "?", fmt.Sprintf("%d", value[0]), 1), 1 } -func (ct CounterType) GenValue(r *rand.Rand, p PartitionRangeConfig) []interface{} { +func (ct *CounterType) GenValue(r *rand.Rand, p PartitionRangeConfig) []interface{} { return []interface{}{atomic.AddInt64(&ct.Value, 1)} } -func (ct CounterType) CQLDef() string { +func (ct *CounterType) CQLDef() string { return "counter" } -func (ct CounterType) Indexable() bool { +func (ct *CounterType) Indexable() bool { return false } @@ -608,17 +609,17 @@ func genTupleType(sc *SchemaConfig) Type { if n < 2 { n = 2 } - typeList := make([]SimpleType, n, n) + typeList := make([]SimpleType, n) for i := 0; i < n; i++ { typeList[i] = genSimpleType(sc) } - return TupleType{ + return &TupleType{ Types: typeList, Frozen: rand.Uint32()%2 == 0, } } -func genUDTType(sc *SchemaConfig) UDTType { +func genUDTType(sc *SchemaConfig) *UDTType { udtNum := rand.Uint32() typeName := fmt.Sprintf("udt_%d", udtNum) ts := make(map[string]SimpleType) @@ -627,22 +628,22 @@ func genUDTType(sc *SchemaConfig) UDTType { ts[typeName+fmt.Sprintf("_%d", i)] = genSimpleType(sc) } - return UDTType{ + return &UDTType{ Types: ts, TypeName: typeName, Frozen: true, } } -func genSetType(sc *SchemaConfig) BagType { +func genSetType(sc *SchemaConfig) *BagType { return genBagType("set", sc) } -func genListType(sc *SchemaConfig) BagType { +func genListType(sc *SchemaConfig) *BagType { return genBagType("list", sc) } -func genBagType(kind string, sc *SchemaConfig) BagType { +func genBagType(kind string, sc *SchemaConfig) *BagType { var t SimpleType for { t = genSimpleType(sc) @@ -650,14 +651,14 @@ func genBagType(kind string, sc *SchemaConfig) BagType { break } } - return BagType{ + return &BagType{ Kind: kind, Type: t, Frozen: rand.Uint32()%2 == 0, } } -func genMapType(sc *SchemaConfig) MapType { +func genMapType(sc *SchemaConfig) *MapType { t := genSimpleType(sc) for { if _, ok := typesMapKeyBlacklist[t]; !ok { @@ -665,7 +666,7 @@ func genMapType(sc *SchemaConfig) MapType { } t = genSimpleType(sc) } - return MapType{ + return &MapType{ KeyType: t, ValueType: genSimpleType(sc), Frozen: rand.Uint32()%2 == 0, @@ -715,40 +716,43 @@ func (cd *ColumnDef) UnmarshalJSON(data []byte) error { return nil } -func getMapTypeColumn(data map[string]interface{}) (ColumnDef, error) { +func getMapTypeColumn(data map[string]interface{}) (out *ColumnDef, err error) { st := struct { - Name string Type map[string]interface{} + Name string }{} - err := mapstructure.Decode(data, &st) + + if err = mapstructure.Decode(data, &st); err != nil { + return nil, errors.Wrapf(err, "can't decode MapType value, value=%+v", data) + } if _, ok := st.Type["frozen"]; !ok { - return ColumnDef{}, errors.Errorf("not a map type, value=%v", st) + return nil, errors.Errorf("not a map type, value=%v", st) } if _, ok := st.Type["value_type"]; !ok { - return ColumnDef{}, errors.Errorf("not a map type, value=%v", st) + return nil, errors.Errorf("not a map type, value=%v", st) } if _, ok := st.Type["key_type"]; !ok { - return ColumnDef{}, errors.Errorf("not a map type, value=%v", st) + return nil, errors.Errorf("not a map type, value=%v", st) } var frozen bool - if err := mapstructure.Decode(st.Type["frozen"], &frozen); err != nil { - return ColumnDef{}, errors.Wrapf(err, "can't decode bool value for MapType::Frozen, value=%v", st) + if err = mapstructure.Decode(st.Type["frozen"], &frozen); err != nil { + return nil, errors.Wrapf(err, "can't decode bool value for MapType::Frozen, value=%v", st) } var valueType SimpleType - if err := mapstructure.Decode(st.Type["value_type"], &valueType); err != nil { - return ColumnDef{}, errors.Wrapf(err, "can't decode SimpleType value for MapType::ValueType, value=%v", st) + if err = mapstructure.Decode(st.Type["value_type"], &valueType); err != nil { + return nil, errors.Wrapf(err, "can't decode SimpleType value for MapType::ValueType, value=%v", st) } var keyType SimpleType - if err := mapstructure.Decode(st.Type["key_type"], &keyType); err != nil { - return ColumnDef{}, errors.Wrapf(err, "can't decode bool value for MapType::KeyType, value=%v", st) + if err = mapstructure.Decode(st.Type["key_type"], &keyType); err != nil { + return nil, errors.Wrapf(err, "can't decode bool value for MapType::KeyType, value=%v", st) } - return ColumnDef{ + return &ColumnDef{ Name: st.Name, - Type: MapType{ + Type: &MapType{ Frozen: frozen, ValueType: valueType, KeyType: keyType, @@ -756,28 +760,31 @@ func getMapTypeColumn(data map[string]interface{}) (ColumnDef, error) { }, err } -func getBagTypeColumn(data map[string]interface{}) (ColumnDef, error) { +func getBagTypeColumn(data map[string]interface{}) (out *ColumnDef, err error) { st := struct { - Name string Type map[string]interface{} + Name string }{} - err := mapstructure.Decode(data, &st) + + if err = mapstructure.Decode(data, &st); err != nil { + return nil, errors.Wrapf(err, "can't decode string value for BagType, value=%+v", data) + } var kind string - if err := mapstructure.Decode(st.Type["kind"], &kind); err != nil { - return ColumnDef{}, errors.Wrapf(err, "can't decode string value for BagType::Frozen, value=%v", st) + if err = mapstructure.Decode(st.Type["kind"], &kind); err != nil { + return nil, errors.Wrapf(err, "can't decode string value for BagType::Frozen, value=%v", st) } var frozen bool - if err := mapstructure.Decode(st.Type["frozen"], &frozen); err != nil { - return ColumnDef{}, errors.Wrapf(err, "can't decode bool value for BagType::Frozen, value=%v", st) + if err = mapstructure.Decode(st.Type["frozen"], &frozen); err != nil { + return nil, errors.Wrapf(err, "can't decode bool value for BagType::Frozen, value=%v", st) } var typ SimpleType - if err := mapstructure.Decode(st.Type["type"], &typ); err != nil { - return ColumnDef{}, errors.Wrapf(err, "can't decode SimpleType value for BagType::ValueType, value=%v", st) + if err = mapstructure.Decode(st.Type["type"], &typ); err != nil { + return nil, errors.Wrapf(err, "can't decode SimpleType value for BagType::ValueType, value=%v", st) } - return ColumnDef{ + return &ColumnDef{ Name: st.Name, - Type: BagType{ + Type: &BagType{ Kind: kind, Frozen: frozen, Type: typ, @@ -785,80 +792,86 @@ func getBagTypeColumn(data map[string]interface{}) (ColumnDef, error) { }, err } -func getTupleTypeColumn(data map[string]interface{}) (ColumnDef, error) { +func getTupleTypeColumn(data map[string]interface{}) (out *ColumnDef, err error) { st := struct { - Name string Type map[string]interface{} + Name string }{} - err := mapstructure.Decode(data, &st) + + if err = mapstructure.Decode(data, &st); err != nil { + return nil, errors.Wrapf(err, "can't decode []SimpleType value, value=%+v", data) + } if _, ok := st.Type["types"]; !ok { - return ColumnDef{}, errors.Errorf("not a tuple type, value=%v", st) + return nil, errors.Errorf("not a tuple type, value=%v", st) } - var types []SimpleType - if err := mapstructure.Decode(st.Type["types"], &types); err != nil { - return ColumnDef{}, errors.Wrapf(err, "can't decode []SimpleType value for TupleType::Types, value=%v", st) + var dbTypes []SimpleType + if err = mapstructure.Decode(st.Type["types"], &dbTypes); err != nil { + return nil, errors.Wrapf(err, "can't decode []SimpleType value for TupleType::Types, value=%v", st) } var frozen bool - if err := mapstructure.Decode(st.Type["frozen"], &frozen); err != nil { - return ColumnDef{}, errors.Wrapf(err, "can't decode bool value for TupleType::Types, value=%v", st) + if err = mapstructure.Decode(st.Type["frozen"], &frozen); err != nil { + return nil, errors.Wrapf(err, "can't decode bool value for TupleType::Types, value=%v", st) } - return ColumnDef{ + return &ColumnDef{ Name: st.Name, - Type: TupleType{ - Types: types, + Type: &TupleType{ + Types: dbTypes, Frozen: frozen, }, - }, err + }, nil } -func getUDTTypeColumn(data map[string]interface{}) (ColumnDef, error) { +func getUDTTypeColumn(data map[string]interface{}) (out *ColumnDef, err error) { st := struct { - Name string Type map[string]interface{} + Name string }{} - err := mapstructure.Decode(data, &st) + + if err = mapstructure.Decode(data, &st); err != nil { + return nil, errors.Wrapf(err, "can't decode []SimpleType , value=%+v", data) + } if _, ok := st.Type["types"]; !ok { - return ColumnDef{}, errors.Errorf("not a UDT type, value=%v", st) + return nil, errors.Errorf("not a UDT type, value=%v", st) } if _, ok := st.Type["type_name"]; !ok { - return ColumnDef{}, errors.Errorf("not a UDT type, value=%v", st) + return nil, errors.Errorf("not a UDT type, value=%v", st) } - var types map[string]SimpleType - if err := mapstructure.Decode(st.Type["types"], &types); err != nil { - return ColumnDef{}, errors.Wrapf(err, "can't decode []SimpleType value for UDTType::Types, value=%v", st) + var dbTypes map[string]SimpleType + if err = mapstructure.Decode(st.Type["types"], &dbTypes); err != nil { + return nil, errors.Wrapf(err, "can't decode []SimpleType value for UDTType::Types, value=%v", st) } var frozen bool - if err := mapstructure.Decode(st.Type["frozen"], &frozen); err != nil { - return ColumnDef{}, errors.Wrapf(err, "can't decode bool value for UDTType::Frozen, value=%v", st) + if err = mapstructure.Decode(st.Type["frozen"], &frozen); err != nil { + return nil, errors.Wrapf(err, "can't decode bool value for UDTType::Frozen, value=%v", st) } var typeName string - if err := mapstructure.Decode(st.Type["type_name"], &typeName); err != nil { - return ColumnDef{}, errors.Wrapf(err, "can't decode string value for UDTType::TypeName, value=%v", st) + if err = mapstructure.Decode(st.Type["type_name"], &typeName); err != nil { + return nil, errors.Wrapf(err, "can't decode string value for UDTType::TypeName, value=%v", st) } - return ColumnDef{ + return &ColumnDef{ Name: st.Name, - Type: UDTType{ - Types: types, + Type: &UDTType{ + Types: dbTypes, TypeName: typeName, Frozen: frozen, }, - }, err + }, nil } -func getSimpleTypeColumn(data map[string]interface{}) (ColumnDef, error) { +func getSimpleTypeColumn(data map[string]interface{}) (*ColumnDef, error) { st := struct { Name string Type SimpleType }{} err := mapstructure.Decode(data, &st) if err != nil { - return ColumnDef{}, err + return nil, err } - return ColumnDef{ + return &ColumnDef{ Name: st.Name, Type: st.Type, }, err diff --git a/types_test.go b/types_test.go index df8120e4..da54cc5e 100644 --- a/types_test.go +++ b/types_test.go @@ -4,14 +4,13 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - package gemini import ( @@ -26,15 +25,13 @@ import ( "gopkg.in/inf.v0" ) -var ( - millenium = time.Date(1999, 12, 31, 23, 59, 59, 0, time.UTC) -) +var millennium = time.Date(1999, 12, 31, 23, 59, 59, 0, time.UTC) var prettytests = []struct { typ Type query string - values []interface{} expected string + values []interface{} }{ { typ: TYPE_ASCII, @@ -63,7 +60,7 @@ var prettytests = []struct { { typ: TYPE_DATE, query: "SELECT * FROM tbl WHERE pk0=?", - values: []interface{}{millenium.Format("2006-01-02")}, + values: []interface{}{millennium.Format("2006-01-02")}, expected: "SELECT * FROM tbl WHERE pk0='1999-12-31'", }, { @@ -117,14 +114,14 @@ var prettytests = []struct { { typ: TYPE_TIME, query: "SELECT * FROM tbl WHERE pk0=?", - values: []interface{}{millenium}, - expected: "SELECT * FROM tbl WHERE pk0='" + millenium.Format(time.RFC3339) + "'", + values: []interface{}{millennium}, + expected: "SELECT * FROM tbl WHERE pk0='" + millennium.Format(time.RFC3339) + "'", }, { typ: TYPE_TIMESTAMP, query: "SELECT * FROM tbl WHERE pk0=?", - values: []interface{}{millenium}, - expected: "SELECT * FROM tbl WHERE pk0='" + millenium.Format(time.RFC3339) + "'", + values: []interface{}{millennium}, + expected: "SELECT * FROM tbl WHERE pk0='" + millennium.Format(time.RFC3339) + "'", }, { typ: TYPE_TIMEUUID, @@ -157,7 +154,7 @@ var prettytests = []struct { expected: "SELECT * FROM tbl WHERE pk0=1001", }, { - typ: BagType{ + typ: &BagType{ Kind: "set", Type: TYPE_ASCII, Frozen: false, @@ -167,7 +164,7 @@ var prettytests = []struct { expected: "SELECT * FROM tbl WHERE pk0={'a','b'}", }, { - typ: BagType{ + typ: &BagType{ Kind: "list", Type: TYPE_ASCII, Frozen: false, @@ -177,7 +174,7 @@ var prettytests = []struct { expected: "SELECT * FROM tbl WHERE pk0={'a','b'}", }, { - typ: MapType{ + typ: &MapType{ KeyType: TYPE_ASCII, ValueType: TYPE_ASCII, Frozen: false, @@ -187,7 +184,7 @@ var prettytests = []struct { expected: "SELECT * FROM tbl WHERE pk0={a:'b'}", }, { - typ: MapType{ + typ: &MapType{ KeyType: TYPE_ASCII, ValueType: TYPE_BLOB, Frozen: false, @@ -197,7 +194,7 @@ var prettytests = []struct { expected: "SELECT * FROM tbl WHERE pk0={a:textasblob('b')}", }, { - typ: TupleType{ + typ: &TupleType{ Types: []SimpleType{TYPE_ASCII}, Frozen: false, }, @@ -206,7 +203,7 @@ var prettytests = []struct { expected: "SELECT * FROM tbl WHERE pk0='a'", }, { - typ: TupleType{ + typ: &TupleType{ Types: []SimpleType{TYPE_ASCII, TYPE_ASCII}, Frozen: false, }, @@ -309,9 +306,9 @@ func TestMarshalUnmarshal(t *testing.T) { }, }, NonPrimaryKey: ColumnDef{ - Name: "", - Type: SimpleType(""), - }, + Name: "", + Type: SimpleType(""), + }, }, }, }, @@ -329,7 +326,7 @@ func TestMarshalUnmarshal(t *testing.T) { } s2 := &Schema{} - if err := json.Unmarshal(b, &s2); err != nil { + if err = json.Unmarshal(b, &s2); err != nil { t.Fatalf("unable to unmarshal json, error=%s\n", err) } diff --git a/utils.go b/utils.go new file mode 100644 index 00000000..d518cd68 --- /dev/null +++ b/utils.go @@ -0,0 +1,19 @@ +// Copyright 2019 ScyllaDB +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package gemini + +func IgnoreError(fn func() error) { + _ = fn() +} From 0140acb1241e7347be7a23215c9a869dfa7dec35 Mon Sep 17 00:00:00 2001 From: Dmitry Kropachev Date: Tue, 25 Apr 2023 11:11:18 -0400 Subject: [PATCH 4/4] Fix job scheduling --- cmd/gemini/jobs.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd/gemini/jobs.go b/cmd/gemini/jobs.go index 5081ef85..a2b6290f 100644 --- a/cmd/gemini/jobs.go +++ b/cmd/gemini/jobs.go @@ -217,8 +217,9 @@ func job( UseLWT: schemaConfig.UseLWT, } - for j, table := range schema.Tables { + for j := range schema.Tables { gen := generators[j] + table := schema.Tables[j] for i := 0; i < int(actors); i++ { r := rand.New(rand.NewSource(seed)) g.Go(func() error {