schedule

package
v0.5.1 Latest Latest
Warning

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

Go to latest
Published: Nov 2, 2026 License: MIT Imports: 31 Imported by: 0

Documentation

Overview

Package schedule provides bounded, coordinated scheduled execution. Calendar parsing is independent of runtime ownership and always uses an explicit zone.

Index

Constants

View Source
const MaxInterval = 365 * 24 * time.Hour

Variables

This section is empty.

Functions

func Module

Module registers one Scheduler kernel and typed service. List the borrowed lease manager's provider in requires. Other kernels do not start scheduling.

func Weekdays

func Weekdays() []time.Weekday

Weekdays returns Monday through Friday for Options.Days.

func Weekends

func Weekends() []time.Weekday

Weekends returns Saturday and Sunday for Options.Days.

Types

type Calendar

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

Calendar constructs ordinary declarations in one configured timezone. It owns no worker or registry; execution reuses the existing scheduler and DST policy.

func NewCalendar

func NewCalendar(name temporal.ZoneName) (Calendar, error)

NewCalendar resolves an explicit timezone for calendar declarations.

func (Calendar) Cron

func (c Calendar) Cron(id ID, expression string, handler Handler) (Declaration, error)

Cron declares a cron expression in the calendar's timezone.

func (Calendar) Daily

func (c Calendar) Daily(id ID, handler Handler) (Declaration, error)

Daily declares an occurrence at local midnight.

func (Calendar) DailyAt

func (c Calendar) DailyAt(id ID, text string, handler Handler) (Declaration, error)

DailyAt declares a daily HH:MM occurrence in the calendar's timezone.

func (Calendar) EveryFifteenMinutes

func (c Calendar) EveryFifteenMinutes(id ID, handler Handler) (Declaration, error)

EveryFifteenMinutes declares occurrences at local minutes divisible by fifteen.

func (Calendar) EveryFiveMinutes

func (c Calendar) EveryFiveMinutes(id ID, handler Handler) (Declaration, error)

EveryFiveMinutes declares occurrences at local minutes divisible by five.

func (Calendar) EveryMinute

func (c Calendar) EveryMinute(id ID, handler Handler) (Declaration, error)

EveryMinute declares an occurrence at each local minute boundary.

func (Calendar) EveryTenMinutes

func (c Calendar) EveryTenMinutes(id ID, handler Handler) (Declaration, error)

EveryTenMinutes declares occurrences at local minutes divisible by ten.

func (Calendar) EveryThirtyMinutes

func (c Calendar) EveryThirtyMinutes(id ID, handler Handler) (Declaration, error)

EveryThirtyMinutes declares occurrences at local minutes zero and thirty.

func (Calendar) Hourly

func (c Calendar) Hourly(id ID, handler Handler) (Declaration, error)

Hourly declares an occurrence at the start of each local hour.

func (Calendar) In

func (c Calendar) In(name temporal.ZoneName) (Calendar, error)

In selects an explicit zone without changing this calendar or its declarations.

func (Calendar) LastDayOfMonthAt

func (c Calendar) LastDayOfMonthAt(id ID, text string, handler Handler) (Declaration, error)

LastDayOfMonthAt declares an HH:MM occurrence on the last local day of each month.

func (Calendar) Monthly

func (c Calendar) Monthly(id ID, handler Handler) (Declaration, error)

Monthly declares an occurrence at local midnight on the first of each month.

func (Calendar) Weekly

func (c Calendar) Weekly(id ID, handler Handler) (Declaration, error)

Weekly declares an occurrence at local midnight each Monday.

type CatchUp

type CatchUp struct {
	Window time.Duration
	Max    int
}

CatchUp is opt-in bounded replay on startup, leadership acquisition and missed ticks. Zero skips missed occurrences. At most Max occurrences from Window are considered per schedule per tick; excess backlog is skipped, never accumulated.

func (CatchUp) Validate

func (c CatchUp) Validate() error

Validate checks catch-up window and count bounds. The zero policy disables catch-up.

type Config

