Simple, zero-dependency scheduling library for Go

go-quartz

Build Status GoDoc Go Report Card codecov

Simple, zero-dependency scheduling library for Go.

About

Inspired by the Quartz Java scheduler.

Library building blocks

Job interface. Any type that implements it can be scheduled.

type Job interface {
	Execute()
	Description() string
	Key() int
}

Implemented Jobs

  • ShellJob
  • CurlJob

Scheduler interface

type Scheduler interface {
	// start the scheduler
	Start()
	// schedule the job with the specified trigger
	ScheduleJob(job Job, trigger Trigger) error
	// get all scheduled jobs keys
	GetJobKeys() []int
	// get the scheduled job metadata
	GetScheduledJob(key int) (*ScheduledJob, error)
	// remove the job from the execution queue
	DeleteJob(key int) error
	// clear all scheduled jobs
	Clear()
	// shutdown the scheduler
	Stop()
}

Implemented Schedulers

  • StdScheduler

Trigger interface

type Trigger interface {
	NextFireTime(prev int64) (int64, error)
	Description() string
}

Implemented Triggers

  • CronTrigger
  • SimpleTrigger
  • RunOnceTrigger

Examples

sched := quartz.NewStdScheduler()
sched.Start()
cronTrigger, _ := quartz.NewCronTrigger("1/5 * * * * *")
shellJob := quartz.NewShellJob("ls -la")
curlJob, _ := quartz.NewCurlJob(http.MethodGet, "http://worldclockapi.com/api/json/est/now", "", nil)
sched.ScheduleJob(shellJob, cronTrigger)
sched.ScheduleJob(curlJob, quartz.NewSimpleTrigger(time.Second*7))
sched.Stop()

More code samples can be found in the examples directory.

License

Licensed under the MIT License.

