diff --git a/pkg/sql/logictest/logic.go b/pkg/sql/logictest/logic.go index de71bbbb26bb..3288f5b0b519 100644 --- a/pkg/sql/logictest/logic.go +++ b/pkg/sql/logictest/logic.go @@ -409,6 +409,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 @@ -866,9 +872,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. @@ -1054,6 +1057,12 @@ type logicTest struct { // backup and restore before running the next SQL statement. This can be set // to true using the `force-backup-restore` directive. forceBackupAndRestore bool + + // 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 { @@ -2394,6 +2403,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 { @@ -2479,6 +2490,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), @@ -2509,7 +2525,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 } @@ -2650,7 +2678,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 @@ -2833,7 +2861,7 @@ 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) @@ -2841,7 +2869,7 @@ func (t *logicTest) processSubtest( 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 @@ -3789,6 +3817,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 {