type Config struct {
	Group         Group
	Clock         clock.Clock
	Concurrency   int
	MaxHistory    int
	MaxPerTick    int
	PollInterval  time.Duration
	LeadershipTTL time.Duration
	// Grace bounds lateness for schedules without catch-up. Older occurrences are
	// skipped. Process startup itself skips all missed occurrences by default.
	Grace time.Duration
	// CapacityWait lets a due occurrence wait for a free execution slot when
	// Concurrency is exhausted, before it is skipped as CapacityReached.
	CapacityWait time.Duration
	// DrainTimeout is the graceful-shutdown budget. When Run's context ends or
	// Drain is called, admission stops while running tasks keep leadership and
	// overlap leases for up to DrainTimeout; they are then cancelled. Zero
	// cancels immediately. Keep it within the application shutdown timeout.
	DrainTimeout time.Duration
	// Logger receives skipped occurrences and failures with redacted
	// diagnostics. Module uses the application logger when it is nil.
	Logger *slog.Logger
	// Wake optionally replaces the polling ticker for controlled application time.
	// The caller owns the channel; closing it stops Run with an error. Leadership
	// and overlap heartbeats still use real safety time independent of this clock.
	Wake <-chan struct{}
}

Config supplies scheduler identity, timing, capacity, history, and shutdown policy.

func DefaultConfig

func DefaultConfig(group Group) Config

DefaultConfig returns the default scheduler settings for group using the system clock.

func (Config) Validate

func (c Config) Validate() error

Validate checks scheduler identity, clock, capacity and timing bounds, including the leadership duration.

func (Config) ValidateShutdown

func (c Config) ValidateShutdown(timeout time.Duration) error

ValidateShutdown checks that the graceful drain fits within the owning application's shutdown timeout, leaving time to release leadership.

type CursorBackend

type CursorBackend interface {
	// ScheduleCursor returns the latest retained occurrence for key and whether one exists.
	ScheduleCursor(context.Context, lease.Key) (time.Time, bool, error)
	// AdvanceScheduleCursor stores the maximum handled instant for key, with optional expiry after ttl.
	AdvanceScheduleCursor(context.Context, lease.Key, time.Time, time.Duration) error
}

CursorBackend is optional on the scheduler's lease backend. It persists, per schedule, the latest occurrence that completed or was deliberately skipped, so a restart or leadership change resumes catch-up after it instead of replaying completed occurrences. Advance must keep the maximum instant and may expire the entry after ttl. The Redis coordination client implements it; without it the cursor lives only in this scheduler process.

type Declaration

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

Declaration freezes parsed timing and domain callbacks. No I/O or background activity occurs during declaration/registration. Services are constructor-injected.

func Cron

func Cron(id ID, expression string, zone *time.Location, handler Handler) (Declaration, error)

Cron parses an expression in an explicit timezone and declares it with default options.

func Daily

func Daily(id ID, zone *time.Location, handler Handler) (Declaration, error)

Daily declares an occurrence at local midnight in zone.

func DailyAt

func DailyAt(id ID, text string, zone *time.Location, handler Handler) (Declaration, error)

DailyAt declares a daily HH:MM occurrence in zone with default options.

func Define

func Define(id ID, spec Spec, handler Handler, options Options) (Declaration, error)

Define validates a declaration and snapshots its mutable option slices without performing I/O.

func Every

func Every(id ID, every time.Duration, handler Handler) (Declaration, error)

Every declares an elapsed-time interval aligned to the UTC Unix epoch with default options.

func EveryFifteenMinutes

func EveryFifteenMinutes(id ID, zone *time.Location, handler Handler) (Declaration, error)

EveryFifteenMinutes declares occurrences at local minutes divisible by fifteen in zone.

func EveryFiveMinutes

func EveryFiveMinutes(id ID, zone *time.Location, handler Handler) (Declaration, error)

EveryFiveMinutes declares occurrences at local minutes divisible by five in zone.

func EveryMinute

func EveryMinute(id ID, zone *time.Location, handler Handler) (Declaration, error)

EveryMinute and the Every*Minutes helpers run on local wall-clock minute boundaries in zone (for example :00, :05, :10 for EveryFiveMinutes).

func EveryTenMinutes

func EveryTenMinutes(id ID, zone *time.Location, handler Handler) (Declaration, error)

EveryTenMinutes declares occurrences at local minutes divisible by ten in zone.

func EveryThirtyMinutes

func EveryThirtyMinutes(id ID, zone *time.Location, handler Handler) (Declaration, error)

EveryThirtyMinutes declares occurrences at local minutes zero and thirty in zone.

func Hourly

func Hourly(id ID, zone *time.Location, handler Handler) (Declaration, error)

Hourly declares an occurrence at the start of each local hour in zone.

func LastDayOfMonthAt

func LastDayOfMonthAt(id ID, text string, zone *time.Location, handler Handler) (Declaration, error)

LastDayOfMonthAt runs at HH:MM on the last local day of each month.

func Monthly

func Monthly(id ID, zone *time.Location, handler Handler) (Declaration, error)

Monthly declares an occurrence at local midnight on the first of each month in zone.

func Weekly

func Weekly(id ID, zone *time.Location, handler Handler) (Declaration, error)

Weekly declares an occurrence at local midnight each Monday in zone.

func (Declaration) ID

func (d Declaration) ID() ID

ID returns the schedule identifier.

func (Declaration) Options

func (d Declaration) Options() Options

Options returns the execution policy with owned copies of mutable slices.

func (Declaration) Spec

func (d Declaration) Spec() Spec

Spec returns the immutable parsed timing specification.

func (Declaration) Validate

func (d Declaration) Validate() error

Validate checks the identifier, handler, timing specification, and execution options.

func (Declaration) With

func (d Declaration) With(options Options) (Declaration, error)

With returns a validated declaration with replacement options, leaving the receiver unchanged.

type Description

type Description struct {
	ID             ID            `json:"id"`
	Expression     string        `json:"expression"`
	TimeZone       string        `json:"time_zone"`
	Interval       time.Duration `json:"interval,omitzero"`
	Anchor         time.Time     `json:"anchor,omitzero"`
	Timeout        time.Duration `json:"timeout"`
	WithoutOverlap bool          `json:"without_overlap"`
	OverlapTTL     time.Duration `json:"overlap_ttl"`
	Environments   []string      `json:"environments"`
	CatchUp        CatchUp       `json:"catch_up"`
}

Description contains timing and execution policy without callback references, coordination ownership or task history. Interval anchors are explicit UTC.

type Execution

type Execution struct{}

Execution marks the identity type of an individual schedule execution.

type ExecutionID

type ExecutionID = model.ID[Execution]

ExecutionID identifies an individual schedule execution.

type Group

type Group string

Group isolates independent scheduler elections in one namespace. All replicas of a group must deploy the same registry, timing and environment policy.

type Handler

type Handler func(context.Context, Invocation) error

Handler handles one scheduled invocation and must cooperate with context cancellation.

func ConnectionJobTarget

func ConnectionJobTarget[P any](connection *jobs.Connection, definition jobs.Definition[P], queue jobs.Queue, build func(context.Context, Invocation) (P, error)) (Handler, error)

ConnectionJobTarget selects the connection's configured default queue. The bound job and explicit options retain the concrete payload type throughout.

func JobTarget

func JobTarget[P any](dispatcher *jobs.Dispatcher, definition jobs.Definition[P], build func(context.Context, Invocation) (P, error)) (Handler, error)

JobTarget preserves definition routing. Each occurrence becomes a stable queue execution ID; retention must cover the recovery/catch-up window.

type ID

type ID string

ID is the semantic identifier of a registered schedule.

type Invocation

type Invocation struct {
	Schedule   ID
	Occurrence OccurrenceID
	IntendedAt time.Time
	Origin     attribution.Origin
}

Invocation carries stable occurrence identity, not a live service container. The same namespace/group/schedule/UTC instant has the same ID after failover. It is an idempotency key, not proof of exactly-once business effects.

func Current

func Current(ctx context.Context) (Invocation, bool)

Current is valid only while this scheduler owns the active callback. A saved context ceases to represent live execution after all hooks/handlers return.

type Occurrence

type Occurrence struct{}

Occurrence marks the identity type of an intended schedule occurrence.

type OccurrenceID

type OccurrenceID = model.ID[Occurrence]

OccurrenceID identifies a stable schedule occurrence across scheduler failover.

type Options

type Options struct {
	Timeout               time.Duration
	WithoutOverlap        bool
	OverlapTTL            time.Duration
	Environments          []string
	CatchUp               CatchUp
	Days                  []time.Weekday
	Between               Window
	LastDayOfMonth        bool
	When                  Predicate
	EvenInMaintenanceMode bool
	Before                Handler
	After                 Handler
	Failed                func(context.Context, Invocation, error) error
}

Options configure one declaration. Days, Between and LastDayOfMonth narrow the spec in its zone without callbacks: a filtered occurrence advances the schedule silently. When runs as an owned callback before the Before hook; a false result records the occurrence as Skipped with reason Filtered (not logged as a failure), and an error fails it as a hook failure. EvenInMaintenanceMode keeps admitting this schedule while the application's maintenance gate is paused or draining.

func DefaultOptions

func DefaultOptions() Options

DefaultOptions returns the default execution timeout and overlap lease duration.

func (Options) Validate

func (o Options) Validate() error

Validate checks timeout, overlap, catch-up, calendar-filter, and environment bounds.

type Predicate

type Predicate func(context.Context, Invocation) (bool, error)

Predicate decides at execution time whether an occurrence runs. It is an owned callback: it receives the invocation context and must honor it.

type Reason

type Reason string

Reason classifies an execution outcome without retaining arbitrary callback errors.

const (
	NoReason           Reason = ""
	HandlerFailed      Reason = "handler_failed"
	HookFailed         Reason = "hook_failed"
	Panicked           Reason = "panicked"
	TimedOut           Reason = "timed_out"
	Stopped            Reason = "stopped"
	LeadershipLost     Reason = "leadership_lost"
	OverlapLost        Reason = "overlap_lost"
	OverlapBusy        Reason = "overlap_busy"
	CoordinationFailed Reason = "coordination_failed"
	CapacityReached    Reason = "capacity_reached"
	Missed             Reason = "missed"
	BacklogLimited     Reason = "backlog_limited"
	ClockFailed        Reason = "clock_failed"
	// Filtered records an occurrence whose Options.When predicate declined it.
	Filtered Reason = "filtered"
)

type Record

type Record struct {
	Execution  ExecutionID
	Invocation Invocation
	State      State
	Reason     Reason
	StartedAt  time.Time
	FinishedAt time.Time
}

Record is bounded process-local operational history, not a durable ledger. It deliberately contains classifications instead of arbitrary callback errors.

type Registry

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

Registry retains validated declarations with owned option slices.

func NewRegistry

func NewRegistry(declarations ...Declaration) (*Registry, error)

NewRegistry validates and snapshots 1 to 4096 declarations, rejecting duplicate schedule IDs.

func (*Registry) Describe

func (r *Registry) Describe() []Description

Describe returns owned descriptions sorted by schedule ID, without inspecting runtime state.

type ScheduleStatus

type ScheduleStatus struct {
	ID      ID
	Enabled bool
	Next    time.Time
	Active  int
}

ScheduleStatus reports one schedule's enabled state, next occurrence, and active count.

type Scheduler

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

Scheduler borrows the shared lease manager. Run is single-use. Cancelling Run's context, or Drain, stops admission and lets running tasks finish within DrainTimeout while leadership is kept; Stop cancels tasks immediately. Done waits for their actual exit. Keep the manager/backend alive until Done; Module supplies foundation dependency order. A Scheduler must not be copied.

func New

func New(manager *lease.Manager, registry *Registry, config Config) (*Scheduler, error)

New validates settings and binds coordination declarations to the borrowed lease manager. It constructs the scheduler without starting execution.

func (*Scheduler) Describe

func (s *Scheduler) Describe() []Description

Describe reads immutable declarations; it does not inspect leases or run work.

func (*Scheduler) Done

func (s *Scheduler) Done() <-chan struct{}

Done closes after scheduler shutdown waits for actual task exit. A nil scheduler returns nil.

func (*Scheduler) Drain

func (s *Scheduler) Drain(ctx context.Context) error

Drain stops admission and lets running tasks finish within DrainTimeout before cancelling them. ctx bounds only the caller's wait.

func (*Scheduler) Run

func (s *Scheduler) Run(ctx context.Context) error

Run drops caller context values; only cancellation is inherited. Tasks receive explicit system attribution. Leadership uncertainty cancels that epoch's tasks and retries election after a bounded polling interval, never executing unfenced. Parent cancellation starts a graceful drain.

func (*Scheduler) RunNow

func (s *Scheduler) RunNow(ctx context.Context, id ID) (Record, error)

RunNow invokes one registered schedule immediately, outside leadership and its timing, for operators and tests (the `schedule test` command). The When predicate, hooks, timeout and WithoutOverlap protection apply as for a scheduled occurrence; calendar filters, catch-up cursors, history and the running scheduler's Concurrency do not. The occurrence is the current millisecond. The returned record classifies the outcome; the error reports only invalid use, never the handler's own failure.

func (*Scheduler) Snapshot

func (s *Scheduler) Snapshot() Snapshot

Snapshot copies status/history under one lock and can be used from callbacks. Authorize any operational endpoint exposing attribution or occurrence IDs.

func (*Scheduler) Stop

func (s *Scheduler) Stop(ctx context.Context) error

Stop cancels running tasks immediately and bounds only the caller's wait.

type Snapshot

type Snapshot struct {
	Running                bool
	Stopping               bool
	Leader                 bool
	Active                 int
	LeadershipAcquisitions uint64
	LeadershipLosses       uint64
	CoordinationFailures   uint64
	Schedules              []ScheduleStatus
	History                []Record
}

Snapshot is an owned view of scheduler status and bounded process-local history.

type Spec

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

Spec is an immutable parsed cron or anchored interval. Calendar schedules use local wall time: nonexistent DST times are skipped, repeated times each denote a separate UTC occurrence. Intervals measure elapsed time and ignore DST.

func Interval

func Interval(every time.Duration) (Spec, error)

Interval aligns whole-millisecond occurrences to the UTC Unix epoch. Alignment survives process restarts; use IntervalFrom for an explicit phase/first instant.

func IntervalFrom

func IntervalFrom(every time.Duration, anchor time.Time) (Spec, error)

IntervalFrom constructs a whole-millisecond interval with an explicit UTC-normalized anchor.

func ParseCron

func ParseCron(source string, zone *time.Location) (Spec, error)

ParseCron accepts five fields (minute first) or six (second first). No year, embedded timezone or @ directives are accepted. Day-of-month/day-of-week use standard cron OR semantics when both are restricted. Use temporal.ParseTimeZone to resolve configuration strings before calling this constructor.

func (Spec) Next

func (s Spec) Next(after time.Time) (time.Time, error)

Next returns a strictly later occurrence. Missing means the calendar has no future occurrence within its bounded search/range; it never loops indefinitely.

func (Spec) String

func (s Spec) String() string

String returns the normalized cron expression or interval duration text.

func (Spec) TimeZone

func (s Spec) TimeZone() string

TimeZone returns the calendar zone, UTC for an interval, or empty text for a zero spec.

func (Spec) Validate

func (s Spec) Validate() error

Validate rejects an uninitialized timing specification with fault.Invalid.

type State

type State string

State classifies a schedule execution or skipped occurrence.

const (
	Running   State = "running"
	Succeeded State = "succeeded"
	Failed    State = "failed"
	Cancelled State = "cancelled"
	Skipped   State = "skipped"
)

type Window

type Window struct {
	From, To temporal.Time
	Unless   bool
}

Window limits occurrences to local wall-clock times in [From, To) in the spec's zone (UTC for intervals). From after To wraps midnight, for example 22:00 to 06:00. Unless inverts the window: occurrences inside it are skipped. The zero Window allows every time.

func Between

func Between(from, to temporal.Time) Window

Between returns a window admitting local times in [from, to).

func UnlessBetween

func UnlessBetween(from, to temporal.Time) Window

UnlessBetween returns a window skipping local times in [from, to).

func (Window) Validate

func (w Window) Validate() error

Validate rejects an enabled window whose endpoints are equal. The zero window is valid.

Directories

Path Synopsis
Package command supplies the application-owned `schedule test` operation.
Package command supplies the application-owned `schedule test` operation.

Jump to

Keyboard shortcuts

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