go.bigb.es/auxilia

v0.5.0
Doc Versions Source

Documentation

Package chronos is an in-process, cron-like scheduler. It exists to run recurring work inside a single Go program without an external scheduler or a separate process: jobs share one supervisor, one concurrency budget, and the program's own context for shutdown.

A job pairs a schedule with a JobFunc. Build one of two ways:

  • NewCron parses a cron spec (5- or 6-field, an @-macro, or "@every <dur>").
  • NewEvery fires on a fixed interval expressed as one or more Span values, which are calendar-aware: months and years respect month lengths, leap years, and daylight saving.

Refine a job with chained builders before registering it: Job.Delta (per-fire jitter), Job.Timeout (cancel a slow run), Job.SlotWait (how long to wait for a concurrency slot), Job.Name, and Job.OnOverlap (OverlapSkip or OverlapAllow).

A Supervisor owns the jobs and their lifecycle. Build it with New and [Option]s, register jobs with Supervisor.Add, then call Supervisor.Start with a context and Supervisor.Stop to drain. The supervisor bounds total concurrency across all jobs with WithMaxInflight: when the budget is full, WithSlotWait (or the per-job Job.SlotWait) decides whether a fire sheds, waits, or waits then sheds.

Inside a run, the JobFunc receives a ChronosContext. It embeds the run's context.Context (cancelled on timeout or shutdown) and lets the job register sibling jobs with ChronosContext.Add or retire itself with ChronosContext.Deactivate without cancelling the current run.

Index

Variables

Unit spans. Sub-day units live in Dur; Day and Week are calendar days; Month and Year are calendar units resolved by time.Time.AddDate.

Types

T type ChronosContext

src
type ChronosContext struct {
	context.Context // the per-run context; promotes Deadline/Done/Err/Value (IV11)
	// contains filtered or unexported fields
}

ChronosContext is the handle a JobFunc receives on each run. It embeds the per-run context.Context so Deadline/Done/Err/Value reflect the run's cancellation (IV11), and offers methods to log, register sibling jobs, and deactivate jobs at runtime.

m func (*ChronosContext) Add

src
func (c *ChronosContext) Add(j *Job) error

Add registers a new Job with the running supervisor, surfacing any stashed build error (IV6).

m func (*ChronosContext) Deactivate

src
func (c *ChronosContext) Deactivate()

Deactivate retires the running job (by name), stopping its further fires without cancelling this run (IV13).

m func (*ChronosContext) DeactivateJob

src
func (c *ChronosContext) DeactivateJob(n string) bool

DeactivateJob retires the named job, reporting whether it existed.

m func (*ChronosContext) Logger

src
func (c *ChronosContext) Logger() *slog.Logger

Logger returns the supervisor's logger.

m func (*ChronosContext) Name

src
func (c *ChronosContext) Name() string

Name returns the running job's name.

T type Clock

src
type Clock interface {
	// Now returns the current time.
	Now() time.Time
	// NewTimer returns a [Timer] that fires once after d.
	NewTimer(d time.Duration) Timer
}

Clock is the injectable-time seam: it provides the current time and creates timers, so callers can substitute a fake clock in tests.

T type Job

src
type Job struct {
	// contains filtered or unexported fields
}

Job is a schedule paired with the function to run and per-job options. Construct one with NewCron or NewEvery, then refine it with the chained builders (Job.Delta, Job.Timeout, etc.).

Construction errors are stashed in buildErr rather than panicked (IV6); they surface when the job is registered. The Must* variants panic instead. The paired *Set bools record whether Job.Timeout and Job.SlotWait were called, so the supervisor can tell an explicit zero from "unset" (GPC1).

f func MustCron

src
func MustCron(spec string, fn JobFunc) *Job

MustCron is NewCron that panics on a malformed spec.

f func MustEvery

src
func MustEvery(fn JobFunc, every ...Span) *Job

MustEvery is NewEvery that panics on an empty or all-zero span.

f func NewCron

src
func NewCron(spec string, fn JobFunc) *Job

NewCron builds a Job from a cron spec (5- or 6-field, an @-macro, or "@every <dur>"). A malformed spec is stashed in buildErr (IV6) rather than panicked; see MustCron for the panicking variant.

f func NewEvery

src
func NewEvery(fn JobFunc, every ...Span) *Job

NewEvery builds a Job that fires every total of the given spans. The spans are summed component-wise (AS1); a non-positive total (empty, all-zero, or any negative component) is rejected via buildErr (IV6), since [everySchedule] requires a forward-moving span to honour the strictly-after contract (IV1).

m func (*Job) Delta

src
func (j *Job) Delta(d time.Duration) *Job

Delta offsets each fire by d relative to the schedule's nominal time.

m func (*Job) Name

src
func (j *Job) Name(name string) *Job

Name sets the job's name, used for logging and self-deactivation.

m func (*Job) OnOverlap

src
func (j *Job) OnOverlap(p OverlapPolicy) *Job

OnOverlap sets the OverlapPolicy applied when a fire arrives during a still-running prior invocation.

m func (*Job) SlotWait

src
func (j *Job) SlotWait(d time.Duration) *Job

SlotWait bounds how long a fire waits for an in-flight run before the overlap policy applies. Calling it marks slotWait as explicitly set.

m func (*Job) Timeout

src
func (j *Job) Timeout(d time.Duration) *Job

Timeout caps each run at d; the run's ChronosContext is cancelled when it elapses. Calling it marks the timeout as explicitly set (GPC1).

T type JobFunc

