Chrono is a scheduler library that lets you run your task and code periodically

chrono

Go Report Card Build Status codecov

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

Scheduling a One-Shot Task

The Schedule method helps us schedule the task to run once at the specified time. In the following example, the task will first be executed 1 second after the current time.  WithStartTime option is used to specify the execution time.

taskScheduler := chrono.NewDefaultTaskScheduler()

task, err := taskScheduler.Schedule(func(ctx context.Context) {
	log.Print("One-Shot Task")
}, WithStartTime(now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second()+1))

if err == nil {
	log.Print("Task has been scheduled successfully.")
}

Scheduling a Task with Fixed Delay

Let's schedule a task to run with a fixed delay between the finish time of the last execution of the task and the start time of the next execution of the task. The fixed delay counts the delay after the completion of the last execution.

taskScheduler := chrono.NewDefaultTaskScheduler()

task, err := taskScheduler.ScheduleWithFixedDelay(func(ctx context.Context) {
	log.Print("Fixed Delay Task")
	time.Sleep(3 * time.Second)
}, 5 * time.Second)

if err == nil {
	log.Print("Task has been scheduled successfully.")
}

Since the task itself takes 3 seconds to complete and we have specified a delay of 5 seconds between the finish time of the last execution of the task and the start time of the next execution of the task, there will be a delay of 8 seconds between each execution.

WithStartTime and WithLocation options can be combined with this.

Schedule Task at a Fixed Rate

Let's schedule a task to run at a fixed rate of seconds.

taskScheduler := chrono.NewDefaultTaskScheduler()

task, err := taskScheduler.ScheduleAtFixedRate(func(ctx context.Context) {
	log.Print("Fixed Rate of 5 seconds")
}, 5 * time.Second)

if err == nil {
	log.Print("Task has been scheduled successfully.")
}

The next task will run always after 5 seconds no matter the status of the previous task, which may be still running. So even if the previous task isn't done, the next task will run. We can also use the WithStartTime option to specify the desired first execution time of the task.

now := time.Now()

task, err := taskScheduler.ScheduleAtFixedRate(func(ctx context.Context) {
	log.Print("Fixed Rate of 5 seconds")
}, 5 * time.Second, WithStartTime(now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second() + 2))

When we use this option, the task will run at the specified execution time and subsequently with the given period. In the above example, the task will first be executed 2 seconds after the current time.

We can also combine this option with WithLocation based on our requirements.

now := time.Now()

task, err := taskScheduler.ScheduleAtFixedRate(func(ctx context.Context) {
	log.Print("Fixed Rate of 5 seconds")
}, 5 * time.Second, WithStartTime(now.Year(), now.Month(), now.Day(), 18, 45, 0),
WithLocation("America/New_York"))

In the above example, the task will first be executed at 18:45 of the current date in America/New York time. If the start time is in the past, the task will be executed immediately.

Scheduling a Task using Cron Expression

Sometimes Fixed Rate and Fixed Delay can not fulfill your needs, and we need the flexibility of cron expressions to schedule the execution of your tasks. With the help of the provided ScheduleWithCron method, we can schedule a task based on a cron expression.

taskScheduler := chrono.NewDefaultTaskScheduler()

task, err := taskScheduler.ScheduleWithCron(func(ctx context.Context) {
	log.Print("Scheduled Task With Cron")
}, "0 45 18 10 * *")

if err == nil {
	log.Print("Task has been scheduled")
}

In this case, we're scheduling a task to be executed at 18:45 on the 10th day of every month

By default, the local time is used for the cron expression. However, we can use the WithLocation option to change this.

task, err := taskScheduler.ScheduleWithCron(func(ctx context.Context) {
	log.Print("Scheduled Task With Cron")
}, "0 45 18 10 * *", WithLocation("America/New_York"))

In the above example, Task will be scheduled to be executed at 18:45 on the 10th day of every month in America/New York time.

