crong

package module
v0.0.0-...-4abe2a5 Latest Latest
Warning

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

Go to latest
Published: Aug 27, 2024 License: AGPL-3.0 Imports: 12 Imported by: 0

README

crong

Crong: Lightweight, straightforward cron expression parser, ticker and task scheduler for your Golang projects.

Usage

Create a schedule from a cron expression, calculate future/past schedules:

package main

import (
	"fmt"
	"log"
	"time"
	"github.com/arcward/crong"
)

func main() {
	schedule, err := crong.New("0 0 * * *", time.UTC)
	if err != nil {
		log.Fatal(err)
    	}
	
	// Get the next (or most recent) scheduled time relative to the given time
	fmt.Println("Next scheduled time:", schedule.Next(time.Now()))
	fmt.Println("Previous scheduled time:", schedule.Prev(time.Now()))
    
	// Check if the given time satisfies the schedule
	if schedule.Matches(time.Now()) {
		fmt.Println("It's time!")
    	}
}

Create a ticker that sends a tick on a channel whenever the cron schedule fires, similar to time.Ticker:

package main

import (
	"context"
	"log"
	"time"
	"github.com/arcward/crong"
)

func main() {
	schedule, err := crong.New("@hourly", time.UTC)
	if err != nil {
		log.Fatal(err)
    	}
	
	ticker := crong.NewTicker(context.Background(), schedule, 1 * time.Minute)
	defer ticker.Stop()
	
	select {
	case t := <-ticker.C:
		log.Printf("%s: Tick!", t)
    	}
}

Schedule a function to run whenever the schedule fires:

package main

import (
	"context"
	"log"
	"time"
	"github.com/arcward/crong"
)

func main() {
	schedule, err := crong.New("* * * * *", time.UTC)
	if err != nil {
		log.Fatal(err)
    	}
	
	// MaxConcurrent=0 only allows the job to run sequentially, while
	// increasing TickerReceiveTimeout can accommodate potentially long-running
	// jobs, where you may not want the next tick to be dropped immediately.
	// MaxConsecutiveFailures=10 will stop executing the given function if it
	// returns a non-nil error ten times in a row.
	opts := &crong.ScheduledJobOptions{
		MaxConcurrent:        0,
		TickerReceiveTimeout: 30 * time.Second,
		MaxConsecutiveFailures: 10,
	}
	scheduledJob := crong.ScheduleFunc(
		context.Background(),
		schedule,
		opts,
		func(t time.Time) error {
			log.Printf("Scheduled run for %s started at %s", t, time.Now())
			return nil
        	},
	)
	defer scheduledJob.Stop(context.Background())
}

Syntax

