Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat:custom-cron interface for own custom cron implimentation #834

Merged
merged 7 commits into from
Feb 25, 2025
Merged
85 changes: 71 additions & 14 deletions job.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
id uuid.UUID
name string
tags []string
cron Cron
jobSchedule

// as some jobs may queue up, it's possible to
Expand Down Expand Up @@ -104,6 +105,20 @@
runCount uint
}

// -----------------------------------------------
// -----------------------------------------------
// --------------- Custom Cron -------------------
// -----------------------------------------------
// -----------------------------------------------

// Cron defines the interface that must be
// implemented to provide a custom cron implementation for
// the job. Pass in the implementation using the JobOption WithCronImplementation.
type Cron interface {
IsValid(crontab string, location *time.Location, now time.Time) error
Next(lastRun time.Time) time.Time
}

// -----------------------------------------------
// -----------------------------------------------
// --------------- Job Variants ------------------
Expand All @@ -116,29 +131,37 @@
setup(j *internalJob, l *time.Location, now time.Time) error
}

var _ JobDefinition = (*cronJobDefinition)(nil)
// Default cron implementation

type cronJobDefinition struct {
crontab string
withSeconds bool
func newDefaultCronImplementation(withSeconds bool) Cron {
return &defaultCron{
withSeconds: withSeconds,
}
}

func (c cronJobDefinition) setup(j *internalJob, location *time.Location, now time.Time) error {
var _ Cron = (*defaultCron)(nil)

type defaultCron struct {
cronSchedule cron.Schedule
withSeconds bool
}

func (r *defaultCron) IsValid(crontab string, location *time.Location, now time.Time) error {
var withLocation string
if strings.HasPrefix(c.crontab, "TZ=") || strings.HasPrefix(c.crontab, "CRON_TZ=") {
withLocation = c.crontab
if strings.HasPrefix(crontab, "TZ=") || strings.HasPrefix(crontab, "CRON_TZ=") {
withLocation = crontab
} else {
// since the user didn't provide a timezone default to the location
// passed in by the scheduler. Default: time.Local
withLocation = fmt.Sprintf("CRON_TZ=%s %s", location.String(), c.crontab)
withLocation = fmt.Sprintf("CRON_TZ=%s %s", location.String(), crontab)
}

var (
cronSchedule cron.Schedule
err error
)

if c.withSeconds {
if r.withSeconds {
p := cron.NewParser(cron.SecondOptional | cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow | cron.Descriptor)
cronSchedule, err = p.Parse(withLocation)
} else {
Expand All @@ -150,8 +173,32 @@
if cronSchedule.Next(now).IsZero() {
return ErrCronJobInvalid
}
r.cronSchedule = cronSchedule
return nil
}

j.jobSchedule = &cronJob{cronSchedule: cronSchedule}
func (r *defaultCron) Next(lastRun time.Time) time.Time {
return r.cronSchedule.Next(lastRun)
}

// default cron job implimentation
var _ JobDefinition = (*cronJobDefinition)(nil)

type cronJobDefinition struct {
crontab string
cron Cron
}

func (c cronJobDefinition) setup(j *internalJob, location *time.Location, now time.Time) error {
if j.cron != nil {
c.cron = j.cron
}

if err := c.cron.IsValid(c.crontab, location, now); err != nil {
return err
}

j.jobSchedule = &cronJob{crontab: c.crontab, cronSchedule: c.cron}
return nil
}

Expand All @@ -163,8 +210,8 @@
// `CRON_TZ=America/Chicago * * * * *`
func CronJob(crontab string, withSeconds bool) JobDefinition {
return cronJobDefinition{
crontab: crontab,
withSeconds: withSeconds,
crontab: crontab,
cron: newDefaultCronImplementation(withSeconds),
}
}

Expand Down Expand Up @@ -608,6 +655,15 @@
}
}

// WithCronImplementation sets the custom Cron implementation for the job.
// This is only utilized for the CronJob type.
func WithCronImplementation(c Cron) JobOption {
return func(j *internalJob, _ time.Time) error {
j.cron = c
return nil
}
}

// WithSingletonMode keeps the job from running again if it is already running.
// This is useful for jobs that should not overlap, and that occasionally
// (but not consistently) run longer than the interval between job runs.
Expand Down Expand Up @@ -818,11 +874,12 @@
var _ jobSchedule = (*cronJob)(nil)

type cronJob struct {
cronSchedule cron.Schedule
crontab string
cronSchedule Cron
}

func (j *cronJob) next(lastRun time.Time) time.Time {
return j.cronSchedule.Next(lastRun)
return j.cronSchedule.Next(j.crontab, lastRun)

Check failure on line 882 in job.go

View workflow job for this annotation

GitHub Actions / lint and test (1.21)

too many arguments in call to j.cronSchedule.Next

Check failure on line 882 in job.go

View workflow job for this annotation

GitHub Actions / lint and test (1.21)

too many arguments in call to j.cronSchedule.Next

Check failure on line 882 in job.go

View workflow job for this annotation

GitHub Actions / lint and test (1.22)

too many arguments in call to j.cronSchedule.Next

Check failure on line 882 in job.go

View workflow job for this annotation

GitHub Actions / lint and test (1.22)

too many arguments in call to j.cronSchedule.Next
}

var _ jobSchedule = (*durationJob)(nil)
Expand Down
Loading