WithStartTimeoption cannot be used with ScheduleWithCron.

Canceling a Scheduled Task

Schedule methods return an instance of type ScheduledTask, which allows us to cancel a task or to check if the task is canceled. The Cancel method cancels the scheduled task but running tasks won't be interrupted.

taskScheduler := chrono.NewDefaultTaskScheduler()

task, err := taskScheduler.ScheduleAtFixedRate(func(ctx context.Context) {
	log.Print("Fixed Rate of 5 seconds")
}, 5 * time.Second)

/* ... */
	
task.Cancel()

Shutting Down a Scheduler

The Shutdown() method doesn't cause immediate shut down of the Scheduler and returns a channel. It will make the Scheduler stop accepting new tasks and shut down after all running tasks finish their current work.

taskScheduler := chrono.NewDefaultTaskScheduler()

/* ... */

shutdownChannel := taskScheduler.Shutdown()
<- shutdownChannel
	
/* after all running task finished their works */

License

Chrono is released under MIT License.

Owner
Procyon
Procyon is a powerful web framework written in Go
Procyon
Comments
  • CronExpression not working inside docker

    CronExpression not working inside docker

    CronExpression can be scheduled but never gets executed.

    method: ScheduleWithCron(..., ..., chrono.WithLocation("Europe/Berlin")) expression: does not matter, also */1 * * * * not working docker-file to build and run:

    # The base go-image
    FROM golang:1.18-alpine as build
    # Create a directory for the app
    RUN mkdir /app
     # Copy all files from the current directory to the app directory
    COPY ./src /app
     # Set working directory
    WORKDIR /app
    # go build will build an executable file named tics in the current directory
    RUN go build -o tics
    FROM alpine:latest AS bin
    # copy from temporary "build"-image to the current
    copy --from=build /app/tics /tics
    copy ./tics.yml /tics.yml
    EXPOSE 3222
    RUN /bin/sh
    # Run the tics executable
    ENTRYPOINT ./tics
    

    running the same source outside docker as an executable works.

    do you have any ideas?

  • *ATTENTION* `ScheduleWithCron` don't work as expected

    *ATTENTION* `ScheduleWithCron` don't work as expected

    The code I use:

    task, err = ts.ScheduleWithCron(func(ctx context.Context) {
    	fmt.Print("Scheduled Task With Cron\n")
    	ierrors.Println(logTag, "Scheduled Task With Cron\n")
    }, "* * * * * *", chrono.WithLocation("Etc/UTC"))
    

    I would expect that the cronjob here will run every second, but in real it runs every "tick"

    ....
    
    Scheduled Task With Cron
    2021-12-10T21:48:15.307977068Z (Fight): Scheduled Task With Cron
    
    Scheduled Task With Cron
    2021-12-10T21:48:15.308020282Z (Fight): Scheduled Task With Cron
    
    Scheduled Task With Cron
    2021-12-10T21:48:15.30807167Z (Fight): Scheduled Task With Cron
    
    Scheduled Task With Cron
    2021-12-10T21:48:15.308107834Z (Fight): Scheduled Task With Cron
    ....
    

    also its not working to use styles like:

    */5 * * * * *
    0 0-59/5 * * * * 
    0 */5 * * * * 
    0 1,6,11,16,21,26,31,36,41,46,51,56 * * * *
    ....
    

    this will result also in the run in "loop" or also like in while true loops.

    This will result in a very very bad DoS because the CPU load goes into the sky.

  • `taskScheduler.Schedule` don't respect the `WithStartTime`

    `taskScheduler.Schedule` don't respect the `WithStartTime`

    I tested this Lib to create Timebased Jobs, from our MySQL Entries.

    But what I see is that each Job I put to the Schedule, is running right after i add it.

    task, err = ts.Schedule(func(ctx context.Context) {
    	ierrors.Println(logTag, "FightLoad Scheduled Task")
    	ierrors.Printf(logTag, "Time: %s\n", time.Now().UTC().String())
    	FightScript(fleet) // Print also the current Time same as the abdove
    }, chrono.WithStartTime(
    	fleet.Timein.Year(),
    	fleet.Timein.Month(),
    	fleet.Timein.Day(),
    	fleet.Timein.Hour(),
    	fleet.Timein.Minute(),
    	fleet.Timein.Second(),
    ), chrono.WithLocation(time.UTC.String()),
    )
    

    What I see is the Job is running right after the schedule, but the fleet.Timein is 10 minutes in the future.

    EDIT: If we remove chrono.WithLocation(time.UTC.String()) and use fleet.Timein.Local().*** it works as expected. Maybe this should be better documentated.

  • Tests not working correct

    Tests not working correct

    The tests, showing some problems:

    Running tool: /usr/local/go/bin/go test -timeout 1m30s -run ^TestCronExpression_NextTime$ **/chrono -v
    
    === RUN   TestCronExpression_NextTime
        **/chrono/cron_test.go:634: got: 2021-03-16 15:10:18 +0100 CET expected: 2021-03-16 15:04:17 +0000 UTC
        **/chrono/cron_test.go:634: got: 2021-03-16 15:10:21 +0100 CET expected: 2021-03-16 15:04:20 +0000 UTC
        **/chrono/cron_test.go:634: got: 2021-03-16 15:10:24 +0100 CET expected: 2021-03-16 15:04:23 +0000 UTC
        **/chrono/cron_test.go:634: got: 2021-03-16 15:10:27 +0100 CET expected: 2021-03-16 15:04:26 +0000 UTC
        **/chrono/cron_test.go:634: got: 2021-03-16 15:10:30 +0100 CET expected: 2021-03-16 15:04:29 +0000 UTC
        **/chrono/cron_test.go:634: got: 2021-03-16 15:10:33 +0100 CET expected: 2021-03-16 15:04:32 +0000 UTC
    --- FAIL: TestCronExpression_NextTime (0.00s)
    FAIL
    FAIL	github.com/Dexus-Forks/chrono	0.005s
    
    
  • Add immediate mode

    Add immediate mode

    At startup.

    • For unit tests, I want tasks to run immediately and with no delay, even if there is a delay specified.
    • For production, I want tasks to run after a specified delay.

    What I see now is that tasks run immediately, regardless of what the delay is. They then run again after the delay.

  • ScheduleWithCron gives unexpected results with WithLocation

    ScheduleWithCron gives unexpected results with WithLocation

    Description Using ScheduleWithCron with a WithLocation yield incorrect and unexpected results.

    The easiest way to reproduce and understand the issue is to run the Playground POC .

    Notice the code works as expected when not using WithLocation

    Expected behavior A cron to run every 2nd second.

    Behavior A cron can depending on location not run at all or run many times in a second.

    Playground POC https://go.dev/play/p/eGP1vQ-oKBE

    Notice how test2 is never output. Try replacing Europe/Copenhagen with America/New_York it will now runs many times in a second.

    Local POC

    package main
    
    import (
          "context"
          "log"
          "time"
    
          "github.com/procyon-projects/chrono"
    )
    
    func main() {
          r := chrono.NewDefaultTaskScheduler()
          task := func(ctx context.Context) {
                log.Println("task run")
          }
          _, err := r.ScheduleWithCron(task, "*/2 * * * * *", chrono.WithLocation("Europe/Copenhagen")) // America/New_York
          if err != nil {
                log.Fatal(err)
          }
    }
    

    Temp fix Remove WithLocation and calculate the offset yourself.

    Related bugs This could be related to: https://github.com/procyon-projects/chrono/issues/15

  • Query next task time

    Query next task time

    I would like to be able to output something like "Processed data succesfully. Will process again at _____." Could the scheduler support querying when the next task is scheduled for?

  • Does the scheduler persist?

    Does the scheduler persist?

    I am looking for a task scheduler which can execute tasks in the future. And Chrono seems pretty amazing! and close to my need.

    However, I'd like to know if there is a backing store? And does the schedule tasks persist through restarts as well?

    Thanks

  • Executor and Scheduler methods

    Executor and Scheduler methods

    Executor and Scheduler both share similar methods, for example :

    Executor

    Schedule(task Task, delay time.Duration) (ScheduledTask, error)	
    ScheduleWithFixedDelay(task Task, initialDelay time.Duration, delay time.Duration) (ScheduledTask, error)
    ScheduleAtFixedRate(task Task, initialDelay time.Duration, period time.Duration) (ScheduledTask, error)
    

    and

    Scheduler

    Schedule(task Task, options ...Option) (ScheduledTask, error)	
    ScheduleWithFixedDelay(task Task, delay time.Duration, options ...Option) (ScheduledTask, error)	
    ScheduleAtFixedRate(task Task, period time.Duration, options ...Option) (ScheduledTask, error)
    

    Don't you think this could be confusing considering that both interfaces are exported in the same package?

  • Fix the bug that causes scheduled task not running

    Fix the bug that causes scheduled task not running

    Fix #17

    Description: the issues occurs when a cron expression is combined with location. the scheduled task is never triggered.

    Root Cause: time.In function does not override the timezone in converting process so it causes an issue while calculation next execution time.

  • Shutdown one-shot scheduler

    Shutdown one-shot scheduler

    I am currently using the one-shot scheduler to end an event. I want to shutdown that scheduler after the event is ended. However, I don't know where to put the shutdown code. The event ending scheduler look like this:

    	repo.endScheduler = chrono.NewDefaultTaskScheduler()
    	endTime := time.Unix(event.EndTime, 0)
    	if _, err := repo.endScheduler.Schedule(func(ctx context.Context) {
    		err := repo.end(event.ID)
    		if err != nil {
    			log.Fatalf("Failed to end event: %v\n", err)
    		} else {
    			log.Println("Event ended.")
    		}
    	}, chrono.WithTime(endTime)); err != nil {
    		log.Fatalf("Failed to schedule event end time: %v\n", err)
    		return nil, err
    	}
    

    My question is: Will the fact that I am not shutting down the scheduler affect the performance of my application? If it will, then how should I do it? Thank you in advance for your attention.

  • Add validation function for cron syntax

    Add validation function for cron syntax

    I've added a new function for validating the cron syntax in this PR. Also, I've added some new error responses for better debugging. I've added the tests for these new errors in cron_test.

  • Subsecond granularity for task scheduling

    Subsecond granularity for task scheduling

    According to what I could find in task.go, within Option.WithTime(), it appears that the subsecond part of a timestamp is ignored so that scheduling tasks with a granularity better than 1 second is not possible.

    My main use case for this would be unit tests, for which scheduling durations around 50-100 ms are usually enough to achieve stable tests while keeping the test execution times small. With the 1 second granularity which is supported at the moment I have several tests that need to run for 5-10 seconds in order to have a stable scheduling order.

  • [chrono 2.x] Add Scheduler Store Support

    [chrono 2.x] Add Scheduler Store Support

    Scheduled jobs and executions will be persisted optionally to a store like a database. Tasks will be fetched from a store.

    Add a store interface. the implementation details is not clear. description will be updated later.

  • [chrono 2.x] Add WithContext function

    [chrono 2.x] Add WithContext function

    WithContext function will be added to be able to pass context to scheduled task.

    Executor and Runner don’t have context parameters. When we add WithContext function, we should be aware this change will be breaking backward compability.

  • Add end of month support

    Add end of month support

    is end of month supported? I don't seem to get errors running 0 0 21 L * ? *

    I definitely don't see it in the code, just curious if I'm missing something here,

    thank you.