Comments
  • Some of the cron expressions are not supported

    Some of the cron expressions are not supported

    All cron expressions not supported

    Example

    package main
    
    import (
    	"fmt"
    	"strconv"
    	"time"
    
    	"github.com/reugn/go-quartz/quartz"
    )
    
    
    type ScheduledJob struct {
    	jobid  int
    	config string
    }
    
    
    func NewScheduledJob(jobid int, config string) *ScheduledJob {
    	var job ScheduledJob
    	job.jobid = jobid
    	job.config = config
    	return &job
    }
    
    func (sh *ScheduledJob) Description() string {
    	return sh.config
    }
    
    func (sh *ScheduledJob) Key() int {
    	return sh.jobid
    }
    
    func (sh *ScheduledJob) Execute() {
    	fmt.Printf("Job id %s Config %s", strconv.Itoa(sh.jobid), sh.config)
    	time.Sleep(2 * time.Second)
    }
    
    func main() {
    	sched := quartz.NewStdScheduler()
    	sched.Start()
    
    	cronTrigger, err := quartz.NewCronTrigger("0 12 */2 * * ? *")
    
    	if err != nil {
    		fmt.Println(err)
    		return
    	}
    	job := NewScheduledJob(10, "My config")
    	sched.ScheduleJob(job, cronTrigger)
    
    	<-make(chan int)
    }
    

    Error

    Invalid cron expression: Cron step min/max validation error

    Above cron expression means every 2 hours

    Additional cron expressions that are fail

    Every 3 hours : 0 24 */3 * * ? *
    Every 4 hours : 0 43 */5 * * ? *
    Every 6 hours : 0 54 */6 * * ? * 
    Every 12 hours : 0 51 */12 * * ? *
    Every 2 days 3rd hour: 0 0 3 */2 * ? *
    Every 6 days 10th hour: 0 0 10 */6 * ? *
    Every Month 2nd Monday 6th Hour: 0 0 6 ? * 2#2 *
    Every Month 4th Tuesday 15th hour: 0 0 15 ? * 3#4 *
    

    etc

  • Scheduler does not schedule jobs without giving any error

    Scheduler does not schedule jobs without giving any error

    I am scheduling 4 jobs. 2 Jobs every 1 hour. (Job names: PLUGIN_DISCOVERY_JOB__18998__DISCOVERY__ and PLUGIN_DISCOVERY_JOB__19003__DISCOVERY__) 1 Job every 6 hours. (Job name: Sync_defs) 1 Job every 5 minutes. (Job name: MON_JOB__39f6f2a3-e0db-4036-90fe-74b67d1af4af__PING__5)

    Following are the logs.

    
    2021-09-29T23:45:00.022Z	INFO	portedapp/ported-app-processor.go:36	Received Ported App Scheduling request..!!
    2021-09-29T23:45:00.029Z	INFO	portedapp/ported-app-processor.go:66	Successfully scheduled Job..!! 
    2021-09-29T23:45:00.029Z	INFO	portedapp/ported-app-processor.go:67	Job Name : MON_JOB__39f6f2a3-e0db-4036-90fe-74b67d1af4af__PING__5, Target : ping-adapter
    2021-09-29T23:50:00.000Z	INFO	portedapp/ported-app-processor.go:36	Received Ported App Scheduling request..!!
    2021-09-29T23:50:00.007Z	INFO	portedapp/ported-app-processor.go:66	Successfully scheduled Job..!! 
    2021-09-29T23:50:00.007Z	INFO	portedapp/ported-app-processor.go:67	Job Name : MON_JOB__39f6f2a3-e0db-4036-90fe-74b67d1af4af__PING__5, Target : ping-adapter
    2021-09-29T23:55:00.020Z	INFO	portedapp/ported-app-processor.go:36	Received Ported App Scheduling request..!!
    2021-09-29T23:55:00.026Z	INFO	portedapp/ported-app-processor.go:66	Successfully scheduled Job..!! 
    2021-09-29T23:55:00.027Z	INFO	portedapp/ported-app-processor.go:67	Job Name : MON_JOB__39f6f2a3-e0db-4036-90fe-74b67d1af4af__PING__5, Target : ping-adapter
    2021-09-30T00:00:00.000Z	INFO	portedapp/ported-app-processor.go:36	Received Ported App Scheduling request..!!
    2021-09-30T00:00:00.008Z	INFO	portedapp/ported-app-processor.go:66	Successfully scheduled Job..!! 
    2021-09-30T00:00:00.008Z	INFO	portedapp/ported-app-processor.go:67	Job Name : Sync_defs, Target : snmp-adapter
    2021-09-30T00:06:00.023Z	INFO	portedapp/ported-app-processor.go:36	Received Ported App Scheduling request..!!
    2021-09-30T00:06:00.029Z	INFO	portedapp/ported-app-processor.go:66	Successfully scheduled Job..!! 
    2021-09-30T00:06:00.029Z	INFO	portedapp/ported-app-processor.go:67	Job Name : PLUGIN_DISCOVERY_JOB__18998__DISCOVERY__, Target : snmp-adapter
    2021-09-30T00:38:00.000Z	INFO	portedapp/ported-app-processor.go:36	Received Ported App Scheduling request..!!
    2021-09-30T00:38:00.006Z	INFO	portedapp/ported-app-processor.go:66	Successfully scheduled Job..!! 
    2021-09-30T00:38:00.007Z	INFO	portedapp/ported-app-processor.go:67	Job Name : PLUGIN_DISCOVERY_JOB__19003__DISCOVERY__, Target : snmp-adapter
    2021-09-30T01:00:00.001Z	INFO	portedapp/ported-app-processor.go:36	Received Ported App Scheduling request..!!
    2021-09-30T01:00:00.008Z	INFO	portedapp/ported-app-processor.go:66	Successfully scheduled Job..!! 
    2021-09-30T01:00:00.008Z	INFO	portedapp/ported-app-processor.go:67	Job Name : MON_JOB__39f6f2a3-e0db-4036-90fe-74b67d1af4af__PING__5, Target : ping-adapter
    2021-09-30T01:05:00.024Z	INFO	portedapp/ported-app-processor.go:36	Received Ported App Scheduling request..!!
    2021-09-30T01:05:00.031Z	INFO	portedapp/ported-app-processor.go:66	Successfully scheduled Job..!! 
    2021-09-30T01:05:00.031Z	INFO	portedapp/ported-app-processor.go:67	Job Name : MON_JOB__39f6f2a3-e0db-4036-90fe-74b67d1af4af__PING__5, Target : ping-adapter
    2021-09-30T01:06:00.024Z	INFO	portedapp/ported-app-processor.go:36	Received Ported App Scheduling request..!!
    2021-09-30T01:06:00.029Z	INFO	portedapp/ported-app-processor.go:66	Successfully scheduled Job..!! 
    2021-09-30T01:06:00.029Z	INFO	portedapp/ported-app-processor.go:67	Job Name : PLUGIN_DISCOVERY_JOB__18998__DISCOVERY__, Target : snmp-adapter
    2021-09-30T01:10:00.024Z	INFO	portedapp/ported-app-processor.go:36	Received Ported App Scheduling request..!!
    2021-09-30T01:10:00.030Z	INFO	portedapp/ported-app-processor.go:66	Successfully scheduled Job..!! 
    2021-09-30T01:10:00.030Z	INFO	portedapp/ported-app-processor.go:67	Job Name : MON_JOB__39f6f2a3-e0db-4036-90fe-74b67d1af4af__PING__5, Target : ping-adapter
    

    Code snippet

    type ScheduledJob struct {
    	JobName string
    	Config  string
    }
    
    func (sh *ScheduledJob) Description() string {
    	return sh.JobName
    }
    
    func (sh *ScheduledJob) Key() int {
    	return quartz.HashCode(sh.JobName)
    }
    
    func (sh *ScheduledJob) Execute() {
    	logger.SugarLogger.Info("Received Ported App Scheduling request..!!")
    	var request model.GenericRequest
    	err := json.Unmarshal([]byte(sh.Config), &request)
    	if err != nil {
    		logger.SugarLogger.Error("Error while reading generic request message. Error: ", err)
    	}
    	metaData := request.MetaData
    	metaData.Source = "Scheduler"
    
    	jobConfig := request.JobConfiguration
    
    	requestJson, err := json.Marshal(request)
    	if err != nil {
    		logger.SugarLogger.Error("Error while reading metadata message. Error: ", err)
    	}
    
    	// publish request json to app
    	target := metaData.Target
    	publishUrl := httpclient.GetPublishUrl(target)
    	_, err = httpclient.PostMessage(string(requestJson), publishUrl)
    	if err != nil {
    		logger.SugarLogger.Error("Error while publish message to app. App Name : ", target, ", Error:", err)
    	}
    
    	logger.SugarLogger.Info("Successfully scheduled Job..!! ")
    	logger.SugarLogger.Info("Job Name : ", jobConfig.JobName, ", Target : ", target)
    }
    

    Version: v0.3.6

    In the above logs From 5 minute job not scheduled job from 2021-09-29T23:55:00.027Z to 2021-09-30T01:00:00.001Z Here 1 hour 5 minutes not scheduled any jobs.

    Observation : Here request is not coming to Execute() method It seems there might be issue in NextTriggerTime calculation. If you observer trigger times 23:55 and it is resumed back at 1:00.

  • feat: add Scheduler options

    feat: add Scheduler options

    This is just an example, to see if you all would be interested in adopting a feature like this. If this is viable, I would want to write tests and better documentation.

    Thanks for your time and consideration!

  • Persistent Job store

    Persistent Job store

    Is there a storage layer for this ?

    I was thinking of adding a JSON based storage and some sort of web based gui to configure that json.

    essentially it’s really a config .

    then there is the question of if the config is changed via the Jain , how we update the running program without restarting .

    what do you think ?

    Do you have other plans for this area perhaps ?

  • feat: add contexts to interfaces

    feat: add contexts to interfaces

    I still want to do some work to add test coverage of this, and potentially exercise the new signal channel, but I'm pretty confident in the general approach, and wanted to get some early feedback.

    Addresses: #40

  • add support for contexts in scheduler

    add support for contexts in scheduler

    It would be nice to be able to have a context that is canceled when the job finishes executing to make it difficult/impossible for jobs to spawn go routines that last longer than the job execution itself. This change would also allow quartz schedulers to integrate with existing process/task lifescale mechanisms that use contexts.

    I've prototyped this in this branch: https://github.com/tychoish/go-quartz/tree/dev-contexts.

    If this is something that you/the project is interested in, I would be willing to carry this over the line.

  • add support for generic FunctionJob

    add support for generic FunctionJob

    Expand the suite of Jobs to include plain Golang functions, including their closures.

    You can create a FunctionJob like so:

    n = 0
    job := quartz.NewFunctionJob(func() (int, error) {
        n = n + 1
        return n, nil
    }
    // later on access results
    println(job.JobStatus, job.Result)
    

    The other Jobs either shell out or hit endpoint with cron. It is also useful to invoke Go functions. For example, various periodic cleanup tasks for a service.

    This simple,. type-safe implementation requires generic support in Go, available in Go 1.18. Note that the library user doesn't need to deal with generics and types at all.

  • Day of week bug in CronTrigger.NextFireTime

    Day of week bug in CronTrigger.NextFireTime

    CronTrigger.NextFireTime produces wrong fire time when day of week in cron expression is smaller than current day of week.

    Example:

    package main
    
    import (
    	"fmt"
    	"github.com/reugn/go-quartz/quartz"
    	"time"
    )
    
    func main() {
    	const dateLayout = "Mon Jan 2 15:04:05 2006"
    	var currTime int64 = 1615363744528064087 // time.Now().UnixNano()
    	fmt.Printf("Current time: %s\n\n", time.Unix(currTime/int64(time.Second), 0).UTC().Format(dateLayout))
    	for i, dayOfWeek := range []string{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"} {
    		for _, dayOfWeekFormat := range []string{fmt.Sprint(i), dayOfWeek} {
    			cronExpr := fmt.Sprintf("0 0 0 * * %s", dayOfWeekFormat)
    			fmt.Println("Cron expression:", cronExpr)
    			cronTrigger, err := quartz.NewCronTrigger(cronExpr)
    			if err != nil {
    				panic(err)
    			}
    			nextFireTime, err := cronTrigger.NextFireTime(currTime)
    			if err != nil {
    				panic(err)
    			}
    			fmt.Printf("Next fire time: %s\n\n", time.Unix(nextFireTime/int64(time.Second), 0).UTC().Format(dateLayout))
    		}
    	}
    }
    

    prints:

    Current time: Wed Mar 10 08:09:04 2021
    
    Cron expression: 0 0 0 * * 0
    Next fire time: Wed Mar 17 00:00:00 2021
    
    Cron expression: 0 0 0 * * Sun
    Next fire time: Wed Mar 17 00:00:00 2021
    
    Cron expression: 0 0 0 * * 1
    Next fire time: Wed Mar 17 00:00:00 2021
    
    Cron expression: 0 0 0 * * Mon
    Next fire time: Wed Mar 17 00:00:00 2021
    
    Cron expression: 0 0 0 * * 2
    Next fire time: Wed Mar 17 00:00:00 2021
    
    Cron expression: 0 0 0 * * Tue
    Next fire time: Wed Mar 17 00:00:00 2021
    
    Cron expression: 0 0 0 * * 3
    Next fire time: Wed Mar 17 00:00:00 2021
    
    Cron expression: 0 0 0 * * Wed
    Next fire time: Wed Mar 17 00:00:00 2021
    
    Cron expression: 0 0 0 * * 4
    Next fire time: Thu Mar 11 00:00:00 2021
    
    Cron expression: 0 0 0 * * Thu
    Next fire time: Thu Mar 11 00:00:00 2021
    
    Cron expression: 0 0 0 * * 5
    Next fire time: Fri Mar 12 00:00:00 2021
    
    Cron expression: 0 0 0 * * Fri
    Next fire time: Fri Mar 12 00:00:00 2021
    
    Cron expression: 0 0 0 * * 6
    Next fire time: Sat Mar 13 00:00:00 2021
    
    Cron expression: 0 0 0 * * Sat
    Next fire time: Sat Mar 13 00:00:00 2021
    
  • Trigger start time functionality suggestion

    Trigger start time functionality suggestion

    The start time can be set in other packages (java, .net). We should be able to do that with this package as well.

    Edit: I guess you can do it with NextFireTime. It didn't seem very understandable to me.

  • CronField and CronExpressionParser should be not be exported

    CronField and CronExpressionParser should be not be exported

    CronField andCronExpressionParser seem to be private data structures for the implementation of CronTrigger. I don't see a reason why they are exported.

  • CronTrigger use only UTC

    CronTrigger use only UTC

            cronTime := "0 30 21 * * ?"
    	ct, err := quartz.NewCronTrigger(cronTime)
    	if err != nil {
    		return err
    	}
    
    	currentTime := time.Now().UnixNano()
    	fmt.Println("CurrentTime:", time.Unix(currentTime/int64(time.Second), 0).String())
    
    	nft, err := ct.NextFireTime(currentTime)
    	if err != nil {
    		return err
    	}
    
             fmt.Println("NextFireTime:", time.Unix(nft/int64(time.Second), 0).String())
    

    Result:

    CurrentTime: 2022-03-23 09:09:25 +0300 MSK
    NextFireTime: 2022-03-24 00:30:00 +0300 MSK
    

    Looks like cron trigger set job time in UTC. Is there some option to set job in local time?

  • scheduler: wait for all threads

    scheduler: wait for all threads

    I was working on an integration with this and I realized that as implemented Wait() doesn't wait for very much to be done, this improves that situation.

  • job: prevent more than once instance of a job from running at once

    job: prevent more than once instance of a job from running at once

    This may be a reasonable alternative to the "blocking" execution options, but may resolve some of the concurrent job running semantic issues. Basically a single instance of a job can be made so that only one instance is running at a time (as if it were locked.)

  • NextFireTime question

    NextFireTime question

    var ( loc, _ = time.LoadLocation("Asia/Shanghai") ) cTrigger, _ := quartz.NewCronTriggerWithLoc("0 * * ? * 4", loc) nextTime, _ := cTrigger.NextFireTime(time.Now().UnixNano())

    Today is 2022-11-09 17:05:00, and the next execution time is 2022-11-16 17:05:00, shouldn't it be 2022-11-09 17:06:00?

  • some questions about NextFireTime

    some questions about NextFireTime

    I use the test code:

    now := time.Now()

    fmt.Println(utils.TimeFormat(now))

    loc, _ := time.LoadLocation("Asia/Shanghai")

    ctrigger1, _ := quartz.NewCronTriggerWithLoc("0 * * ? * *", loc)

    ne1, _ := ctrigger1.NextFireTime(now.UnixNano())

    fmt.Println(utils.TimeFormat(time.Unix(ne1/int64(time.Second), 0)))

    ctrigger2, _ := quartz.NewCronTriggerWithLoc("0 * * ? * MON", loc)

    ne2, _ := ctrigger2.NextFireTime(now.UnixNano())

    fmt.Println(utils.TimeFormat(time.Unix(ne2/int64(time.Second), 0)))

    ctrigger3, _ := quartz.NewCronTriggerWithLoc("0 * * ? * 1", loc)

    ne3, _ := ctrigger3.NextFireTime(now.UnixNano())

    fmt.Println(utils.TimeFormat(time.Unix(ne3/int64(time.Second), 0)))

    then I got:

    2022-06-27 18:32:23 2022-06-27 18:33:00 2022-07-04 18:32:00 2022-07-03 18:32:00

    the third and the fourth output are not as I expected, is this correct?

  • api: Allocate JobId instead of using hashcodes as job identifiers

    api: Allocate JobId instead of using hashcodes as job identifiers

    Suggestions for API change:

    • define a type JobId
    • allocate JobId when ScheduleJob is called and return the JobId
    • change GetJobKeys/DeleteJob/GetScheduledJob to use JobId
    • remove method Key from Job interface
high performance distributed task scheduling system, Support multi protocol scheduling tasks
 high performance distributed task scheduling system, Support multi protocol scheduling tasks

high performance distributed task scheduling system, Support multi protocol scheduling tasks

Dec 2, 2022
A zero-dependencies and lightweight go library for job scheduling

A zero-dependencies and lightweight go library for job scheduling.

Aug 3, 2022
clockwork - Simple and intuitive job scheduling library in Go.
clockwork - Simple and intuitive job scheduling library in Go.

clockwork A simple and intuitive scheduling library in Go. Inspired by python's schedule and ruby's clockwork libraries. Example use package main imp

Jul 27, 2022
Easy and fluent Go cron scheduling

goCron: A Golang Job Scheduling Package. goCron is a Golang job scheduling package which lets you run Go functions periodically at pre-determined inte

Jan 8, 2023
Job scheduling made easy.

scheduler Job scheduling made easy. Scheduler allows you to schedule recurrent jobs with an easy-to-read syntax. Inspired by the article Rethinking Cr

Dec 30, 2022
goCron: A Golang Job Scheduling Package.

goCron: A Golang Job Scheduling Package.

Jan 9, 2023
Distributed Task Scheduling System|分布式定时任务调度平台
Distributed Task Scheduling System|分布式定时任务调度平台

Crocodile Distributed Task Scheduling System English | 中文 Introduction A distributed task scheduling system based on Golang that supports http request

Jan 5, 2023
nano-gpu-scheduler is a Kubernetes scheduler extender for GPU resources scheduling.
nano-gpu-scheduler is a Kubernetes scheduler extender for GPU resources scheduling.

Nano GPU Scheduler About This Project With the continuous evolution of cloud native AI scenarios, more and more users run AI tasks on Kubernetes, whic

Dec 29, 2022
K8s cluster simulator for workload scheduling.
K8s cluster simulator for workload scheduling.

Open-Simulator Motivation 概念定义 Open-Simulator 是 K8s 下的仿真调度组件。用户准备一批待创建 Workload 资源,Workload 资源指定好资源配额、绑核规则、亲和性规则、优先级等,通过 Open-Simulator 的仿真调度能力可判断当前集群

Dec 25, 2022
A way of scheduling volcano jobs

JobFlow 背景 volcano Volcano是CNCF 下首个也是唯一的基于Kubernetes的容器批量计算平台,主要用于高性能计算场景。 它提供了Kubernetes目前缺 少的一套机制,这些机制通常是机器学习大数据应用、科学计算、 特效渲染等多种高性能工作负载所需的。 现状:当前vol

Oct 12, 2022
Lightweight, fast and dependency-free Cron expression parser (due checker) for Golang (tested on v1.13 and above)

adhocore/gronx gronx is Golang cron expression parser ported from adhocore/cron-expr. Zero dependency. Very fast because it bails early in case a segm

Dec 30, 2022
Marshmallow provides a flexible and performant JSON unmarshalling in Go. It specializes in dealing with unstructured struct - when some fields are known and some aren't, with zero performance overhead nor extra coding needed.
Marshmallow provides a flexible and performant JSON unmarshalling in Go. It specializes in dealing with unstructured struct - when some fields are known and some aren't, with zero performance overhead nor extra coding needed.

Marshmallow Marshmallow package provides a simple API to perform flexible and performant JSON unmarshalling in Go. Marshmallow specializes in dealing

Dec 26, 2022
A simple Cron library for go that can execute closures or functions at varying intervals, from once a second to once a year on a specific date and time. Primarily for web applications and long running daemons.

Cron.go This is a simple library to handle scheduled tasks. Tasks can be run in a minimum delay of once a second--for which Cron isn't actually design

Dec 17, 2022
A persistent and flexible background jobs library for go.

Jobs Development Status Jobs is no longer being actively developed. I will still try my best to respond to issues and pull requests, but in general yo

Nov 21, 2022
a cron library for go

cron Cron V3 has been released! To download the specific tagged release, run: go get github.com/robfig/cron/[email protected] Import it in your program as: im

Dec 25, 2022
Chrono is a scheduler library that lets you run your task and code periodically
Chrono is a scheduler library that lets you run your task and code periodically

Chrono is a scheduler library that lets you run your tasks and code periodically. It provides different scheduling functionalities to make it easier t

Dec 26, 2022
go-sche is a golang library that lets you schedule your task to be executed later.

go-sche is a golang library that lets you schedule your task to be executed later.

Dec 24, 2022
Tiny library to handle background jobs.

bgjob Tiny library to handle background jobs. Use PostgreSQL to organize job queues. Highly inspired by gue Features Durable job storage At-least-ones

Nov 16, 2021
Task Timer (tt) is a dead simple TUI task timer
Task Timer (tt) is a dead simple TUI task timer

tasktimer Task Timer (tt) is a dead simple TUI task timer Usage To get started, just run tt: tt You'll be presented with something like this: You can

Dec 21, 2022