Skip to content

Commit

Permalink
metamorphic: transform percentage of SINGLEDEL ops to DELETE ops
Browse files Browse the repository at this point in the history
Generated SINGLEDEL operations are eligible for transformation into less
restrictive DELETE operations.

Transform a fixed percentage of SINGLEDEL operations at generation time
into DELETEs to further exercise the delete execution paths.

Related to cockroachdb/cockroach#69414.
  • Loading branch information
nicktrav committed Sep 8, 2021
1 parent 41239f8 commit e0f0a4a
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 7 deletions.
6 changes: 6 additions & 0 deletions internal/metamorphic/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -843,6 +843,12 @@ func (g *generator) writerSingleDelete() {
g.add(&singleDeleteOp{
writerID: writerID,
key: key,
// Keys eligible for single deletes can be removed with a regular
// delete. Mutate a percentage of SINGLEDEL ops into DELETEs. Note that
// here we are only determining whether the replacement *could* happen.
// At test runtime, the `replaceSingleDelete` test option must also be
// set to true for the single delete to be replaced.
maybeReplaceDelete: g.rng.Float64() < 0.25,
})
g.tryRepositionBatchIters(writerID)
}
Expand Down
3 changes: 2 additions & 1 deletion internal/metamorphic/meta_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,8 @@ func TestMeta(t *testing.T) {
}

// Perform runs with random options.
for i := 0; i < 20; i++ {
nOpts := len(options)
for i := 0; i < nOpts; i++ {
name := fmt.Sprintf("random-%03d", i)
names = append(names, name)
opts := randomOptions(rng)
Expand Down
19 changes: 15 additions & 4 deletions internal/metamorphic/ops.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,18 +132,29 @@ func (o *deleteOp) String() string {

// singleDeleteOp models a Write.SingleDelete operation.
type singleDeleteOp struct {
writerID objID
key []byte
writerID objID
key []byte
maybeReplaceDelete bool
}

func (o *singleDeleteOp) run(t *test, h *history) {
w := t.getWriter(o.writerID)
err := w.SingleDelete(o.key, t.writeOpts)
var err error
if t.testOpts.replaceSingleDelete && o.maybeReplaceDelete {
err = w.Delete(o.key, t.writeOpts)
} else {
err = w.SingleDelete(o.key, t.writeOpts)
}
// NOTE: even if the SINGLEDEL was replaced with a DELETE, we must still
// write the former to the history log. The log line will indicate whether
// or not the delete *could* have been replaced. The OPTIONS file should
// also be consulted to determine what happened at runtime (i.e. by taking
// the logical AND).
h.Recordf("%s // %v", o, err)
}

func (o *singleDeleteOp) String() string {
return fmt.Sprintf("%s.SingleDelete(%q)", o.writerID, o.key)
return fmt.Sprintf("%s.SingleDelete(%q, %v /* maybeReplaceDelete */)", o.writerID, o.key, o.maybeReplaceDelete)
}

// deleteRangeOp models a Write.DeleteRange operation.
Expand Down
15 changes: 14 additions & 1 deletion internal/metamorphic/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ func parseOptions(opts *testOptions, data string) error {
case "TestOptions.ingest_using_apply":
opts.ingestUsingApply = true
return true
case "TestOptions.replace_single_delete":
opts.replaceSingleDelete = true
return true
default:
return false
}
Expand All @@ -50,7 +53,7 @@ func parseOptions(opts *testOptions, data string) error {

func optionsToString(opts *testOptions) string {
str := opts.opts.String()
if opts.strictFS || opts.ingestUsingApply {
if opts.strictFS || opts.ingestUsingApply || opts.replaceSingleDelete {
str += "\n[TestOptions]\n"
}
if opts.strictFS {
Expand All @@ -59,6 +62,9 @@ func optionsToString(opts *testOptions) string {
if opts.ingestUsingApply {
str += " ingest_using_apply=true\n"
}
if opts.replaceSingleDelete {
str += " replace_single_delete=true\n"
}
return str
}

Expand All @@ -79,6 +85,8 @@ type testOptions struct {
strictFS bool
// Use Batch.Apply rather than DB.Ingest.
ingestUsingApply bool
// Replace a SINGLEDEL with a DELETE.
replaceSingleDelete bool
}

func standardOptions() []*testOptions {
Expand Down Expand Up @@ -165,6 +173,10 @@ func standardOptions() []*testOptions {
19: `
[TestOptions]
ingest_using_apply=true
`,
20: `
[TestOptions]
replace_single_delete=true
`,
}

Expand Down Expand Up @@ -214,5 +226,6 @@ func randomOptions(rng *rand.Rand) *testOptions {
opts.DisableWAL = false
}
testOpts.ingestUsingApply = rng.Intn(2) != 0
testOpts.replaceSingleDelete = rng.Intn(2) != 0
return testOpts
}
10 changes: 9 additions & 1 deletion internal/metamorphic/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ func opArgs(op op) (receiverID *objID, targetID *objID, args []interface{}) {
case *iterSetBoundsOp:
return &t.iterID, nil, []interface{}{&t.lower, &t.upper}
case *singleDeleteOp:
return &t.writerID, nil, []interface{}{&t.key}
return &t.writerID, nil, []interface{}{&t.key, &t.maybeReplaceDelete}
}
panic(fmt.Sprintf("unsupported op type: %T", op))
}
Expand Down Expand Up @@ -262,6 +262,14 @@ func (p *parser) parseArgs(op op, methodName string, args []interface{}) {
*t = []byte(s)
}

case *bool:
_, lit := p.scanToken(token.IDENT)
b, err := strconv.ParseBool(lit)
if err != nil {
panic(err)
}
*t = b

case *objID:
pos, lit := p.scanToken(token.IDENT)
*t = p.parseObjID(pos, lit)
Expand Down

0 comments on commit e0f0a4a

Please sign in to comment.