129 lines
3.8 KiB
Go
129 lines
3.8 KiB
Go
package scheduler
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/influxdata/cron"
|
|
"github.com/influxdata/influxdb/v2/task/options"
|
|
)
|
|
|
|
// ID duplicates the influxdb ID so users of the scheduler don't have to
|
|
// import influxdb for the ID.
|
|
type ID uint64
|
|
|
|
// Executor is a system used by the scheduler to actually execute the scheduleable item.
|
|
type Executor interface {
|
|
// Execute is used to execute run's for any schedulable object.
|
|
// the executor can go through manual runs, clean currently running, and then create a new run based on `now`.
|
|
// if Now is zero we can just do the first 2 steps (This is how we would trigger manual runs).
|
|
// Errors returned from the execute request imply that this attempt has failed and
|
|
// should be put back in scheduler and re executed at a alter time. We will add scheduler specific errors
|
|
// so the time can be configurable.
|
|
Execute(ctx context.Context, id ID, scheduledFor time.Time, runAt time.Time) error
|
|
}
|
|
|
|
// Schedulable is the interface that encapsulates work that
|
|
// is to be executed on a specified schedule.
|
|
type Schedulable interface {
|
|
// ID is the unique identifier for this Schedulable
|
|
ID() ID
|
|
|
|
// Schedule defines the frequency for which this Schedulable should be
|
|
// queued for execution.
|
|
Schedule() Schedule
|
|
|
|
// Offset defines a negative or positive duration that should be added
|
|
// to the scheduled time, resulting in the instance running earlier or later
|
|
// than the scheduled time.
|
|
Offset() time.Duration
|
|
|
|
// LastScheduled specifies last time this Schedulable was queued
|
|
// for execution.
|
|
LastScheduled() time.Time
|
|
}
|
|
|
|
// SchedulableService encapsulates the work necessary to schedule a job
|
|
type SchedulableService interface {
|
|
|
|
// UpdateLastScheduled notifies the instance that it was scheduled for
|
|
// execution at the specified time
|
|
UpdateLastScheduled(ctx context.Context, id ID, t time.Time) error
|
|
}
|
|
|
|
func NewSchedule(unparsed string, lastScheduledAt time.Time) (Schedule, time.Time, error) {
|
|
lastScheduledAt = lastScheduledAt.UTC().Truncate(time.Second)
|
|
c, err := cron.ParseUTC(unparsed)
|
|
if err != nil {
|
|
return Schedule{}, lastScheduledAt, err
|
|
}
|
|
|
|
unparsed = strings.TrimSpace(unparsed)
|
|
|
|
// Align create to the hour/minute
|
|
if strings.HasPrefix(unparsed, "@every ") {
|
|
everyString := strings.TrimSpace(strings.TrimPrefix(unparsed, "@every "))
|
|
every := options.Duration{}
|
|
err := every.Parse(everyString)
|
|
if err != nil {
|
|
// We cannot align a invalid time
|
|
return Schedule{c}, lastScheduledAt, nil
|
|
}
|
|
|
|
// drop nanoseconds
|
|
lastScheduledAt = time.Unix(lastScheduledAt.UTC().Unix(), 0).UTC()
|
|
everyDur, err := every.DurationFrom(lastScheduledAt)
|
|
if err != nil {
|
|
return Schedule{c}, lastScheduledAt, nil
|
|
}
|
|
|
|
// and align
|
|
lastScheduledAt = lastScheduledAt.Truncate(everyDur).Truncate(time.Second)
|
|
}
|
|
|
|
return Schedule{c}, lastScheduledAt, err
|
|
}
|
|
|
|
// Schedule is an object a valid schedule of runs
|
|
type Schedule struct {
|
|
cron cron.Parsed
|
|
}
|
|
|
|
// Next returns the next time after from that a schedule should trigger on.
|
|
func (s Schedule) Next(from time.Time) (time.Time, error) {
|
|
return cron.Parsed(s.cron).Next(from)
|
|
}
|
|
|
|
// ValidSchedule returns an error if the cron string is invalid.
|
|
func ValidateSchedule(c string) error {
|
|
_, err := cron.ParseUTC(c)
|
|
return err
|
|
}
|
|
|
|
// Scheduler is a example interface of a Scheduler.
|
|
// // todo(lh): remove this once we start building the actual scheduler
|
|
type Scheduler interface {
|
|
|
|
// Schedule adds the specified task to the scheduler.
|
|
Schedule(task Schedulable) error
|
|
|
|
// Release removes the specified task from the scheduler.
|
|
Release(taskID ID) error
|
|
}
|
|
|
|
type ErrUnrecoverable struct {
|
|
error
|
|
}
|
|
|
|
func (e *ErrUnrecoverable) Error() string {
|
|
if e.error != nil {
|
|
return "error unrecoverable error on task run " + e.error.Error()
|
|
}
|
|
return "error unrecoverable error on task run"
|
|
}
|
|
|
|
func (e *ErrUnrecoverable) Unwrap() error {
|
|
return e.error
|
|
}
|