Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: the cron job with precise second time pattern might be executed twice in the same time #3437

Merged
merged 2 commits into from
Mar 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion contrib/config/consul/go.mod
@@ -1,6 +1,6 @@
module github.com/gogf/gf/contrib/config/consul/v2

go 1.19
go 1.18

require (
github.com/gogf/gf/v2 v2.6.4
Expand Down
2 changes: 1 addition & 1 deletion contrib/config/kubecm/go.mod
@@ -1,6 +1,6 @@
module github.com/gogf/gf/contrib/config/kubecm/v2

go 1.19
go 1.18

require (
github.com/gogf/gf/v2 v2.6.4
Expand Down
2 changes: 1 addition & 1 deletion contrib/metric/otelmetric/go.mod
@@ -1,6 +1,6 @@
module github.com/gogf/gf/contrib/metric/otelmetric/v2

go 1.20
go 1.18

require (
github.com/gogf/gf/v2 v2.6.1
Expand Down
2 changes: 1 addition & 1 deletion contrib/trace/otlpgrpc/go.mod
@@ -1,6 +1,6 @@
module github.com/gogf/gf/contrib/trace/otlpgrpc/v2

go 1.20
go 1.18

require (
github.com/gogf/gf/v2 v2.6.1
Expand Down
2 changes: 1 addition & 1 deletion contrib/trace/otlphttp/go.mod
@@ -1,6 +1,6 @@
module github.com/gogf/gf/contrib/trace/otlphttp/v2

go 1.20
go 1.18

require (
github.com/gogf/gf/v2 v2.6.1
Expand Down
9 changes: 7 additions & 2 deletions os/gcron/gcron_cron.go
Expand Up @@ -46,7 +46,12 @@ func (c *Cron) GetLogger() glog.ILogger {

// AddEntry creates and returns a new Entry object.
func (c *Cron) AddEntry(
ctx context.Context, pattern string, job JobFunc, times int, isSingleton bool, name ...string,
ctx context.Context,
pattern string,
job JobFunc,
times int,
isSingleton bool,
name ...string,
) (*Entry, error) {
var (
entryName = ""
Expand Down Expand Up @@ -204,7 +209,7 @@ func (c *Cron) Entries() []*Entry {
array := garray.NewSortedArraySize(c.entries.Size(), func(v1, v2 interface{}) int {
entry1 := v1.(*Entry)
entry2 := v2.(*Entry)
if entry1.Time.Nanosecond() > entry2.Time.Nanosecond() {
if entry1.RegisterTime.Nanosecond() > entry2.RegisterTime.Nanosecond() {
return 1
}
return -1
Expand Down
32 changes: 16 additions & 16 deletions os/gcron/gcron_entry.go
Expand Up @@ -26,15 +26,15 @@ type JobFunc = gtimer.JobFunc

// Entry is timing task entry.
type Entry struct {
cron *Cron // Cron object belonged to.
timerEntry *gtimer.Entry // Associated timer Entry.
schedule *cronSchedule // Timed schedule object.
jobName string // Callback function name(address info).
times *gtype.Int // Running times limit.
infinite *gtype.Bool // No times limit.
Name string // Entry name.
Job JobFunc `json:"-"` // Callback function.
Time time.Time // Registered time.
cron *Cron // Cron object belonged to.
timerEntry *gtimer.Entry // Associated timer Entry.
schedule *cronSchedule // Timed schedule object.
jobName string // Callback function name(address info).
times *gtype.Int // Running times limit.
infinite *gtype.Bool // No times limit.
Name string // Entry name.
RegisterTime time.Time // Registered time.
Job JobFunc `json:"-"` // Callback function.
}

type doAddEntryInput struct {
Expand Down Expand Up @@ -64,13 +64,13 @@ func (c *Cron) doAddEntry(in doAddEntryInput) (*Entry, error) {
}
// No limit for `times`, for timer checking scheduling every second.
entry := &Entry{
cron: c,
schedule: schedule,
jobName: runtime.FuncForPC(reflect.ValueOf(in.Job).Pointer()).Name(),
times: gtype.NewInt(in.Times),
infinite: gtype.NewBool(in.Infinite),
Job: in.Job,
Time: time.Now(),
cron: c,
schedule: schedule,
jobName: runtime.FuncForPC(reflect.ValueOf(in.Job).Pointer()).Name(),
times: gtype.NewInt(in.Times),
infinite: gtype.NewBool(in.Infinite),
RegisterTime: time.Now(),
Job: in.Job,
}
if in.Name != "" {
entry.Name = in.Name
Expand Down
5 changes: 5 additions & 0 deletions os/gcron/gcron_schedule_check.go
Expand Up @@ -70,6 +70,11 @@ func (s *cronSchedule) checkMeetSecond(lastMeetTime, currentTime time.Time) (ok
return false
}
} else {
// If this pattern is set in precise second time,
// it is not allowed executed in the same time.
if len(s.secondMap) == 1 && lastMeetTime.Format(time.RFC3339) == currentTime.Format(time.RFC3339) {
return false
}
if !s.keyMatch(s.secondMap, currentTime.Second()) {
return false
}
Expand Down
14 changes: 7 additions & 7 deletions os/gcron/gcron_schedule_fix.go
Expand Up @@ -20,26 +20,26 @@ func (s *cronSchedule) getAndUpdateLastCheckTimestamp(ctx context.Context, t tim
lastCheckTimestamp = s.lastCheckTimestamp.Val()
)
switch {
// Often happens, timer triggers in the same second.
// Often happens, timer triggers in the same second, but the millisecond is different.
// Example:
// lastCheckTimestamp: 10
// currentTimestamp: 10
// lastCheckTimestamp: 2024-03-26 19:47:34.000
// currentTimestamp: 2024-03-26 19:47:34.999
case
lastCheckTimestamp == currentTimestamp:
lastCheckTimestamp += 1

// Often happens, no latency.
// Example:
// lastCheckTimestamp: 9
// currentTimestamp: 10
// lastCheckTimestamp: 2024-03-26 19:47:34.000
// currentTimestamp: 2024-03-26 19:47:35.000
case
lastCheckTimestamp == currentTimestamp-1:
lastCheckTimestamp = currentTimestamp

// Latency in 3 seconds, which can be tolerant.
// Example:
// lastCheckTimestamp: 7/8
// currentTimestamp: 10
// lastCheckTimestamp: 2024-03-26 19:47:31.000、2024-03-26 19:47:32.000
// currentTimestamp: 2024-03-26 19:47:34.000
case
lastCheckTimestamp == currentTimestamp-2,
lastCheckTimestamp == currentTimestamp-3:
Expand Down