cron

package module
v1.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 1, 2025 License: MIT Imports: 23 Imported by: 1

README

Changes:
Job runs has a context, so each "run" can be individually cancelled
Jobs recover panic by default
Jobs can have a description (label)
Jobs ID are string and can have user specified ID
Job run have a dedicated logger that can be use to keep logs for a specific run
Can add hooks callback to global JobEvent or entry specific events
Can have a job that only run once using cron.Once
Can change the *time.Location while running
Do not sort all entries at every iteration
Use Heap data structure to store entries
Can ask the cron object if a job is currently running with isRunning := c.IsRunning(entryID)
Tests runs in under a second (instead of over a minute)
Comes with a complete admin web interface built-in

Built in job wrapper:

func Once(job IntoJob) Job
func N(n int, j IntoJob) Job
func WithRetry(maxRetry int, job IntoJob) Job
func WithTimeout(time.Duration, IntoJob) Job
func WithDeadline(time.Time, IntoJob) Job
func WithJitter(time.Duration, IntoJob) Job
func SkipIfStillRunning(IntoJob) Job
func DelayIfStillRunning(IntoJob) Job
func ThresholdClb(threshold time.Duration, j IntoJob, clb ThresholdCallback) Job
func ThresholdClbAsync(threshold time.Duration, j IntoJob, clb ThresholdCallbackAsync) Job
package main

import (
	"context"
	"errors"
	"fmt"
	"github.com/alaingilbert/cron"
	"github.com/alaingilbert/cron/webadmin"
	"log/slog"
	"net/http"
	"os"
	"time"
)

type SomeJob struct{}

// Run implements the Job interface for SomeJob
func (s SomeJob) Run(context.Context, *cron.Cron, cron.JobRun) error {
	fmt.Println("Some job")
	return nil
}

func main() {
	appCtx := context.Background()

	l := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelError}))

	c := cron.New().
		WithSeconds().
		WithContext(appCtx). // Can provide a custom context
		WithLogger(l).       // Can provide a custom logger
		Build()

	// A simple function can be used as a job
	_, _ = c.AddJob("* * * * * *", func() {
		fmt.Println("hello world")
	})

	// If your function returns an error, it will be logged by the cron logger
	_, _ = c.AddJob("* * * * * *", func() error {
		return errors.New("this error will be logged by the cron logger")
	})

	// You can give a description to your jobs using the cron.Label helper
	// The label will be part of the log if the job returns an error
	_, _ = c.AddJob("* * * * * *", func() error {
		return errors.New("this error will be logged by the cron logger")
	}, cron.Label("some description"))

	// A job can also receive a context if you want to handle graceful shutdown
	_, _ = c.AddJob("* * * * * *", func(ctx context.Context) {
		// ...
	})

	// This is also valid
	_, _ = c.AddJob("* * * * * *", func(ctx context.Context) error {
		return nil
	})

	// WithTimeout will ensure that the context get cancelled after the given duration
	_, _ = c.AddJob("*/3 * * * * *", cron.WithTimeout(2*time.Second, func(ctx context.Context) {
		select {
		case <-time.After(time.Minute):
		case <-ctx.Done():
			return
		}
	}))

	// This is also valid
	_, _ = c.AddJob("*/3 * * * * *", cron.WithTimeout(2*time.Second, func(ctx context.Context) error {
		select {
		case <-time.After(time.Minute):
		case <-ctx.Done():
			return ctx.Err()
		}
		return nil
	}))

	// Anything that implements the Job interface can be used for job
	// Or any of:
	// Run()
	// Run() error
	// Run(context.Context)
	// Run(context.Context) error
	// Run(cron.EntryID)
	// Run(cron.EntryID) error
	// Run(cron.Entry)
	// Run(cron.Entry) error
	// Run(*cron.Cron)
	// Run(*cron.Cron) error
	// Run(cron.JobRun)
	// Run(cron.JobRun) error
	// Run(context.Context, cron.EntryID)
	// Run(context.Context, cron.EntryID) error
	// Run(context.Context, cron.Entry)
	// Run(context.Context, cron.Entry) error
	// Run(context.Context, *cron.Cron)
	// Run(context.Context, *cron.Cron) error
	// Run(context.Context, cron.JobRun)
	// Run(context.Context, cron.JobRun) error
	// Run(*cron.Cron, cron.EntryID)
	// Run(*cron.Cron, cron.EntryID) error
	// Run(*cron.Cron, cron.Entry)
	// Run(*cron.Cron, cron.Entry) error
	// Run(*cron.Cron, cron.JobRun)
	// Run(*cron.Cron, cron.JobRun) error
	// Run(context.Context, *cron.Cron, cron.EntryID)
	// Run(context.Context, *cron.Cron, cron.EntryID) error
	// Run(context.Context, *cron.Cron, cron.Entry)
	// Run(context.Context, *cron.Cron, cron.Entry) error
	// Run(context.Context, *cron.Cron, cron.JobRun)
	// Run(context.Context, *cron.Cron, cron.JobRun) error
	_, _ = c.AddJob("* * * * * *", SomeJob{})

	// When using cron.Once, the job will remove itself from the cron entries
	// after being executed once
	_, _ = c.AddJob("* * * * * *", cron.Once(func() {
		fmt.Println("Will only be executed once")
	}))

	// When using cron.N, the job will remove itself from the cron entries
	// after being executed "n" times
	_, _ = c.AddJob("* * * * * *", cron.N(2, func() {
		fmt.Println("Will be executed 2 times")
	}))

	// cron.SkipIfStillRunning will ensure that the job is skipped if a previous
	// invocation is still running
	_, _ = c.AddJob("* * * * * *", cron.SkipIfStillRunning(func() {
		fmt.Println("Slow job")
		time.Sleep(3 * time.Second)
	}))

	// You can use cron.Chain to chain wrappers to your job
	_, _ = c.AddJob("* * * * * *", cron.Chain(func() {
		fmt.Println("job with chained wrappers")
	}, cron.TimeoutWrapper(time.Second), cron.Once))

	// Jitter adds some random delay before running the job
	_, _ = c.AddJob("0 */5 * * * *", cron.WithJitter(time.Minute, func() {
		fmt.Println("Runs every 5min, but wait 0-1 minute before starting")
	}))

	// This job is disabled by default
	disabledID, _ := c.AddJob("* * * * * *", func() {
		fmt.Println("this job is disabled by default")
	}, cron.Disabled)

	// Job can be enabled/disabled using their ID
	c.Enable(disabledID)
	c.Disable(disabledID)

	// You can specify the ID to use for a job
	customJobIDEntryID, _ := c.AddJob("* * * * * *", func(id cron.EntryID) {
		fmt.Printf("this job has a custom ID: %s\n", id)
	}, cron.WithID("my-job-id"))

	// You can run a job as soon as the cron is started with cron.RunOnStart
	_, _ = c.AddJob("*/10 * * * * *", func() {
		fmt.Println("this job runs as soon as cron is started, then at every 10th seconds", time.Now())
	}, cron.RunOnStart)

	// This is an example of how you can get the time
	// the job was scheduled to run at
	_, _ = c.AddJob("*/5 * * * * *", func(entry cron.Entry) {
		fmt.Println(entry.Prev.UnixNano(), time.Now().UnixNano())
	})

	// A job can use the logger in JobRun to keep logs specific
	// to this run which can be retrieved once completed.
	useLoggerID, _ := c.AddJob("*/5 * * * * *", func(ctx context.Context, jr cron.JobRun) error {
		logger := jr.Logger()
		logger.Debug("debug log")
		logger.Info("info log")
		logger.Warn("warn log")
		logger.Error("error log")
		for i := 0; i < 10; i++ {
			logger.Info("info log", slog.Int("i", i))
			select {
			case <-time.After(time.Second):
			case <-ctx.Done():
				logger.Error(ctx.Err().Error())
				return ctx.Err()
			}
		}
		return nil
	}, cron.Label("JobRun logger"), cron.WithID("logger-job"))

	// Hooking
	// By default hooks are running asynchronously.
	// It is possible to add hooks for global job events
	c.OnJobStart(func(ctx context.Context, c *cron.Cron, id cron.HookID, run cron.JobRun) {
		fmt.Println("job started", run.Entry.ID, run.RunID)
	}, cron.HookLabel("global job start hook"))
	// You can also make a hook for a specific entry
	// See also OnJobCompleted / OnEntryJobCompleted
	c.OnEntryJobStart(customJobIDEntryID, func(ctx context.Context, c *cron.Cron, id cron.HookID, run cron.JobRun) {
		fmt.Println("job started", run.Entry.ID, run.RunID)
	})
	c.OnEntryJobCompleted(useLoggerID, func(ctx context.Context, c *cron.Cron, id cron.HookID, run cron.JobRun) {
		fmt.Println("get back job logs", run.Logs)
	}, cron.HookLabel("logger job completed hook"))
	// You can make hook for all JobEventType (JobStart, JobCompleted, JobErr, JobPanic)
	// using OnEvt / OnEntryEvt
	c.OnEvt(cron.JobPanic, func(ctx context.Context, c *cron.Cron, id cron.HookID, run cron.JobRun) {
		fmt.Println("job panic", run.Entry.ID, run.RunID)
	})
	
	// Starts the cron executor in its own thread (async)
	c.Start()

	// It is possible to update the schedule of a job while the application is running
	_ = c.UpdateScheduleWithSpec(useLoggerID, "*/10 * * * * *")
	
	// This library comes with a complete web interface administration tool (optional)
	mux := webadmin.GetMux(c)
	if err := http.ListenAndServe(":8080", mux); err != nil {
		panic(err)
	}
}