src
type JobFunc func(cc *ChronosContext) error

JobFunc is the work a Job runs on each fire. It receives a ChronosContext scoped to the run (cancelled on timeout or shutdown) and returns an error that the supervisor records.

T type Option

src
type Option func(*supervisorOpts)

Option configures a Supervisor at construction time.

f func WithClock

src

WithClock injects a Clock, chiefly for deterministic tests. The default is the package wall clock.

f func WithJitter

src

WithJitter injects the source of per-fire jitter spread (PC3): given a job's Job.Delta, it returns the offset added to that fire, expected in [0, max). The default draws uniformly from math/rand/v2. Chiefly for deterministic tests.

f func WithLocation

src

WithLocation sets the time zone schedules are evaluated in (AS2). The default is time.Local.

f func WithLogger

src

WithLogger sets the logger used for all supervisor logging (PC4). The default is slog.Default.

f func WithMaxInflight

src

WithMaxInflight bounds the number of runs executing concurrently across all jobs (IV9). n<=0 is unlimited.

f func WithSlotWait

src

WithSlotWait sets the default slot-wait applied to jobs that do not set their own with Job.SlotWait (IV10): 0 sheds when full, <0 waits indefinitely, >0 waits up to the duration.

f func WithTimeout

src

WithTimeout sets the default per-run timeout applied to jobs that do not set their own with Job.Timeout. Zero means no timeout.

T type OverlapPolicy

src
type OverlapPolicy int

OverlapPolicy decides what happens when a Job's fire time arrives while its previous run is still in flight.

const (
	// OverlapSkip drops the new fire when the prior run is still active.
	OverlapSkip OverlapPolicy = iota
	// OverlapAllow starts the new run concurrently with the prior one.
	OverlapAllow
)

T type Schedule

src
type Schedule interface {
	// Next returns the next fire time strictly after the given time, or
	// the zero [time.Time] to mean "no further runs". The argument is
	// the anchor; for a recurring schedule it is typically the time of
	// the previous fire (or loop start for the first call).
	Next(after time.Time) time.Time
}

Schedule computes fire times. It is the single scheduling seam: it never sleeps and has no side effects.

T type Span

src
type Span struct {
	Years  int
	Months int
	Days   int
	Dur    time.Duration
}

Span is a calendar-aware amount of time. Calendar components (Years, Months, Days) are added via time.Time.AddDate, which makes them sensitive to month lengths, leap years, and daylight saving; the sub-day Dur is added as an absolute time.Duration.

f func Days

src
func Days(n int) Span

Days returns a Span of n calendar days.

f func Hours

src
func Hours(n int) Span

Hours returns a Span of n hours of absolute duration.

f func Minutes

src
func Minutes(n int) Span

Minutes returns a Span of n minutes of absolute duration.

f func Months

src
func Months(n int) Span

Months returns a Span of n calendar months.

f func Seconds

src
func Seconds(n int) Span

Seconds returns a Span of n seconds of absolute duration.

f func Weeks

src
func Weeks(n int) Span

Weeks returns a Span of n weeks (7n calendar days).

f func Years

src
func Years(n int) Span

Years returns a Span of n calendar years.

m func (Span) Add

src
func (s Span) Add(o Span) Span

Add returns the component-wise sum of s and o.

m func (Span) IsZero

src
func (s Span) IsZero() bool

IsZero reports whether s has no calendar and no duration component.

m func (Span) Mul

src
func (s Span) Mul(n int) Span

Mul returns s with every component scaled by n.

T type Supervisor

src
type Supervisor struct {
	// contains filtered or unexported fields
}

Supervisor owns a set of [Job]s and runs each on its own scheduling loop. It bounds total concurrency with a shared semaphore (IV9), drains in-flight runs on Supervisor.Stop (IV5), and never blocks a loop on a run, slot acquisition, or timeout (IV3). The zero value is not usable; construct one with New.

f func New

src
func New(opts ...Option) *Supervisor

New builds a Supervisor from the given [Option]s. Defaults: location time.Local, logger slog.Default, the package wall Clock, no timeout, unlimited concurrency, and slot-wait 0 (shed when full).

m func (*Supervisor) Add

src
func (s *Supervisor) Add(j *Job) error

Add registers a Job, surfacing its stashed build error (IV6) and rejecting duplicate names. If the supervisor is already running, the job's loop starts immediately (IV12).

m func (*Supervisor) Deactivate

src
func (s *Supervisor) Deactivate(name string) bool

Deactivate retires the named job, stopping its future fires without cancelling any in-flight run (IV13). It reports whether the job existed and is idempotent.

m func (*Supervisor) Jobs

src
func (s *Supervisor) Jobs() []string

Jobs returns the names of the registered jobs, sorted.

m func (*Supervisor) Start

src
func (s *Supervisor) Start(ctx context.Context) error

Start begins scheduling every registered job, deriving the run root from ctx (PC2). It errors if already started.

m func (*Supervisor) Stop

src
func (s *Supervisor) Stop(ctx context.Context) error

Stop cancels every loop and run, then waits for in-flight runs to drain, bounded by ctx (IV5, PC2). It is idempotent and returns ctx.Err() if the drain outlasts ctx.

T type Timer

src
type Timer interface {
	// C returns the channel on which the fire time is delivered.
	C() <-chan time.Time
	// Stop prevents the timer from firing. It reports whether the call
	// stops the timer (false if it has already fired or been stopped).
	Stop() bool
}

Timer is a single-shot timer created by a Clock.