Supports standard cron syntax (see https://en.wikipedia.org/wiki/Cron), as well as less standard expressions. For example, 5/10 4,5 * * means "every 10 minutes starting at the 5th minute of the hour, for hours 4 and 5."

Days of the week are indexed 0-6, with 0 being Sunday, and can be referenced by name (SUN, MON, TUE, WED, THU, FRI, SAT) or by number.

Months are indexed 1-12, and can be referenced by name (JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC) or by number.

Cron macros supported:

  • @yearly (or @annually) - Run once a year, midnight, Jan. 1
  • @monthly - Run once a month, midnight, first of month
  • @weekly - Run once a week, midnight between Saturday and Sunday
  • @daily (or @midnight) - Run once a day, midnight
  • @hourly - Run once an hour, beginning of hour

Other characters supported:

  • *: Wildcard/Any value (ex: Every minute: * * * * *)
  • ,: Value list separator (ex: Minute 0 and 30 of every hour: 0,30 * * * *)
  • -: Range of values (ex: Minute 0-15 of every hour: 0-15 * * * *)
  • /: Step values (ex: Every 2nd minute from minute 10-20 of every hour: 10-20/2 * * * *)
  • ?: No specific value (month, day of month, day of week only)
  • L: Last day of month. When used, must be used alone in the day field (ex: 12:30 on the last day of every month: 30 12 L * *)

Documentation

Overview

Package crong is a library for parsing cron expressions, calculating the next run times of the expressions, and validating the expressions.

Syntax

Supports standard cron syntax (see https://en.wikipedia.org/wiki/Cron), as well as less standard expressions. For example, `5/10 4,5 * *` means "every 10 minutes starting at the 5th minute of the hour, for hours 4 and 5."

Days of the week are indexed 0-6, with 0 being Sunday, and can be referenced by name (SUN, MON, TUE, WED, THU, FRI, SAT) or by number.

Months are indexed 1-12, and can be referenced by name (JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC) or by number.

Cron macros supported:

@yearly (or @annually) - Run once a year, midnight, Jan. 1
@monthly - Run once a month, midnight, first of month
@weekly - Run once a week, midnight between Saturday and Sunday
@daily (or @midnight) - Run once a day, midnight
@hourly - Run once an hour, beginning of hour

Other characters supported:

  • - any value , - value list separator
  • - range of values / - step values ? - no specific value (month, day of month, day of week only) L - last day of month (when used, must be used alone)

Index

Constants

View Source
const (
	Any           = '*'
	ListSeparator = ','
	Range         = '-'
	Step          = '/'
	Blank         = '?'
	Last          = 'L'

	Yearly   = "@yearly"
	Annually = "@annually"
	Monthly  = "@monthly"
	Weekly   = "@weekly"
	Daily    = "@daily"
	Midnight = "@midnight"
	Hourly   = "@hourly"

	Sunday    = "SUN"
	Monday    = "MON"
	Tuesday   = "TUE"
	Wednesday = "WED"
	Thursday  = "THU"
	Friday    = "FRI"
	Saturday  = "SAT"

	January   = "JAN"
	February  = "FEB"
	March     = "MAR"
	April     = "APR"
	May       = "MAY"
	June      = "JUN"
	July      = "JUL"
	August    = "AUG"
	September = "SEP"
	October   = "OCT"
	November  = "NOV"
	December  = "DEC"
)

Variables

Logger used by Ticker and ScheduledJob. By default, it discards all logs.

Functions

func NewRandom

func NewRandom(r *rand.Rand) (string, error)

NewRandom creates a new Schedule with a random cron expression

Types

type JobRuntime

type JobRuntime struct {
	// Start is the time the job started
	Start time.Time

	// End is the time the job ended
	End time.Time

	// Error is any error that occurred during the job
	Error error
}

JobRuntime is a record of a job's runtime and any error

type Schedule

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

Schedule is a cron schedule created from a cron expression

Usage

To create a new Schedule, use the New function:

  	s, err := crong.New("0 0 * * *", time.UTC)
	if err != nil {
		log.Fatal(err)
	}

To get the next scheduled time after a given time, use the Next method:

next := s.Next(time.Now())

To check if a time matches the schedule, use the Matches method:

if s.Matches(time.Now()) {
	fmt.Println("It's time!")
}

func New

func New(cron string, loc *time.Location) (*Schedule, error)

New creates a new Schedule from a cron expression. loc is the location to use for the schedule (if nil, defaults to time.UTC)

func (*Schedule) Day

func (s *Schedule) Day() string

Day returns the day value of the schedule

func (*Schedule) Hour

func (s *Schedule) Hour() string

Hour returns the hour value of the schedule

func (*Schedule) LogValue

func (s *Schedule) LogValue() slog.Value

func (*Schedule) Matches

func (s *Schedule) Matches(t time.Time) bool

Matches returns true if the schedule matches the given time

func (*Schedule) Minute

func (s *Schedule) Minute() string

Minute returns the minute value of the schedule

func (*Schedule) Month

func (s *Schedule) Month() string

Month returns the month value of the schedule

func (*Schedule) Next

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

Next returns the next scheduled time after the given time

func (*Schedule) Prev

func (s *Schedule) Prev(t time.Time) time.Time

Prev returns the previous scheduled time before the given time

func (*Schedule) String

func (s *Schedule) String() string

String returns the string representation of the schedule

func (*Schedule) UntilNext

func (s *Schedule) UntilNext(t time.Time) time.Duration

UntilNext returns the duration until the next scheduled time after the given time

func (*Schedule) Weekday

func (s *Schedule) Weekday() string

Weekday returns the weekday value of the schedule

type ScheduleState

type ScheduleState int64
const (
	ScheduleStarted ScheduleState = iota + 1
	ScheduleSuspended
	ScheduleStopped
)

type ScheduledJob

type ScheduledJob struct {

	// Failures is the number of times the job has failed
	Failures atomic.Int64

	// ConsecutiveFailures is the number of times the job has failed in a row
	ConsecutiveFailures atomic.Int64

	// Runs is the number of times the job has run
	Runs atomic.Int64

	// Running is the number of times the job is currently running
	Running atomic.Int64
	// contains filtered or unexported fields
}

ScheduledJob is a function that runs on Ticker ticks for a Schedule

func NewScheduledJob

func NewScheduledJob(
	schedule *Schedule,
	opts ScheduledJobOptions,
	f func(t time.Time) error,
) *ScheduledJob

func ScheduleFunc

func ScheduleFunc(
	ctx context.Context,
	schedule *Schedule,
	opts ScheduledJobOptions,
	f func(t time.Time) error,
) *ScheduledJob

ScheduleFunc creates and starts a new ScheduledJob with the given schedule and options. It immediately begins executing the provided function according to the schedule.

The function f will be called with the current time whenever the schedule is triggered. If f returns an error, it will be recorded in the job's runtime history.

Parameters:

  • ctx: A context.Context for cancellation and timeout control.
  • schedule: A *Schedule that determines when the job should run.
  • opts: ScheduledJobOptions to configure the job's behavior.
  • f: A function to be executed on each scheduled tick, with the signature func(time.Time) error.

Returns:

  • *ScheduledJob: A pointer to the newly created and started ScheduledJob.

The returned ScheduledJob is already running and does not need to be started manually. Use the returned ScheduledJob's methods (e.g., Stop, Suspend, Resume) to control its execution.

Example:

schedule, _ := crong.New("*/5 * * * *", nil)
job := crong.ScheduleFunc(ctx, schedule, crong.ScheduledJobOptions{
	MaxConcurrent: 1,
	TickerReceiveTimeout: 5 * time.Second,
}, func(t time.Time) error {
	fmt.Printf("Job ran at %v\n", t)
	return nil
})

// ... later
job.Stop(context.Background())

func (ScheduledJob) LogValue

func (s ScheduledJob) LogValue() slog.Value

func (*ScheduledJob) Resume

func (s *ScheduledJob) Resume() bool

Resume resumes job execution after a call to Suspend

func (*ScheduledJob) Runtimes

func (s *ScheduledJob) Runtimes() []*JobRuntime

Runtimes returns a slice of the job's runtimes

func (*ScheduledJob) Start

func (s *ScheduledJob) Start(ctx context.Context) error

func (*ScheduledJob) State

func (s *ScheduledJob) State() ScheduleState

func (*ScheduledJob) Stop

func (s *ScheduledJob) Stop(ctx context.Context) bool

Stop stops job execution. After Stop is called, the job cannot be restarted.

func (*ScheduledJob) Suspend

func (s *ScheduledJob) Suspend() bool

Suspend pauses job execution until Resume is called

type ScheduledJobOptions

type ScheduledJobOptions struct {
	// MaxConcurrent is the maximum number of concurrent job executions.
	// If 0, there is no limit
	MaxConcurrent int

	// TickerReceiveTimeout is the maximum time the job's ticker will
	// wait for the job to receive a tick on the Ticker.C channel
	TickerReceiveTimeout time.Duration

	// MaxFailures is the maximum number of times the job can fail
	// before it is stopped. 0=no limit
	MaxFailures int

	// MaxConsecutiveFailures is the maximum number of consecutive
	// times the job can fail before it is stopped. 0=no limit
	MaxConsecutiveFailures int
}

func (ScheduledJobOptions) LogValue

func (s ScheduledJobOptions) LogValue() slog.Value

type Ticker

type Ticker struct {
	C chan time.Time
	// contains filtered or unexported fields
}

Ticker is a cron ticker that sends the current time on the Ticker.C channel when the schedule is triggered

func NewTicker

func NewTicker(
	ctx context.Context,
	schedule *Schedule,
	sendTimeout time.Duration,
) *Ticker

NewTicker creates a new Ticker from a cron expression, sending the current time on Ticker.C when the schedule is triggered. It works similarly to time.Ticker(https://golang.org/pkg/time/#Ticker), but is granular only to the minute. sendTimeout is the maximum time to wait for a receiver to send a tick on the Ticker.C channel (this differs from time.Ticker, allowing some wiggle room for slow receivers). If the provided context is canceled, the ticker will stop automatically.

func (Ticker) LogValue

func (t Ticker) LogValue() slog.Value

func (*Ticker) Stop

func (t *Ticker) Stop()

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL
JackTT - Gopher 🇻🇳