Skip to content

Commit

Permalink
logictest: retry support for "statement" and "query" commands
Browse files Browse the repository at this point in the history
This adds a retry command to logic tests which may be issued on a
separate line preceding either a "statement" or "query" command.
It causes the statement to be retried with exponential backoff up
to some maximum duration, e.g.
```
retry
statement error column "non_exist" does not exist
ALTER TABLE created_as_global SET LOCALITY REGIONAL BY ROW AS "non_exist"
```
This has the same effect as the retry option of the query command, but
now also supports "statement ok", "statement error" and "query error"
commands.

Retry of a query command may be specified by the standalone retry
command, the retry option of the query command, or both.

Fixes: cockroachdb#95668

Epic: CRDB-20535

Release note: None
  • Loading branch information
Mark Sirek committed Jun 29, 2023
1 parent 59d8113 commit a2d9f99
Showing 1 changed file with 36 additions and 7 deletions.
43 changes: 36 additions & 7 deletions pkg/sql/logictest/logic.go
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,12 @@ import (
// that occur after this command until the end of file or the next subtest
// command.
//
// - retry
// Specifies that the next occurrence of a statement or query directive
// (including those which expect errors) will be retried for a fixed
// duration until the test passes, or the alloted time has elapsed.
// This is similar to the retry option of the query directive.
//
// The overall architecture of TestLogic is as follows:
//
// - TestLogic() selects the input files and instantiates
Expand Down Expand Up @@ -834,9 +840,6 @@ type logicQuery struct {
colTypes string
// colNames controls the inclusion of column names in the query result.
colNames bool
// retry indicates if the query should be retried in case of failure with
// exponential backoff up to some maximum duration.
retry bool
// some tests require the output to match modulo sorting.
sorter logicSorter
// expectedErr and expectedErrCode are as in logicStatement.
Expand Down Expand Up @@ -1016,6 +1019,12 @@ type logicTest struct {
// declarativeCorpusCollector used to save declarative schema changer state
// to disk.
declarativeCorpusCollector *corpus.Collector

// retry indicates if the statement or query should be retried in case of
// failure with exponential backoff up to some maximum duration. It is reset
// to false after every successful statement or query test point, including
// those which are supposed to error out.
retry bool
}

func (t *logicTest) t() *testing.T {
Expand Down Expand Up @@ -2323,6 +2332,8 @@ func (t *logicTest) processSubtest(
t.lastProgress = timeutil.Now()

repeat := 1
t.retry = false

for s.Scan() {
t.curPath, t.curLineNo = path, s.Line+subtest.lineLineIndexIntoFile
if *maxErrs > 0 && t.failures >= *maxErrs {
Expand Down Expand Up @@ -2411,6 +2422,11 @@ func (t *logicTest) processSubtest(

t.success(path)

case "retry":
// retry is a standalone command that may precede a "statement" or "query"
// command. It has the same retry effect as the retry option of the query
// command.
t.retry = true
case "statement":
stmt := logicStatement{
pos: fmt.Sprintf("\n%s:%d", path, s.Line+subtest.lineLineIndexIntoFile),
Expand Down Expand Up @@ -2441,7 +2457,19 @@ func (t *logicTest) processSubtest(
}
if !s.Skip {
for i := 0; i < repeat; i++ {
if cont, err := t.execStatement(stmt); err != nil {
var cont bool
var err error
if t.retry {
err = testutils.SucceedsSoonError(func() error {
t.purgeZoneConfig()
var tempErr error
cont, tempErr = t.execStatement(stmt)
return tempErr
})
} else {
cont, err = t.execStatement(stmt)
}
if err != nil {
if !cont {
return err
}
Expand Down Expand Up @@ -2582,7 +2610,7 @@ func (t *logicTest) processSubtest(
query.colNames = true

case "retry":
query.retry = true
t.retry = true

case "kvtrace":
// kvtrace without any arguments doesn't perform any additional
Expand Down Expand Up @@ -2765,15 +2793,15 @@ func (t *logicTest) processSubtest(
}

for i := 0; i < repeat; i++ {
if query.retry && !*rewriteResultsInTestfiles {
if t.retry && !*rewriteResultsInTestfiles {
if err := testutils.SucceedsSoonError(func() error {
t.purgeZoneConfig()
return t.execQuery(query)
}); err != nil {
t.Error(err)
}
} else {
if query.retry && *rewriteResultsInTestfiles {
if t.retry && *rewriteResultsInTestfiles {
t.purgeZoneConfig()
// The presence of the retry flag indicates that we expect this
// query may need some time to succeed. If we are rewriting, wait
Expand Down Expand Up @@ -3679,6 +3707,7 @@ func (t *logicTest) formatValues(vals []string, valsPerLine int) []string {
}

func (t *logicTest) success(file string) {
t.retry = false
t.progress++
now := timeutil.Now()
if now.Sub(t.lastProgress) >= 2*time.Second {
Expand Down

0 comments on commit a2d9f99

Please sign in to comment.