Skip to content

Commit 3ce6308

Browse files
yimincrodrigozhou
authored andcommitted
Fix cron for schedules that do not match any time (#4206)
* Fix cron for schedules that do not match any time * Use NoBackoff
1 parent 99c10cc commit 3ce6308

File tree

2 files changed

+42
-25
lines changed

2 files changed

+42
-25
lines changed

common/backoff/cron.go

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,14 @@ func ValidateSchedule(cronSchedule string) error {
4141
if cronSchedule == "" {
4242
return nil
4343
}
44-
if _, err := cron.ParseStandard(cronSchedule); err != nil {
45-
return serviceerror.NewInvalidArgument("Invalid CronSchedule.")
44+
schedule, err := cron.ParseStandard(cronSchedule)
45+
if err != nil {
46+
return serviceerror.NewInvalidArgument("invalid CronSchedule.")
47+
}
48+
nextTime := schedule.Next(time.Now().UTC())
49+
if nextTime.IsZero() {
50+
// no time can be found to satisfy the schedule
51+
return serviceerror.NewInvalidArgument("invalid CronSchedule, no time can be found to satisfy the schedule")
4652
}
4753
return nil
4854
}
@@ -68,10 +74,14 @@ func GetBackoffForNextSchedule(cronSchedule string, scheduledTime time.Time, now
6874
} else {
6975
nextScheduleTime = schedule.Next(scheduledUTCTime)
7076
// Calculate the next schedule start time which is nearest to now (right after now).
71-
for nextScheduleTime.Before(nowUTC) {
77+
for !nextScheduleTime.IsZero() && nextScheduleTime.Before(nowUTC) {
7278
nextScheduleTime = schedule.Next(nextScheduleTime)
7379
}
7480
}
81+
if nextScheduleTime.IsZero() {
82+
// no time can be found to satisfy the schedule
83+
return NoBackoff
84+
}
7585

7686
backoffInterval := nextScheduleTime.Sub(nowUTC)
7787
roundedInterval := time.Second * time.Duration(convert.Int64Ceil(backoffInterval.Seconds()))

common/backoff/cron_test.go

Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -37,27 +37,30 @@ var crontests = []struct {
3737
scheduledTime string
3838
now string
3939
result time.Duration
40+
err string
4041
}{
41-
{"0 10 * * *", "2018-12-17T08:00:00-08:00", "", time.Hour * 18},
42-
{"0 10 * * *", "2018-12-18T02:00:00-08:00", "", time.Hour * 24},
43-
{"0 * * * *", "2018-12-17T08:08:00+00:00", "", time.Minute * 52},
44-
{"0 * * * *", "2018-12-17T09:00:00+00:00", "", time.Hour},
45-
{"* * * * *", "2018-12-17T08:08:18+00:00", "", time.Second * 42},
46-
{"0 * * * *", "2018-12-17T09:00:00+00:00", "", time.Minute * 60},
47-
{"0 10 * * *", "2018-12-17T08:00:00+00:00", "2018-12-20T00:00:00+00:00", time.Hour * 10},
48-
{"0 10 * * *", "2018-12-17T08:00:00+00:00", "2018-12-17T09:00:00+00:00", time.Hour},
49-
{"*/10 * * * *", "2018-12-17T00:04:00+00:00", "2018-12-17T01:02:00+00:00", time.Minute * 8},
50-
{"invalid-cron-spec", "2018-12-17T00:04:00+00:00", "2018-12-17T01:02:00+00:00", NoBackoff},
51-
{"@every 5h", "2018-12-17T08:00:00+00:00", "2018-12-17T09:00:00+00:00", time.Hour * 4},
52-
{"@every 5h", "2018-12-17T08:00:00+00:00", "2018-12-18T00:00:00+00:00", time.Hour * 4},
53-
{"0 3 * * 0-6", "2018-12-17T08:00:00-08:00", "", time.Hour * 11},
54-
{"@every 30s", "2020-07-17T09:00:02-01:00", "2020-07-17T09:00:02-01:00", time.Second * 30},
55-
{"@every 30s", "2020-07-17T09:00:02-01:00", "2020-09-17T03:00:53-01:00", time.Second * 9},
56-
{"@every 30m", "2020-07-17T09:00:00-01:00", "2020-07-17T08:45:00-01:00", time.Minute * 15},
42+
{"0 0 31 2 *", "2018-12-17T08:00:00-08:00", "", 0, "invalid CronSchedule, no time can be found to satisfy the schedule"},
43+
{"invalid-cron-spec", "2018-12-17T00:04:00+00:00", "2018-12-17T01:02:00+00:00", NoBackoff, "invalid CronSchedule"},
44+
45+
{"0 10 * * *", "2018-12-17T08:00:00-08:00", "", time.Hour * 18, ""},
46+
{"0 10 * * *", "2018-12-18T02:00:00-08:00", "", time.Hour * 24, ""},
47+
{"0 * * * *", "2018-12-17T08:08:00+00:00", "", time.Minute * 52, ""},
48+
{"0 * * * *", "2018-12-17T09:00:00+00:00", "", time.Hour, ""},
49+
{"* * * * *", "2018-12-17T08:08:18+00:00", "", time.Second * 42, ""},
50+
{"0 * * * *", "2018-12-17T09:00:00+00:00", "", time.Minute * 60, ""},
51+
{"0 10 * * *", "2018-12-17T08:00:00+00:00", "2018-12-20T00:00:00+00:00", time.Hour * 10, ""},
52+
{"0 10 * * *", "2018-12-17T08:00:00+00:00", "2018-12-17T09:00:00+00:00", time.Hour, ""},
53+
{"*/10 * * * *", "2018-12-17T00:04:00+00:00", "2018-12-17T01:02:00+00:00", time.Minute * 8, ""},
54+
{"@every 5h", "2018-12-17T08:00:00+00:00", "2018-12-17T09:00:00+00:00", time.Hour * 4, ""},
55+
{"@every 5h", "2018-12-17T08:00:00+00:00", "2018-12-18T00:00:00+00:00", time.Hour * 4, ""},
56+
{"0 3 * * 0-6", "2018-12-17T08:00:00-08:00", "", time.Hour * 11, ""},
57+
{"@every 30s", "2020-07-17T09:00:02-01:00", "2020-07-17T09:00:02-01:00", time.Second * 30, ""},
58+
{"@every 30s", "2020-07-17T09:00:02-01:00", "2020-09-17T03:00:53-01:00", time.Second * 9, ""},
59+
{"@every 30m", "2020-07-17T09:00:00-01:00", "2020-07-17T08:45:00-01:00", time.Minute * 15, ""},
5760
// At 16:05 East Coast (Day light saving on)
58-
{"CRON_TZ=America/New_York 5 16 * * *", "2021-03-14T00:00:00-04:00", "2021-03-14T15:05:00-04:00", time.Hour * 1},
61+
{"CRON_TZ=America/New_York 5 16 * * *", "2021-03-14T00:00:00-04:00", "2021-03-14T15:05:00-04:00", time.Hour * 1, ""},
5962
// At 04:05 East Coast (Day light saving off)
60-
{"CRON_TZ=America/New_York 5 4 * * *", "2021-11-25T00:00:00-05:00", "2021-11-25T03:05:00-05:00", time.Hour * 1},
63+
{"CRON_TZ=America/New_York 5 4 * * *", "2021-11-25T00:00:00-05:00", "2021-11-25T03:05:00-05:00", time.Hour * 1, ""},
6164
}
6265

6366
func TestCron(t *testing.T) {
@@ -71,11 +74,15 @@ func TestCron(t *testing.T) {
7174
assert.NoError(t, err, "Parse end time: %v", tt.now)
7275
}
7376
err = ValidateSchedule(tt.cron)
74-
if tt.result != NoBackoff {
75-
assert.NoError(t, err)
77+
if len(tt.err) > 0 {
78+
assert.Error(t, err)
79+
assert.Contains(t, err.Error(), tt.err)
80+
} else {
81+
assert.NoError(t, err, "Failed on cron schedule: %v", tt.cron)
82+
83+
backoff := GetBackoffForNextSchedule(tt.cron, start, end)
84+
assert.Equal(t, tt.result, backoff, "The cron spec is %s and the expected result is %s", tt.cron, tt.result)
7685
}
77-
backoff := GetBackoffForNextSchedule(tt.cron, start, end)
78-
assert.Equal(t, tt.result, backoff, "The cron spec is %s and the expected result is %s", tt.cron, tt.result)
7986
})
8087
}
8188
}

0 commit comments

Comments
 (0)