Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 14 additions & 5 deletions pkg/statistics/handle/autoanalyze/priorityqueue/interval.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ const LastFailedDurationQueryForPartition = `
`

// GetAverageAnalysisDuration returns the average duration of the last 5 successful analyses for each specified partition.
// If there are no successful analyses, it returns 0.
// If there are no successful analyses, it returns NoRecord.
func GetAverageAnalysisDuration(
sctx sessionctx.Context,
schema, tableName string,
Expand All @@ -104,7 +104,7 @@ func GetAverageAnalysisDuration(
return NoRecord, err
}

// NOTE: if there are no successful analyses, we return 0.
// NOTE: if there are no successful analyses, we return NoRecord.
if len(rows) == 0 || rows[0].IsNull(0) {
return NoRecord, nil
}
Expand All @@ -113,12 +113,16 @@ func GetAverageAnalysisDuration(
if err != nil {
return NoRecord, err
}
if duration < 0 {
// Defensive: bad records or clock skew could yield negative durations.
return NoRecord, nil
}

return time.Duration(duration) * time.Second, nil
}

// GetLastFailedAnalysisDuration returns the duration since the last failed analysis.
// If there is no failed analysis, it returns 0.
// If there is no failed analysis, it returns NoRecord.
func GetLastFailedAnalysisDuration(
sctx sessionctx.Context,
schema, tableName string,
Expand All @@ -139,14 +143,19 @@ func GetLastFailedAnalysisDuration(
return NoRecord, err
}

// NOTE: if there are no failed analyses, we return 0.
// NOTE: if there are no failed analyses, we return NoRecord.
if len(rows) == 0 || rows[0].IsNull(0) {
return NoRecord, nil
}
lastFailedDuration := rows[0].GetUint64(0)
lastFailedDuration := rows[0].GetInt64(0)
if lastFailedDuration == 0 {
return justFailed, nil
}
if lastFailedDuration < 0 {
// Defensive: a bad record or clock skew can make start_time in the future.
// Treat it as a bounded default instead of "no record".
return defaultFailedAnalysisWaitTime, nil
}

return time.Duration(lastFailedDuration) * time.Second, nil
}
31 changes: 31 additions & 0 deletions pkg/statistics/handle/autoanalyze/priorityqueue/interval_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,22 @@ func TestGetAverageAnalysisDuration(t *testing.T) {
require.Equal(t, time.Duration(3600)*time.Second, avgDuration)
}

func TestGetAverageAnalysisDurationNegativeRecord(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")
tk.MustExec(metadef.CreateAnalyzeJobsTable)

// Insert a finished record with end_time earlier than start_time to force a negative duration.
// This could happen if the system clock is adjusted backward (rare in practice).
insertFinishedJob(tk, "neg_schema", "neg_table", "", "2024-01-01 10:00:00", "2024-01-01 09:00:00")

sctx := tk.Session().(sessionctx.Context)
avgDuration, err := priorityqueue.GetAverageAnalysisDuration(sctx, "neg_schema", "neg_table")
require.NoError(t, err)
require.Equal(t, time.Duration(priorityqueue.NoRecord), avgDuration)
}

func insertMultipleFinishedJobs(tk *testkit.TestKit, tableName string, partitionName string) {
jobs := []struct {
dbName string
Expand Down Expand Up @@ -130,6 +146,21 @@ func TestGetLastFailedAnalysisDuration(t *testing.T) {
require.GreaterOrEqual(t, lastFailedDuration, time.Duration(24)*time.Hour)
}

func TestGetLastFailedAnalysisDurationNegativeRecord(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")
tk.MustExec(metadef.CreateAnalyzeJobsTable)

// Insert a failed record whose start_time is in the future to force a negative duration.
insertFailedJobWithStartTime(tk, "neg_schema", "neg_table", "", "2037-01-01 00:00:00")

sctx := tk.Session().(sessionctx.Context)
lastFailedDuration, err := priorityqueue.GetLastFailedAnalysisDuration(sctx, "neg_schema", "neg_table")
require.NoError(t, err)
require.Equal(t, 30*time.Minute, lastFailedDuration)
}

func initJobs(tk *testkit.TestKit) {
tk.MustExec(`
INSERT INTO mysql.analyze_jobs (
Expand Down