go test -v ./...
go clean -testcache && go test -race ./...

Documentation

Overview

Package cron implements a cron spec parser and job runner.

Usage

Callers may register Funcs to be invoked on a given schedule. Cron will run them in their own goroutines.

c := cron.New()
c.AddFunc("0 30 * * * *", func() { fmt.Println("Every hour on the half hour") })
c.AddFunc("@hourly",      func() { fmt.Println("Every hour") })
c.AddFunc("@every 1h30m", func() { fmt.Println("Every hour thirty") })
c.Start()
..
// Funcs are invoked in their own goroutine, asynchronously.
...
// Funcs may also be added to a running Cron
c.AddFunc("@daily", func() { fmt.Println("Every day") })
..
// Inspect the cron job entries' next and previous run times.
inspect(c.Entries())
..
c.Stop()  // Stop the scheduler (does not stop any jobs already running).

CRON Expression Format

A cron expression represents a set of times, using 6 space-separated fields.

Field name   | Mandatory? | Allowed values  | Allowed special characters
----------   | ---------- | --------------  | --------------------------
Seconds      | Yes        | 0-59            | * / , -
Minutes      | Yes        | 0-59            | * / , -
Hours        | Yes        | 0-23            | * / , -
Day of month | Yes        | 1-31            | * / , - ?
Month        | Yes        | 1-12 or JAN-DEC | * / , -
Day of week  | Yes        | 0-6 or SUN-SAT  | * / , - ?

Note: Month and Day-of-week field values are case insensitive. "SUN", "Sun", and "sun" are equally accepted.

Special Characters

Asterisk ( * )

The asterisk indicates that the cron expression will match for all values of the field; e.g., using an asterisk in the 5th field (month) would indicate every month.

Slash ( / )

Slashes are used to describe increments of ranges. For example 3-59/15 in the 1st field (minutes) would indicate the 3rd minute of the hour and every 15 minutes thereafter. The form "*\/..." is equivalent to the form "first-last/...", that is, an increment over the largest possible range of the field. The form "N/..." is accepted as meaning "N-MAX/...", that is, starting at N, use the increment until the end of that specific range. It does not wrap around.

Comma ( , )

Commas are used to separate items of a list. For example, using "MON,WED,FRI" in the 5th field (day of week) would mean Mondays, Wednesdays and Fridays.

Hyphen ( - )

Hyphens are used to define ranges. For example, 9-17 would indicate every hour between 9am and 5pm inclusive.

Question mark ( ? )

