Giter Site home page Giter Site logo

codnect / chrono Goto Github PK

View Code? Open in Web Editor NEW
437.0 5.0 21.0 75 KB

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

License: MIT License

Go 100.00%
scheduler-library chrono golang-library golang cron go schedule-task scheduler

chrono's Introduction

Chrono Logo

Chrono

Go Report Card CircleCI 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. WithTime option is used to specify the execution time.

taskScheduler := chrono.NewDefaultTaskScheduler()
now := time.Now()
startTime := now.Add(time.Second * 1)

task, err := taskScheduler.Schedule(func(ctx context.Context) {
	log.Print("One-Shot Task")
}, chrono.WithTime(startTime))

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

Also, WithStartTime option can be used to specify the execution time. But It's deprecated.

taskScheduler := chrono.NewDefaultTaskScheduler()

task, err := taskScheduler.Schedule(func(ctx context.Context) {
	log.Print("One-Shot Task")
}, chrono.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, chrono.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, chrono.WithStartTime(now.Year(), now.Month(), now.Day(), 18, 45, 0),
chrono.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 * *", chrono.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 */

Stargazers

Stargazers repo roster for @procyon-projects/chrono

Forkers

Forkers repo roster for @procyon-projects/chrono

License

Chrono is released under MIT License.

chrono's People

Contributors

burakkoken avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

chrono's Issues

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:
#15

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.

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?

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

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.

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.

[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.

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?

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?

`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.

Add Marker support to be able to schedule tasks

This feature makes it easier to schedule task by using markers and code generation.
The following markers should be added, they will be enough for now.

  • +chrono:scheduled. This should have the following arguments.
    FixedDelay, FixedRate, Cron
  • +chrono:enable-scheduling
    (main method should be marked with this annotation, it will enable support for scheduling)

The simple rule that we need to follow to marker a method with +chrono:scheduled is:
The method should have the method signature as shown below. (If not, the method will be ignored or an error message will be shown.)

func(ctx context.Context)

Please investigate how to support this feature.

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

[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.

*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.

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.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.