Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

workload/schemachange: improve error screening #56379

Merged
Prev Previous commit
Next Next commit
workload/schemachange: expand sequence generation features
Previously sequences would only be attached to the public
schema by default. This commit enhances the workload by allowing
sequences to belong to other schemas. Furthermore, this commit
adds the possibility that a sequence can be owned by a column
in a table which belongs to a different schema.

Release note: None
  • Loading branch information
jayshrivastava committed Nov 25, 2020
commit 18ea68e2b177e002cc2503a38a49b8467aa7bb3b
144 changes: 119 additions & 25 deletions pkg/workload/schemachange/operation_generator.go
Original file line number Diff line number Diff line change
@@ -31,12 +31,13 @@ import (
// seqNum may be shared across multiple instances of this, so it should only
// be change atomically.
type operationGeneratorParams struct {
seqNum *int64
errorRate int
enumPct int
rng *rand.Rand
ops *deck
maxSourceTables int
seqNum *int64
errorRate int
enumPct int
rng *rand.Rand
ops *deck
maxSourceTables int
sequenceOwnedByPct int
}

// The OperationBuilder has the sole responsibility of generating ops
@@ -353,7 +354,40 @@ func (og *operationGenerator) createIndex(tx *pgx.Tx) (string, error) {
}

func (og *operationGenerator) createSequence(tx *pgx.Tx) (string, error) {
return fmt.Sprintf(`CREATE SEQUENCE "seq%d"`, og.newUniqueSeqNum()), nil
seqName, err := og.randSequence(tx, og.pctExisting(false), "")
if err != nil {
return "", err
}

ifNotExists := og.randIntn(2) == 0

var seqOptions tree.SequenceOptions
// Decide if the sequence should be owned by a column. If so, it can
// set using the tree.SeqOptOwnedBy sequence option.
if og.randIntn(100) < og.params.sequenceOwnedByPct {
table, err := og.randTable(tx, og.pctExisting(true), "")
if err != nil {
return "", err
}
column, err := og.randColumn(tx, *table, og.pctExisting(true))
if err != nil {
return "", err
}

seqOptions = append(
seqOptions,
tree.SequenceOption{
Name: tree.SeqOptOwnedBy,
ColumnItemVal: &tree.ColumnItem{TableName: table.ToUnresolvedObjectName(), ColumnName: tree.Name(column)}},
)
}
createSeq := &tree.CreateSequence{
IfNotExists: ifNotExists,
Name: *seqName,
Options: seqOptions,
}

return tree.Serialize(createSeq), nil
}

func (og *operationGenerator) createTable(tx *pgx.Tx) (string, error) {
@@ -790,11 +824,16 @@ func (og *operationGenerator) dropIndex(tx *pgx.Tx) (string, error) {
}

func (og *operationGenerator) dropSequence(tx *pgx.Tx) (string, error) {
sequenceName, err := og.randSequence(tx, og.pctExisting(true))
sequenceName, err := og.randSequence(tx, og.pctExisting(true), "")
if err != nil {
return "", err
}
return fmt.Sprintf(`DROP SEQUENCE "%s"`, sequenceName), nil
ifExists := og.randIntn(2) == 0
dropSeq := &tree.DropSequence{
Names: tree.TableNames{*sequenceName},
IfExists: ifExists,
}
return tree.Serialize(dropSeq), nil
}

func (og *operationGenerator) dropTable(tx *pgx.Tx) (string, error) {
@@ -928,17 +967,17 @@ func (og *operationGenerator) renameIndex(tx *pgx.Tx) (string, error) {
}

func (og *operationGenerator) renameSequence(tx *pgx.Tx) (string, error) {
srcSequenceName, err := og.randSequence(tx, og.pctExisting(true))
srcSequenceName, err := og.randSequence(tx, og.pctExisting(true), "")
if err != nil {
return "", err
}

destSequenceName, err := og.randSequence(tx, og.pctExisting(false))
destSequenceName, err := og.randSequence(tx, og.pctExisting(false), "")
if err != nil {
return "", err
}

return fmt.Sprintf(`ALTER SEQUENCE "%s" RENAME TO "%s"`, srcSequenceName, destSequenceName), nil
return fmt.Sprintf(`ALTER SEQUENCE %s RENAME TO %s`, srcSequenceName, destSequenceName), nil
}

func (og *operationGenerator) renameTable(tx *pgx.Tx) (string, error) {
@@ -1244,22 +1283,77 @@ ORDER BY random()
return name, nil
}

func (og *operationGenerator) randSequence(tx *pgx.Tx, pctExisting int) (string, error) {
// randSequence returns a sequence qualified by a schema
func (og *operationGenerator) randSequence(
tx *pgx.Tx, pctExisting int, desiredSchema string,
) (*tree.TableName, error) {

if desiredSchema != "" {
if og.randIntn(100) >= pctExisting {
treeSeqName := tree.MakeTableNameFromPrefix(tree.ObjectNamePrefix{
SchemaName: tree.Name(desiredSchema),
ExplicitSchema: true,
}, tree.Name(fmt.Sprintf("seq%d", og.newUniqueSeqNum())))
return &treeSeqName, nil
}
q := fmt.Sprintf(`
SELECT sequence_name
FROM [SHOW SEQUENCES]
WHERE sequence_name LIKE 'seq%%'
AND sequence_schema = '%s'
ORDER BY random()
LIMIT 1;
`, desiredSchema)

var seqName string
if err := tx.QueryRow(q).Scan(&seqName); err != nil {
treeSeqName := tree.MakeTableNameFromPrefix(tree.ObjectNamePrefix{}, "")
return &treeSeqName, err
}

treeSeqName := tree.MakeTableNameFromPrefix(tree.ObjectNamePrefix{
SchemaName: tree.Name(desiredSchema),
ExplicitSchema: true,
}, tree.Name(seqName))
return &treeSeqName, nil
}

if og.randIntn(100) >= pctExisting {
return fmt.Sprintf(`seq%d`, og.newUniqueSeqNum()), nil
// Most of the time, this case is for creating sequences, so it
// is preferable that the schema exists.
randSchema, err := og.randSchema(tx, og.pctExisting(true))
if err != nil {
treeSeqName := tree.MakeTableNameFromPrefix(tree.ObjectNamePrefix{}, "")
return &treeSeqName, err
}
treeSeqName := tree.MakeTableNameFromPrefix(tree.ObjectNamePrefix{
SchemaName: tree.Name(randSchema),
ExplicitSchema: true,
}, tree.Name(fmt.Sprintf("seq%d", og.newUniqueSeqNum())))
return &treeSeqName, nil
}
const q = `
SELECT sequence_name
FROM [SHOW SEQUENCES]
WHERE sequence_name LIKE 'seq%'
ORDER BY random()
LIMIT 1;
`
var name string
if err := tx.QueryRow(q).Scan(&name); err != nil {
return "", err

q := `
SELECT sequence_schema, sequence_name
FROM [SHOW SEQUENCES]
WHERE sequence_name LIKE 'seq%%'
ORDER BY random()
LIMIT 1;
`

var schemaName string
var seqName string
if err := tx.QueryRow(q).Scan(&schemaName, &seqName); err != nil {
treeTableName := tree.MakeTableNameFromPrefix(tree.ObjectNamePrefix{}, "")
return &treeTableName, err
}
return name, nil

treeSeqName := tree.MakeTableNameFromPrefix(tree.ObjectNamePrefix{
SchemaName: tree.Name(schemaName),
ExplicitSchema: true,
}, tree.Name(seqName))
return &treeSeqName, nil

}

func (og *operationGenerator) randEnum(tx *pgx.Tx, pctExisting int) (tree.UnresolvedName, error) {
43 changes: 24 additions & 19 deletions pkg/workload/schemachange/schemachange.go
Original file line number Diff line number Diff line change
@@ -53,22 +53,24 @@ import (
//For example, an attempt to do something we don't support should be swallowed (though if we can detect that maybe we should just not do it, e.g). It will be hard to use this test for anything more than liveness detection until we go through the tedious process of classifying errors.:

const (
defaultMaxOpsPerWorker = 5
defaultErrorRate = 10
defaultEnumPct = 10
defaultMaxSourceTables = 3
defaultMaxOpsPerWorker = 5
defaultErrorRate = 10
defaultEnumPct = 10
defaultMaxSourceTables = 3
defaultSequenceOwnedByPct = 25
)

type schemaChange struct {
flags workload.Flags
dbOverride string
concurrency int
maxOpsPerWorker int
errorRate int
enumPct int
verbose int
dryRun bool
maxSourceTables int
flags workload.Flags
dbOverride string
concurrency int
maxOpsPerWorker int
errorRate int
enumPct int
verbose int
dryRun bool
maxSourceTables int
sequenceOwnedByPct int
}

var schemaChangeMeta = workload.Meta{
@@ -92,6 +94,8 @@ var schemaChangeMeta = workload.Meta{
s.flags.BoolVarP(&s.dryRun, `dry-run`, `n`, false, ``)
s.flags.IntVar(&s.maxSourceTables, `max-source-tables`, defaultMaxSourceTables,
`Maximum tables or views that a newly created tables or views can depend on`)
s.flags.IntVar(&s.sequenceOwnedByPct, `seq-owned-pct`, defaultSequenceOwnedByPct,
`Percentage of times that a sequence is owned by column upon creation.`)
return s
},
}
@@ -142,12 +146,13 @@ func (s *schemaChange) Ops(
for i := 0; i < s.concurrency; i++ {

opGeneratorParams := operationGeneratorParams{
seqNum: seqNum,
errorRate: s.errorRate,
enumPct: s.enumPct,
rng: rand.New(rand.NewSource(timeutil.Now().UnixNano())),
ops: ops,
maxSourceTables: s.maxSourceTables,
seqNum: seqNum,
errorRate: s.errorRate,
enumPct: s.enumPct,
rng: rand.New(rand.NewSource(timeutil.Now().UnixNano())),
ops: ops,
maxSourceTables: s.maxSourceTables,
sequenceOwnedByPct: s.sequenceOwnedByPct,
}

w := &schemaChangeWorker{