Scheduler - Scheduler package is a zero-dependency scheduling library for Go

Scheduler Scheduler package is a zero-dependency scheduling library for Go Insta

Jan 14, 2022
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
Statefulset-scheduler (aka sfs-scheduler)

statefulset-scheduler (aka sfs-scheduler) Installation I already upload docker i

Dec 19, 2021
Scheduler: the scheduler of distbuild written in Golang

scheduler Introduction scheduler is the scheduler of distbuild written in Go. Pr

Feb 9, 2022
Linstor-scheduler-extender - LINSTOR scheduler extender plugin for Kubernetes

linstor-scheduler-extender LINSTOR scheduler extender plugin for Kubernetes whic

Dec 30, 2022
Crane scheduler is a Kubernetes scheduler which can schedule pod based on actual node load.

Crane-scheduler Overview Crane-scheduler is a collection of scheduler plugins based on scheduler framework, including: Dynamic scheuler: a load-aware

Dec 29, 2022
goInterLock is golang job/task scheduler with distributed locking mechanism (by Using Redis🔒).
goInterLock is golang job/task scheduler with distributed locking mechanism (by Using Redis🔒).

goInterLock is golang job/task scheduler with distributed locking mechanism. In distributed system locking is preventing task been executed in every instant that has the scheduler,

