Skip to content

[BUG] - CPU usage 100% and next schedule is blocking after the system time is changed #385

Closed
@allanche

Description

Describe the bug

use this code run cron scheduler,CPU usage 100% and next schedule is blocking after the system time is changed.

package main

import (
	"github.com/go-co-op/gocron"
	"log"
	"time"
)

func task() {
	log.Println("hello world.")
}

func main() {
	s := gocron.NewScheduler(time.UTC)
	_, _ = s.Cron("*/1 * * * *").Do(task)
	s.StartBlocking()
}

To Reproduce

  1. current system time:
Mon Aug 29 17:30:22 CST 2022
  1. change system time forward 1 hour:
Mon Aug 29 16:30:51 CST 2022
  1. next schedule has bug.

Version

v1.13.0

Additional context

use pprof make analysis,test code is:

package main

import (
	"github.com/go-co-op/gocron"
	"log"
	"net/http"
	_ "net/http/pprof"
	"time"
)

func task() {
	log.Println("hello world.")
}

func main() {
	go func() {
		http.ListenAndServe("0.0.0.0:6060", nil)
	}()
	s := gocron.NewScheduler(time.UTC)
	_, _ = s.Cron("*/1 * * * *").Do(task)
	s.StartBlocking()
}
  1. change system time forward 1 hour,wait for the next scheduling exception.
  2. use curl command dump profile.
curl http://127.0.0.1:6060/debug/pprof/profile\?seconds\=60 > profile.out
  1. use go tool display profile.
go tool pprof -http=":8082" profile.out
  1. get cpu usage 100% code line is:
0.95s   100% |   github.com/go-co-op/gocron.(*Scheduler).scheduleNextRun.func1 xxx/go/pkg/mod/github.com/go-co-op/gocron@v1.13.0/scheduler.go:204
  1. review gocron scheduler line 204:
	job.setTimer(time.AfterFunc(next.duration, func() {
		if !next.dateTime.IsZero() {
			for {
				if time.Now().Unix() >= next.dateTime.Unix() {
					break
				}
			}
		}
		s.run(job)
		s.scheduleNextRun(job)
	}))

The for loop here can cause CPU usage to reach 100% and block the next schedule.
6. suggest example:
We can set the timer check condition time.Now().Unix() >= next.dateTime.Unix() and set the timeout to jump out for loop,ensure the next scheduling.

job.setTimer(time.AfterFunc(next.duration, func() {
		if !next.dateTime.IsZero() {
			timeout := 30 * time.Second
			interval := 1 * time.Second
			timer := time.NewTimer(timeout)
			ticker := time.NewTicker(interval)
			defer timer.Stop()
			defer ticker.Stop()
			for {
				if time.Now().Unix() >= next.dateTime.Unix() {
					break
				}
				select {
				case <-ticker.C:
					ticker.Reset(interval)
					continue
				case <-timer.C:
					break
				}
				break
			}
		}
		s.run(job)
		s.scheduleNextRun(job)
	}))

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions