Documentation
¶
Overview ¶
Package schedule provides bounded, coordinated scheduled execution. Calendar parsing is independent of runtime ownership and always uses an explicit zone.
Index ¶
- Constants
- func Module(name foundation.ProviderID, key foundation.Key[*Scheduler], config Config, ...) foundation.Module
- func Weekdays() []time.Weekday
- func Weekends() []time.Weekday
- type Calendar
- func (c Calendar) Cron(id ID, expression string, handler Handler) (Declaration, error)
- func (c Calendar) Daily(id ID, handler Handler) (Declaration, error)
- func (c Calendar) DailyAt(id ID, text string, handler Handler) (Declaration, error)
- func (c Calendar) EveryFifteenMinutes(id ID, handler Handler) (Declaration, error)
- func (c Calendar) EveryFiveMinutes(id ID, handler Handler) (Declaration, error)
- func (c Calendar) EveryMinute(id ID, handler Handler) (Declaration, error)
- func (c Calendar) EveryTenMinutes(id ID, handler Handler) (Declaration, error)
- func (c Calendar) EveryThirtyMinutes(id ID, handler Handler) (Declaration, error)
- func (c Calendar) Hourly(id ID, handler Handler) (Declaration, error)
- func (c Calendar) In(name temporal.ZoneName) (Calendar, error)
- func (c Calendar) LastDayOfMonthAt(id ID, text string, handler Handler) (Declaration, error)
- func (c Calendar) Monthly(id ID, handler Handler) (Declaration, error)
- func (c Calendar) Weekly(id ID, handler Handler) (Declaration, error)
- type CatchUp
- type Config
- type CursorBackend
- type Declaration
- func Cron(id ID, expression string, zone *time.Location, handler Handler) (Declaration, error)
- func Daily(id ID, zone *time.Location, handler Handler) (Declaration, error)
- func DailyAt(id ID, text string, zone *time.Location, handler Handler) (Declaration, error)
- func Define(id ID, spec Spec, handler Handler, options Options) (Declaration, error)
- func Every(id ID, every time.Duration, handler Handler) (Declaration, error)
- func EveryFifteenMinutes(id ID, zone *time.Location, handler Handler) (Declaration, error)
- func EveryFiveMinutes(id ID, zone *time.Location, handler Handler) (Declaration, error)
- func EveryMinute(id ID, zone *time.Location, handler Handler) (Declaration, error)
- func EveryTenMinutes(id ID, zone *time.Location, handler Handler) (Declaration, error)
- func EveryThirtyMinutes(id ID, zone *time.Location, handler Handler) (Declaration, error)
- func Hourly(id ID, zone *time.Location, handler Handler) (Declaration, error)
- func LastDayOfMonthAt(id ID, text string, zone *time.Location, handler Handler) (Declaration, error)
- func Monthly(id ID, zone *time.Location, handler Handler) (Declaration, error)
- func Weekly(id ID, zone *time.Location, handler Handler) (Declaration, error)
- type Description
- type Execution
- type ExecutionID
- type Group
- type Handler
- type ID
- type Invocation
- type Occurrence
- type OccurrenceID
- type Options
- type Predicate
- type Reason
- type Record
- type Registry
- type ScheduleStatus
- type Scheduler
- func (s *Scheduler) Describe() []Description
- func (s *Scheduler) Done() <-chan struct{}
- func (s *Scheduler) Drain(ctx context.Context) error
- func (s *Scheduler) Run(ctx context.Context) error
- func (s *Scheduler) RunNow(ctx context.Context, id ID) (Record, error)
- func (s *Scheduler) Snapshot() Snapshot
- func (s *Scheduler) Stop(ctx context.Context) error
- type Snapshot
- type Spec
- type State
- type Window
Constants ¶
const MaxInterval = 365 * 24 * time.Hour
Variables ¶
This section is empty.
Functions ¶
func Module ¶
func Module(name foundation.ProviderID, key foundation.Key[*Scheduler], config Config, requires []foundation.ProviderID, construct func(foundation.Resolver) (*lease.Manager, []Declaration, error)) foundation.Module
Module registers one Scheduler kernel and typed service. List the borrowed lease manager's provider in requires. Other kernels do not start scheduling.
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 (Calendar) EveryFifteenMinutes ¶
func (c Calendar) EveryFifteenMinutes(id ID, handler Handler) (Declaration, error)
func (Calendar) EveryFiveMinutes ¶
func (c Calendar) EveryFiveMinutes(id ID, handler Handler) (Declaration, error)
func (Calendar) EveryMinute ¶
func (c Calendar) EveryMinute(id ID, handler Handler) (Declaration, error)
func (Calendar) EveryTenMinutes ¶
func (c Calendar) EveryTenMinutes(id ID, handler Handler) (Declaration, error)
func (Calendar) EveryThirtyMinutes ¶
func (c Calendar) EveryThirtyMinutes(id ID, handler Handler) (Declaration, error)
func (Calendar) In ¶
In selects an explicit zone without changing this calendar or its declarations.
func (Calendar) LastDayOfMonthAt ¶
type CatchUp ¶
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.
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{}
}
func DefaultConfig ¶
type CursorBackend ¶
type CursorBackend interface {
ScheduleCursor(context.Context, lease.Key) (time.Time, bool, error)
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 EveryFifteenMinutes ¶
func EveryFiveMinutes ¶
func EveryMinute ¶
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 EveryThirtyMinutes ¶
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 (Declaration) ID ¶
func (d Declaration) ID() ID
func (Declaration) Options ¶
func (d Declaration) Options() Options
func (Declaration) Spec ¶
func (d Declaration) Spec() Spec
func (Declaration) Validate ¶
func (d Declaration) Validate() error
func (Declaration) With ¶
func (d Declaration) With(options Options) (Declaration, error)
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 ExecutionID ¶
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
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 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.
type Occurrence ¶
type Occurrence struct{}
type OccurrenceID ¶
type OccurrenceID = model.ID[Occurrence]
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
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
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
}
func NewRegistry ¶
func NewRegistry(declarations ...Declaration) (*Registry, error)
func (*Registry) Describe ¶
func (r *Registry) Describe() []Description
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 (*Scheduler) Describe ¶
func (s *Scheduler) Describe() []Description
Describe reads immutable declarations; it does not inspect leases or run work.
func (*Scheduler) Drain ¶
Drain stops admission and lets running tasks finish within DrainTimeout before cancelling them. ctx bounds only the caller's wait.
func (*Scheduler) Run ¶
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 ¶
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.
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 ¶
Interval aligns whole-millisecond occurrences to the UTC Unix epoch. Alignment survives process restarts; use IntervalFrom for an explicit phase/first instant.
func ParseCron ¶
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.
type Window ¶
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 UnlessBetween ¶
UnlessBetween returns a window skipping local times in [from, to).