Dec 5, 2022
Go distributed task scheduler

Go distributed task scheduler

Nov 13, 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
Gotask - A simple task queue is stripped when the program is written to achieve the task delivery function
Gotask - A simple task queue is stripped when the program is written to achieve the task delivery function

gotask The simple task queue is stripped when the program is written to achieve

Feb 14, 2022
personal tweet scheduler - it needs my guidance now for it to work for you - it works on my mac - will release it someday

tit tit daemon write tests automate build & install plist replace {{path_for_titd_executable}} accordingly. <?xml version="1.0" encoding="UTF-8"?> <!D

Feb 4, 2022
A lightweight job scheduler based on priority queue with timeout, retry, replica, context cancellation and easy semantics for job chaining. Build for golang web apps.

Table of Contents Introduction What is RIO? Concern An asynchronous job processor Easy management of these goroutines and chaining them Introduction W

Dec 9, 2022
Chadburn is a scheduler alternative to cron, built on Go and designed for Docker environments.

Chadburn - a job scheduler Chadburn is a modern and low footprint job scheduler for docker environments, written in Go. Chadburn aims to be a replacem

Dec 6, 2022
GPU Sharing Scheduler for Kubernetes Cluster
GPU Sharing Scheduler for Kubernetes Cluster

GPU Sharing Scheduler Extender in Kubernetes Overview More and more data scientists run their Nvidia GPU based inference tasks on Kubernetes. Some of

Jan 6, 2023
Package tasks is an easy to use in-process scheduler for recurring tasks in Go

Tasks Package tasks is an easy to use in-process scheduler for recurring tasks in Go. Tasks is focused on high frequency tasks that run quick, and oft

Dec 18, 2022
A simple job scheduler backed by Postgres.

A simple job scheduler backed by Postgres used in production at https://operand.ai. Setup needs two environment variables, SECRET and ENDPOINT. The se

Sep 10, 2022
cpuworker - A Customized Goroutine Scheduler over Golang Runtime
cpuworker - A Customized Goroutine Scheduler over Golang Runtime

cpuworker Status Working in process. Run the Demo Make sure the GOMAXPROCS is bigger than 1 and there is at least GOMAXPROCS physical OS threads avail

Dec 6, 2022
A Framework for FaaS load balancing | stack-scheduler repository|

P2PFaaS A Framework for FaaS load balancing | stack-scheduler repository Introduction The P2PFaaS is a framework that allows you to implement a load b

Oct 29, 2021
A sample to showcase how to create a k8s scheduler extender

sample-scheduler-extender A sample to showcase how to create a k8s scheduler extender. UPDATE on 2020.6.10 Switch go module, and wire dependencies to

Nov 17, 2021