Question mark may be used instead of '*' for leaving either day-of-month or day-of-week blank.

Predefined schedules

You may use one of several pre-defined schedules in place of a cron expression.

Entry                  | Description                                | Equivalent To
-----                  | -----------                                | -------------
@yearly (or @annually) | Run once a year, midnight, Jan. 1st        | 0 0 0 1 1 *
@monthly               | Run once a month, midnight, first of month | 0 0 0 1 * *
@weekly                | Run once a week, midnight between Sat/Sun  | 0 0 0 * * 0
@daily (or @midnight)  | Run once a day, midnight                   | 0 0 0 * * *
@hourly                | Run once an hour, beginning of hour        | 0 0 * * * *

Intervals

You may also schedule a job to execute at fixed intervals, starting at the time it's added or cron is run. This is supported by formatting the cron spec like this:

@every <duration>

where "duration" is a string accepted by time.ParseDuration (http://golang.org/pkg/time/#ParseDuration).

For example, "@every 1h30m10s" would indicate a schedule that activates after 1 hour, 30 minutes, 10 seconds, and then every interval after that.

Note: The interval does not take the job runtime into account. For example, if a job takes 3 minutes to run, and it is scheduled to run every 5 minutes, it will have only 2 minutes of idle time between each run.

Time zones

All interpretation and scheduling is done in the machine's local time zone (as provided by the Go time package (http://www.golang.org/pkg/time).

Be aware that jobs scheduled during daylight-savings leap-ahead transitions will not be run!

Thread safety

Since the Cron service runs concurrently with the calling code, some amount of care must be taken to ensure proper synchronization.

All cron methods are designed to be correctly synchronized as long as the caller ensures that invocations have a clear happens-before ordering between them.

Implementation

Cron entries are stored in an array, sorted by their next activation time. Cron sleeps until the next job is due to be run.

Upon waking:

  • it runs each entry that is active on that second
  • it calculates the next run times for the jobs that were run
  • it re-sorts the array of entries by next activation time.
  • it goes to sleep until the soonest job.

Index

Constants

This section is empty.

Variables

View Source
var ErrEntryNotFound = errors.New("entry not found")

ErrEntryNotFound ...

View Source
var ErrHookNotFound = errors.New("hook not found")

ErrHookNotFound ...

View Source
var ErrIDAlreadyUsed = errors.New("id already used")

ErrIDAlreadyUsed ...

View Source
var ErrJobAlreadyRunning = errors.New("job already running")

ErrJobAlreadyRunning ...

View Source
var ErrJobRunNotFound = errors.New("job run not found")

ErrJobRunNotFound ...

View Source
var ErrUnsupportedJobType = errors.New("unsupported job type")

ErrUnsupportedJobType ...

Functions

func Disabled

func Disabled(_ *Cron, entry *Entry)

func HookDisable

func HookDisable(hook *hookStruct)

HookDisable disables a hook by setting its active status to false.

func HookSync

func HookSync(hook *hookStruct)

HookSync makes the hook run synchronously.

func Label

func Label(label string) func(*Cron, *Entry)

func RunOnStart

func RunOnStart(c *Cron, entry *Entry)

func WithID

func WithID(id EntryID) func(*Cron, *Entry)

func WithNext

func WithNext(next time.Time) func(*Cron, *Entry)

Types

type Builder

type Builder struct {
	// contains filtered or unexported fields
}

Builder provides a fluent interface for constructing a Cron instance with optional configuration. Use New() to create a builder, chain configuration methods, and call Build() to create the Cron instance.

func New

func New() *Builder

New creates a new Builder instance for constructing a Cron scheduler.

func (*Builder) Build

func (b *Builder) Build() *Cron

Build constructs and returns a configured Cron instance using the builder's settings. Default values are used for any unconfigured properties.

func (*Builder) Quiet

func (b *Builder) Quiet() *Builder

Quiet configures the builder to discard all log output

func (*Builder) WithClock

func (b *Builder) WithClock(clock clockwork.Clock) *Builder

WithClock sets the clock implementation for the Cron instance.

func (*Builder) WithContext

func (b *Builder) WithContext(ctx context.Context) *Builder

WithContext sets the base context for the Cron instance.

func (*Builder) WithIDFactory

func (b *Builder) WithIDFactory(idFactory IDFactory) *Builder

WithIDFactory sets the ID generator factory for the Cron instance.

func (*Builder) WithJobRunLoggerFactory

func (b *Builder) WithJobRunLoggerFactory(jobRunLoggerFactory JobRunLoggerFactory) *Builder

WithJobRunLoggerFactory sets the logger factory for individual job runs.

func (*Builder) WithKeepCompletedRunsDur

func (b *Builder) WithKeepCompletedRunsDur(keepCompletedRunsDur time.Duration) *Builder

WithKeepCompletedRunsDur sets the duration to keep completed job runs before cleanup.

func (*Builder) WithLocation

func (b *Builder) WithLocation(loc *time.Location) *Builder

WithLocation sets the timezone location for the Cron instance.

func (*Builder) WithLogger

func (b *Builder) WithLogger(logger Logger) *Builder

WithLogger sets the logger for the Cron instance.

func (*Builder) WithParser

func (b *Builder) WithParser(parser ScheduleParser) *Builder

WithParser sets the schedule parser for the Cron instance.

func (*Builder) WithSeconds

func (b *Builder) WithSeconds() *Builder

WithSeconds configures the parser to include seconds in the schedule specification.

type ConstantDelaySchedule

type ConstantDelaySchedule struct {
	Delay time.Duration
}

ConstantDelaySchedule represents a simple recurring duty cycle, e.g. "Every 5 minutes". It does not support jobs more frequent than once a second.

func Every

func Every(duration time.Duration) ConstantDelaySchedule

Every returns a crontab Schedule that activates once every duration. Delays of less than a second are not supported (will round up to 1 second). Any fields less than a Second are truncated.

func (ConstantDelaySchedule) Next

Next returns the next time this should be run. This rounds so that the next activation time will be on the second.

type Cron

type Cron struct {
	// contains filtered or unexported fields
}

Cron keeps track of any number of entries, invoking the associated func as specified by the schedule. It may be started, stopped, and the entries may be inspected while running.

func (*Cron) AddEntry

func (c *Cron) AddEntry(entry Entry, opts ...EntryOption) (EntryID, error)

AddEntry adds a pre-configured Entry to the Cron

func (*Cron) AddFunc

func (c *Cron) AddFunc(spec string, job FuncJob, opts ...EntryOption) (EntryID, error)

AddFunc adds a Job to the Cron to be run on the given schedule.

func (*Cron) AddJob

func (c *Cron) AddJob(spec string, job IntoJob, opts ...EntryOption) (EntryID, error)

AddJob adds a Job to the Cron to be run on the given schedule.

func (*Cron) AddJobStrict

func (c *Cron) AddJobStrict(spec string, job Job, opts ...EntryOption) (EntryID, error)

AddJobStrict same as AddJob, but only accept a Job interface instead of IntoJob (any)

func (*Cron) CancelJobRun

func (c *Cron) CancelJobRun(entryID EntryID, runID RunID) error

CancelJobRun attempts to cancel a running job by entry ID and run ID. Returns ErrEntryNotFound if the entry doesn't exist or ErrJobRunNotFound if the run doesn't exist.

func (*Cron) CleanupNow

func (c *Cron) CleanupNow()

CleanupNow triggers immediate cleanup of completed job runs

func (*Cron) CompletedJobRunsFor

func (c *Cron) CompletedJobRunsFor(entryID EntryID) ([]JobRun, error)

CompletedJobRunsFor returns all completed job runs for a specific entry. Returns ErrEntryNotFound if the entry doesn't exist.

func (*Cron) CompletedJobs

func (c *Cron) CompletedJobs() []JobRun

CompletedJobs returns a list of all completed job runs across all entries

func (*Cron) Disable

func (c *Cron) Disable(id EntryID)

Disable deactivates a previously enabled cron entry by its ID

func (*Cron) DisableHook

func (c *Cron) DisableHook(id HookID)

DisableHook disables a previously enabled hook by its ID

func (*Cron) Enable

func (c *Cron) Enable(id EntryID)

Enable activates a previously disabled cron entry by its ID

func (*Cron) EnableHook

func (c *Cron) EnableHook(id HookID)

EnableHook enables a previously disabled hook by its ID

func (*Cron) Entries

func (c *Cron) Entries() []Entry

Entries returns a snapshot of the cron entries.

func (*Cron) Entry

func (c *Cron) Entry(id EntryID) (Entry, error)

Entry returns a snapshot of the given entry, or nil if it couldn't be found.

func (*Cron) GetCleanupTS

func (c *Cron) GetCleanupTS() time.Time

GetCleanupTS returns the timestamp of the last completed job runs cleanup

func (*Cron) GetHook

func (c *Cron) GetHook(id HookID) (Hook, error)

GetHook returns a specific hook by ID or ErrHookNotFound if not found

func (*Cron) GetHooks

func (c *Cron) GetHooks() []Hook

GetHooks returns all registered hooks (both global and entry-specific)

func (*Cron) GetJobRun

func (c *Cron) GetJobRun(entryID EntryID, runID RunID) (JobRun, error)

GetJobRun retrieves a specific job run by entry ID and run ID. Returns ErrEntryNotFound if the entry doesn't exist or ErrJobRunNotFound if the run doesn't exist.

func (*Cron) GetNextTime

func (c *Cron) GetNextTime() time.Time

GetNextTime returns the next time a job is scheduled to be executed If no job is scheduled to be executed, the Zero time is returned

func (*Cron) IsRunning

func (c *Cron) IsRunning(id EntryID) bool

IsRunning either or not a specific job is currently running

func (*Cron) JobRunCompletedCh

func (c *Cron) JobRunCompletedCh() <-chan JobRun

JobRunCompletedCh returns channel for job run completion notifications

func (*Cron) JobRunCreatedCh

func (c *Cron) JobRunCreatedCh() <-chan JobRun

JobRunCreatedCh returns channel for job run creation notifications

func (*Cron) Location

func (c *Cron) Location() *time.Location

Location gets the time zone location

func (*Cron) OnEntryEvt

func (c *Cron) OnEntryEvt(entryID EntryID, evt JobEventType, clb HookFn, opts ...HookOption) HookID

OnEntryEvt registers a hook function for a specific job event type on a specific entry. The hook will be called only when the specified event occurs for the given entry. Returns the HookID for later removal.

func (*Cron) OnEntryJobCompleted

func (c *Cron) OnEntryJobCompleted(entryID EntryID, clb HookFn, opts ...HookOption) HookID

OnEntryJobCompleted registers a hook function for job completion events on a specific entry. The hook will be called only when the given entry's job completes. Returns the HookID for later removal.

func (*Cron) OnEntryJobStart

func (c *Cron) OnEntryJobStart(entryID EntryID, clb HookFn, opts ...HookOption) HookID

OnEntryJobStart registers a hook function for job start events on a specific entry. The hook will be called only when the given entry's job starts. Returns the HookID for later removal.

func (*Cron) OnEvt

func (c *Cron) OnEvt(evt JobEventType, clb HookFn, opts ...HookOption) HookID

OnEvt registers a global hook function for a specific job event type. The hook will be called whenever the specified event occurs for any job. Returns the HookID for later removal.

func (*Cron) OnJobCompleted

func (c *Cron) OnJobCompleted(clb HookFn, opts ...HookOption) HookID

OnJobCompleted registers a global hook function for job completion events. The hook will be called whenever any job completes. Returns the HookID for later removal.

func (*Cron) OnJobStart

func (c *Cron) OnJobStart(clb HookFn, opts ...HookOption) HookID

OnJobStart registers a global hook function for job start events. The hook will be called whenever any job starts. Returns the HookID for later removal.

func (*Cron) Remove

func (c *Cron) Remove(id EntryID)

Remove an entry from being run in the future.

func (*Cron) RemoveHook

func (c *Cron) RemoveHook(id HookID)

RemoveHook removes a previously registered hook by its HookID. Works for both global and entry-specific hooks.

func (*Cron) Run

func (c *Cron) Run() (started bool)

Run the cron scheduler, or no-op if already running.

func (*Cron) RunNow

func (c *Cron) RunNow(id EntryID) error

RunNow allows the user to run a specific job now

func (*Cron) RunningJobs

func (c *Cron) RunningJobs() []JobRun

RunningJobs returns a list of all currently running jobs across all entries

func (*Cron) RunningJobsFor

func (c *Cron) RunningJobsFor(entryID EntryID) ([]JobRun, error)

RunningJobsFor returns all currently running jobs for a specific entry // Returns ErrEntryNotFound if the entry doesn't exist.

func (*Cron) Schedule

func (c *Cron) Schedule(schedule Schedule, job Job, opts ...EntryOption) (EntryID, error)

Schedule adds a Job to the Cron to be run on the given schedule.

func (*Cron) SetCleanupInterval

func (c *Cron) SetCleanupInterval(dur time.Duration)

SetCleanupInterval sets the duration for keeping completed job runs before cleanup

func (*Cron) SetHookLabel

func (c *Cron) SetHookLabel(id HookID, label string)

SetHookLabel sets an hook's label

func (*Cron) SetLocation

func (c *Cron) SetLocation(newLoc *time.Location)

SetLocation sets a new location to use. Re-set the "Next" values for all entries. Re-sort entries and run due entries.

func (*Cron) Start

func (c *Cron) Start() (started bool)

Start the cron scheduler in its own go-routine, or no-op if already started.

func (*Cron) Stop

func (c *Cron) Stop() <-chan struct{}

Stop stops the cron scheduler if it is running; otherwise it does nothing. A context is returned so the caller can wait for running jobs to complete.

func (*Cron) Sub

func (c *Cron) Sub(id EntryID) *pubsub.Sub[EntryID, JobEvent]

Sub subscribes to job events for a specific entry ID

func (*Cron) UpdateLabel

func (c *Cron) UpdateLabel(id EntryID, label string)

UpdateLabel updates an entry's label

func (*Cron) UpdateSchedule

func (c *Cron) UpdateSchedule(id EntryID, schedule Schedule) error

UpdateSchedule updates an entry's schedule with a new Schedule

func (*Cron) UpdateScheduleWithSpec

func (c *Cron) UpdateScheduleWithSpec(id EntryID, spec string) error

UpdateScheduleWithSpec updates an entry's schedule by parsing a spec string

func (*Cron) Wait

func (c *Cron) Wait()

Wait stops the cron and waits for all running jobs to complete before returning.

type Entry

type Entry struct {
	ID   EntryID
	Spec *string
	// The schedule on which this job should be run.
	Schedule Schedule
	// The next time the job will run. This is the zero time if Cron has not been started or this entry's schedule is unsatisfiable
	Next time.Time
	// The last time this job was run. This is the zero time if the job has never been run.
	Prev time.Time
	// Label to describe the job
	Label string
	// Either or not the job is currently active
	Active bool
	// contains filtered or unexported fields
}

Entry consists of a schedule and the func to execute on that schedule.

func (Entry) Job

func (e Entry) Job() any

Job returns the original job as it was before it was wrapped by the cron library

type EntryID

type EntryID ID

EntryID identifies a cron entry

type EntryOption

type EntryOption func(*Cron, *Entry)

type FuncIDFactory

type FuncIDFactory func() ID

FuncIDFactory is a function type that generates a new unique ID. It implements the IDFactory interface by providing a Next() method.

func (FuncIDFactory) Next

func (f FuncIDFactory) Next() ID

Next implements the IDFactory interface for FuncIDFactory. It calls the underlying function to generate and return a new ID.

type FuncJob

type FuncJob func(context.Context, *Cron, JobRun) error

FuncJob is a wrapper that turns a func() into a cron.Job

func (FuncJob) Run

func (f FuncJob) Run(ctx context.Context, c *Cron, r JobRun) error

type FuncJobRunLoggerFactory

type FuncJobRunLoggerFactory func(w io.Writer) Logger

FuncJobRunLoggerFactory is a function type that implements JobRunLoggerFactory. It allows any function with the signature func(w io.Writer) *slog.Logger to be used as a JobRunLoggerFactory.

func (FuncJobRunLoggerFactory) New

New implements the JobRunLoggerFactory interface for FuncJobRunLoggerFactory. It simply calls the underlying function with the provided writer.

type Hook

type Hook struct {
	ID        HookID       // Unique identifier for the hook
	EntryID   *EntryID     // Associated job entry ID (nil if global)
	EventType JobEventType // Type of job event that triggers this hook
	Active    bool         // Whether the hook is currently active
	Async     bool         // Whether the hook runs asynchronously
	Label     string       // Human-readable label for the hook
}

Hook represents a configured cron job hook with its properties.

type HookFn

type HookFn func(context.Context, *Cron, HookID, JobRun)

HookFn is a function type for cron job hooks.

type HookID

type HookID ID

HookID is a unique identifier for a cron job hook.

type HookOption

type HookOption func(*hookStruct)

HookOption is a function type for configuring a hook.

func HookLabel

func HookLabel(label string) HookOption

HookLabel returns a HookOption that sets the label for a hook.

type ID

type ID string

ID is the type to use for all internal IDs (EntryID, RunID, HookID)

type IDFactory

type IDFactory interface {
	// Next generates and returns a new unique ID.
	// Implementations must ensure IDs are unique across calls.
	Next() ID
}

IDFactory is an interface for types that can generate unique IDs. Implementations should provide thread-safe ID generation.

func UuidIDFactory

func UuidIDFactory() IDFactory

UuidIDFactory generate and format UUID V4

type IntoJob

type IntoJob any

IntoJob is something that can be cast into a Job. See the J helper documentation for all accepted function signatures.

type Job

type Job interface {
	Run(context.Context, *Cron, JobRun) error
}

Job is an interface for submitted cron jobs.

func Chain

func Chain(j IntoJob, wrappers ...JobWrapper) Job

Chain `Chain(j, w1, w2, w3)` -> `w3(w2(w1(j)))`

func DelayIfStillRunning

func DelayIfStillRunning(j IntoJob) Job

DelayIfStillRunning serializes jobs, delaying subsequent runs until the previous one is complete. Jobs running after a delay of more than a minute have the delay logged at Info.

func J

func J(v IntoJob) Job

J is a helper to turn a IntoJob into a Job Any of these functions, or anything that have a "Run" method with one of these signatures can be casted into a Job. func() func() error func(context.Context) func(context.Context) error func(cron.EntryID) func(cron.EntryID) error func(cron.Entry) func(cron.Entry) error func(*cron.Cron) func(*cron.Cron) error func(cron.JobRun) func(cron.JobRun) error func(context.Context, cron.EntryID) func(context.Context, cron.EntryID) error func(context.Context, cron.Entry) func(context.Context, cron.Entry) error func(context.Context, *cron.Cron) func(context.Context, *cron.Cron) error func(context.Context, cron.JobRun) func(context.Context, cron.JobRun) error func(*cron.Cron, cron.EntryID) func(*cron.Cron, cron.EntryID) error func(*cron.Cron, cron.Entry) func(*cron.Cron, cron.Entry) error func(*cron.Cron, cron.JobRun) func(*cron.Cron, cron.JobRun) error func(context.Context, *cron.Cron, cron.EntryID) func(context.Context, *cron.Cron, cron.EntryID) error func(context.Context, *cron.Cron, cron.Entry) func(context.Context, *cron.Cron, cron.Entry) error func(context.Context, *cron.Cron, cron.JobRun) func(context.Context, *cron.Cron, cron.JobRun) error

func N

func N(n int, j IntoJob) Job

N runs a job "n" times

func Once

func Once(job IntoJob) Job

func SkipIfStillRunning

func SkipIfStillRunning(j IntoJob) Job

SkipIfStillRunning skips an invocation of the Job if a previous invocation is still running.

func ThresholdClb

func ThresholdClb(threshold time.Duration, j IntoJob, clb ThresholdCallback) Job

ThresholdClb execute a callback if the job runs longer than the specified threshold.

func ThresholdClbAsync

func ThresholdClbAsync(threshold time.Duration, j IntoJob, clb ThresholdCallbackAsync) Job

ThresholdClbAsync ...

func WithDeadline

func WithDeadline(deadline time.Time, job IntoJob) Job

func WithJitter

func WithJitter(duration time.Duration, job IntoJob) Job

WithJitter add some random delay before running the job

func WithRetry

func WithRetry(maxRetry int, job IntoJob) Job

WithRetry runs a job with retries up to maxRetry times

func WithTimeout

func WithTimeout(d time.Duration, job IntoJob) Job

WithTimeout ... `_, _ = cron.AddJob("* * * * * *", cron.WithTimeout(time.Second, func(ctx context.Context) { ... }))`

type Job1

type Job1 interface{ Run() }

type Job10

type Job10 interface{ Run(*Cron) error }

type Job10Wrapper

type Job10Wrapper struct{ Job10 }

func (*Job10Wrapper) Run

func (j *Job10Wrapper) Run(_ context.Context, cron *Cron, _ JobRun) error

type Job11

type Job11 interface{ Run(context.Context, *Cron) }

type Job11Wrapper

type Job11Wrapper struct{ Job11 }

func (*Job11Wrapper) Run

func (j *Job11Wrapper) Run(ctx context.Context, cron *Cron, _ JobRun) error

type Job12

type Job12 interface {
	Run(context.Context, *Cron) error
}

type Job12Wrapper

type Job12Wrapper struct{ Job12 }

func (*Job12Wrapper) Run

func (j *Job12Wrapper) Run(ctx context.Context, cron *Cron, _ JobRun) error

type Job13

type Job13 interface{ Run(*Cron, EntryID) }

type Job13Wrapper

type Job13Wrapper struct{ Job13 }

func (*Job13Wrapper) Run

func (j *Job13Wrapper) Run(_ context.Context, cron *Cron, r JobRun) error

type Job14

type Job14 interface{ Run(*Cron, EntryID) error }

type Job14Wrapper

type Job14Wrapper struct{ Job14 }

func (*Job14Wrapper) Run

func (j *Job14Wrapper) Run(_ context.Context, cron *Cron, r JobRun) error

type Job15

type Job15 interface {
	Run(context.Context, *Cron, EntryID)
}

type Job15Wrapper

type Job15Wrapper struct{ Job15 }

func (*Job15Wrapper) Run

func (j *Job15Wrapper) Run(ctx context.Context, cron *Cron, r JobRun) error

type Job16

type Job16 interface {
	Run(context.Context, *Cron, EntryID) error
}

type Job16Wrapper

type Job16Wrapper struct{ Job16 }

func (*Job16Wrapper) Run

func (j *Job16Wrapper) Run(ctx context.Context, cron *Cron, r JobRun) error

type Job17

type Job17 interface{ Run(Entry) }

type Job17Wrapper

type Job17Wrapper struct{ Job17 }

func (*Job17Wrapper) Run

func (j *Job17Wrapper) Run(_ context.Context, _ *Cron, r JobRun) error

type Job18

type Job18 interface{ Run(Entry) error }

type Job18Wrapper

type Job18Wrapper struct{ Job18 }

func (*Job18Wrapper) Run

func (j *Job18Wrapper) Run(_ context.Context, _ *Cron, r JobRun) error

type Job19

type Job19 interface{ Run(context.Context, Entry) }

type Job19Wrapper

type Job19Wrapper struct{ Job19 }

func (*Job19Wrapper) Run

func (j *Job19Wrapper) Run(ctx context.Context, _ *Cron, r JobRun) error

type Job1Wrapper

type Job1Wrapper struct{ Job1 }

func (*Job1Wrapper) Run

type Job2

type Job2 interface{ Run(context.Context) }

type Job20

type Job20 interface {
	Run(context.Context, Entry) error
}

type Job20Wrapper

type Job20Wrapper struct{ Job20 }

func (*Job20Wrapper) Run

func (j *Job20Wrapper) Run(ctx context.Context, _ *Cron, r JobRun) error

type Job21

type Job21 interface{ Run(*Cron, Entry) }

type Job21Wrapper

type Job21Wrapper struct{ Job21 }

func (*Job21Wrapper) Run

func (j *Job21Wrapper) Run(_ context.Context, c *Cron, r JobRun) error

type Job22

type Job22 interface{ Run(*Cron, Entry) error }

type Job22Wrapper

type Job22Wrapper struct{ Job22 }

func (*Job22Wrapper) Run

func (j *Job22Wrapper) Run(_ context.Context, c *Cron, r JobRun) error

type Job23

type Job23 interface {
	Run(context.Context, *Cron, Entry)
}

type Job23Wrapper

type Job23Wrapper struct{ Job23 }

func (*Job23Wrapper) Run

func (j *Job23Wrapper) Run(ctx context.Context, c *Cron, r JobRun) error

type Job24

type Job24 interface{ Run(JobRun) }

type Job24Wrapper

type Job24Wrapper struct{ Job24 }

func (*Job24Wrapper) Run

func (j *Job24Wrapper) Run(_ context.Context, _ *Cron, r JobRun) error

type Job25

type Job25 interface{ Run(JobRun) error }

type Job25Wrapper

type Job25Wrapper struct{ Job25 }

func (*Job25Wrapper) Run

func (j *Job25Wrapper) Run(_ context.Context, _ *Cron, r JobRun) error

type Job26

type Job26 interface{ Run(context.Context, JobRun) }

type Job26Wrapper

type Job26Wrapper struct{ Job26 }

func (*Job26Wrapper) Run

func (j *Job26Wrapper) Run(ctx context.Context, _ *Cron, r JobRun) error

type Job27

type Job27 interface {
	Run(context.Context, JobRun) error
}

type Job27Wrapper

type Job27Wrapper struct{ Job27 }

func (*Job27Wrapper) Run

func (j *Job27Wrapper) Run(ctx context.Context, _ *Cron, r JobRun) error

type Job28

type Job28 interface{ Run(*Cron, JobRun) }

type Job28Wrapper

type Job28Wrapper struct{ Job28 }

func (*Job28Wrapper) Run

func (j *Job28Wrapper) Run(_ context.Context, c *Cron, r JobRun) error

type Job29

type Job29 interface{ Run(*Cron, JobRun) error }

type Job29Wrapper

type Job29Wrapper struct{ Job29 }

func (*Job29Wrapper) Run

func (j *Job29Wrapper) Run(_ context.Context, c *Cron, r JobRun) error

type Job2Wrapper

type Job2Wrapper struct{ Job2 }

func (*Job2Wrapper) Run

func (j *Job2Wrapper) Run(ctx context.Context, _ *Cron, _ JobRun) error

type Job3

type Job3 interface{ Run(EntryID) }

type Job30

type Job30 interface {
	Run(context.Context, *Cron, JobRun)
}

type Job30Wrapper

type Job30Wrapper struct{ Job30 }

func (*Job30Wrapper) Run

func (j *Job30Wrapper) Run(ctx context.Context, c *Cron, r JobRun) error

type Job31

type Job31 interface {
	Run(context.Context, *Cron, Entry) error
}

type Job31Wrapper

type Job31Wrapper struct{ Job31 }

func (*Job31Wrapper) Run

func (j *Job31Wrapper) Run(ctx context.Context, c *Cron, r JobRun) error

type Job3Wrapper

type Job3Wrapper struct{ Job3 }

func (*Job3Wrapper) Run

func (j *Job3Wrapper) Run(_ context.Context, _ *Cron, r JobRun) error

type Job4

type Job4 interface {
	Run(context.Context, EntryID)
}

type Job4Wrapper

type Job4Wrapper struct{ Job4 }

func (*Job4Wrapper) Run

func (j *Job4Wrapper) Run(ctx context.Context, _ *Cron, r JobRun) error

type Job5

type Job5 interface{ Run() error }

type Job5Wrapper

type Job5Wrapper struct{ Job5 }

func (*Job5Wrapper) Run

type Job6

type Job6 interface{ Run(context.Context) error }

type Job6Wrapper

type Job6Wrapper struct{ Job6 }

func (*Job6Wrapper) Run

func (j *Job6Wrapper) Run(ctx context.Context, _ *Cron, _ JobRun) error

type Job7

type Job7 interface{ Run(EntryID) error }

type Job7Wrapper

type Job7Wrapper struct{ Job7 }

func (*Job7Wrapper) Run

func (j *Job7Wrapper) Run(_ context.Context, _ *Cron, r JobRun) error

type Job8

type Job8 interface {
	Run(context.Context, EntryID) error
}

type Job8Wrapper

type Job8Wrapper struct{ Job8 }

func (*Job8Wrapper) Run

func (j *Job8Wrapper) Run(ctx context.Context, _ *Cron, r JobRun) error

type Job9

type Job9 interface{ Run(*Cron) }

type Job9Wrapper

type Job9Wrapper struct{ Job9 }

func (*Job9Wrapper) Run

func (j *Job9Wrapper) Run(_ context.Context, cron *Cron, _ JobRun) error

type JobEvent

type JobEvent struct {
	Typ       JobEventType
	CreatedAt time.Time
}

type JobEventType

type JobEventType int
const (
	JobStart JobEventType = iota + 1
	JobCompleted
	JobErr
	JobPanic
)

func (JobEventType) String

func (e JobEventType) String() string

type JobRun

type JobRun struct {
	RunID       RunID      // Unique identifier for this job run
	Entry       Entry      // Job definition being executed
	CreatedAt   time.Time  // When the run was created
	StartedAt   *time.Time // When execution began (nil if not started)
	CompletedAt *time.Time // When execution finished (nil if not completed)
	Events      []JobEvent // Timeline of events during execution
	Error       error      // Final error state if any
	Panic       bool       // Whether job panicked during execution
	Logs        string     // Captured log output
	// contains filtered or unexported fields
}

JobRun represents a single execution of a scheduled job.

func (JobRun) Logger

func (j JobRun) Logger() Logger

Logger returns the structured logger for this job run

type JobRunLoggerFactory

type JobRunLoggerFactory interface {
	// New creates a new slog.Logger instance that writes to the given io.Writer.
	New(w io.Writer) Logger
}

JobRunLoggerFactory is an interface for creating loggers for individual job runs. Implementations should create a new logger that writes to the provided io.Writer.

func DefaultJobRunLoggerFactory

func DefaultJobRunLoggerFactory() JobRunLoggerFactory

DefaultJobRunLoggerFactory returns a default implementation of JobRunLoggerFactory. The default implementation creates a text-based logger with debug level logging that writes to the provided io.Writer.

type JobWrapper

type JobWrapper func(IntoJob) Job

func DeadlineWrapper

func DeadlineWrapper(deadline time.Time) JobWrapper

func JitterWrapper

func JitterWrapper(duration time.Duration) JobWrapper

JitterWrapper add some random delay before running the job

func NWrapper

func NWrapper(n int) JobWrapper

func ThresholdClbAsyncWrapper

func ThresholdClbAsyncWrapper(threshold time.Duration, clb ThresholdCallbackAsync) JobWrapper

ThresholdClbAsyncWrapper wraps a job and starts a timer for the given threshold. If the job runs longer than the threshold, the callback is triggered. The callback is invoked asynchronously and does not block job execution.

func ThresholdClbWrapper

func ThresholdClbWrapper(threshold time.Duration, clb ThresholdCallback) JobWrapper

func TimeoutWrapper

func TimeoutWrapper(duration time.Duration) JobWrapper

TimeoutWrapper automatically cancel the job context after a given duration

func WithRetryWrapper

func WithRetryWrapper(maxRetry int) JobWrapper

WithRetryWrapper creates a JobWrapper that retries a job up to maxRetry times

type Logger

type Logger interface {
	// Debug logs a debug message with optional key-value pairs
	Debug(msg string, keysAndValues ...any)
	// Info logs an informational message with optional key-value pairs
	Info(msg string, keysAndValues ...any)
	// Warn logs a warning message with optional key-value pairs
	Warn(msg string, keysAndValues ...any)
	// Error logs an error message with optional key-value pairs
	Error(msg string, keysAndValues ...any)
}

Logger is the interface used in this package for logging, so that any backend can be plugged in.

var DefaultLogger Logger = slog.Default()

DefaultLogger is used by Cron if none is specified.

var DiscardLogger Logger = slog.New(slog.NewTextHandler(io.Discard, nil))

DiscardLogger can be used by callers to discard all log messages.

type ParseOption

type ParseOption int

Configuration options for creating a parser. Most options specify which fields should be included, while others enable features. If a field is not included the parser will assume a default value. These options do not change the order fields are parse in.

const (
	Second         ParseOption = 1 << iota // Seconds field, default 0
	SecondOptional                         // Optional seconds field, default 0
	Minute                                 // Minutes field, default 0
	Hour                                   // Hours field, default 0
	Dom                                    // Day of month field, default *
	Month                                  // Month field, default *
	Dow                                    // Day of week field, default *
	DowOptional                            // Optional day of week field, default *
	Descriptor                             // Allow descriptors such as @monthly, @weekly, etc.
)

type Parser

type Parser struct {
	// contains filtered or unexported fields
}

A custom Parser that can be configured.

func NewParser

func NewParser(options ParseOption) Parser

NewParser creates a Parser with custom options.

It panics if more than one Optional is given, since it would be impossible to correctly infer which optional is provided or missing in general.

Examples

// Standard parser without descriptors
specParser := NewParser(Minute | Hour | Dom | Month | Dow)
sched, err := specParser.Parse("0 0 15 */3 *")

// Same as above, just excludes time fields
specParser := NewParser(Dom | Month | Dow)
sched, err := specParser.Parse("15 */3 *")

// Same as above, just makes Dow optional
specParser := NewParser(Dom | Month | DowOptional)
sched, err := specParser.Parse("15 */3")

func (Parser) Parse

func (p Parser) Parse(spec string) (Schedule, error)

Parse returns a new crontab schedule representing the given spec. It returns a descriptive error if the spec is not valid. It accepts crontab specs and features configured by NewParser.

type RunID

type RunID ID

RunID represents a unique identifier for a job run.

type Schedule

type Schedule interface {
	// Next return the next activation time, later than the given time.
	// Next is invoked initially, and then each time the job is run.
	Next(time.Time) time.Time
}

The Schedule describes a job's duty cycle.

func ParseStandard

func ParseStandard(standardSpec string) (Schedule, error)

ParseStandard returns a new crontab schedule representing the given standardSpec (https://en.wikipedia.org/wiki/Cron). It requires 5 entries representing: minute, hour, day of month, month and day of week, in that order. It returns a descriptive error if the spec is not valid.

It accepts

  • Standard crontab specs, e.g. "* * * * ?"
  • Descriptors, e.g. "@midnight", "@every 1h30m"

type ScheduleParser

type ScheduleParser interface {
	Parse(spec string) (Schedule, error)
}

ScheduleParser is an interface for schedule spec parsers that return a Schedule

type SpecSchedule

type SpecSchedule struct {
	Second, Minute, Hour, Dom, Month, Dow uint64

	// Override location for this schedule.
	Location *time.Location
}

SpecSchedule specifies a duty cycle (to the second granularity), based on a traditional crontab specification. It is computed initially and stored as bit sets.

func (*SpecSchedule) Next

func (s *SpecSchedule) Next(t time.Time) time.Time

Next returns the next time this schedule is activated, greater than the given time. If no time can be found to satisfy the schedule, return the zero time.

type ThresholdCallback

type ThresholdCallback func(ctx context.Context, c *Cron, r JobRun, threshold, dur time.Duration, err error)

type ThresholdCallbackAsync

type ThresholdCallbackAsync func(ctx context.Context, c *Cron, r JobRun, threshold time.Duration)

Directories

Path Synopsis
internal
mtx
Package mtx provides thread-safe wrappers for values and maps using read-write mutexes.
Package mtx provides thread-safe wrappers for values and maps using read-write mutexes.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL