Documentation
¶
Overview ¶
Package cron schedules context-aware jobs from cron expressions or Schedule implementations. Stop cancels running jobs; Drain waits without canceling.
Index ¶
- Variables
- func Between(s Schedule, start, end time.Time) iter.Seq[time.Time]
- func IsTriggered(s Schedule) bool
- func NextN(s Schedule, from time.Time, n int) []time.Time
- func ValidateSpec(spec string) error
- func ValidateSpecWith(spec string, p Parser) error
- type AlignedDelay
- type CanceledFireEvent
- type Claimer
- type ConstantDelay
- type Cron
- func (c *Cron) Add(spec string, j Job, opts ...EntryOption) (EntryID, error)
- func (c *Cron) AddSchedule(s Schedule, j Job, opts ...EntryOption) (EntryID, error)
- func (c *Cron) Drain(ctx context.Context) error
- func (c *Cron) Entries() iter.Seq[Entry]
- func (c *Cron) Entry(id EntryID) (Entry, bool)
- func (c *Cron) Pause(id EntryID) bool
- func (c *Cron) Remove(id EntryID) bool
- func (c *Cron) Resume(id EntryID) bool
- func (c *Cron) Running() bool
- func (c *Cron) Start() error
- func (c *Cron) Stop(ctx context.Context) error
- func (c *Cron) Trigger(id EntryID) error
- func (c *Cron) TriggerAndWait(ctx context.Context, id EntryID) error
- func (c *Cron) TriggerByName(name string) (int, error)
- func (c *Cron) Update(id EntryID, spec string) error
- func (c *Cron) UpdateSchedule(id EntryID, s Schedule) error
- type Elector
- type Entry
- type EntryID
- type EntryInfo
- type EntryOption
- func WithEntryChain(wrappers ...Wrapper) EntryOption
- func WithEntryClaimer(claimer Claimer) EntryOption
- func WithEntryJitter(max time.Duration) EntryOption
- func WithEntryMissedFire(p MissedFirePolicy) EntryOption
- func WithEntryRetry(p RetryPolicy) EntryOption
- func WithKey(key string) EntryOption
- func WithLastRun(t time.Time) EntryOption
- func WithName(name string) EntryOption
- func WithTimeout(d time.Duration) EntryOption
- type EntryRef
- type Event
- type Job
- type JobCompleteEvent
- type JobFunc
- type JobStartEvent
- type MissedFireEvent
- type MissedFirePolicy
- type Observer
- type ObserverDropEvent
- type ObserverFunc
- type Option
- func WithBaseContext(ctx context.Context) Option
- func WithChain(wrappers ...Wrapper) Option
- func WithClaimer(claimer Claimer) Option
- func WithElector(e Elector) Option
- func WithJitter(max time.Duration) Option
- func WithLocation(loc *time.Location) Option
- func WithLogger(l *slog.Logger) Option
- func WithMaxConcurrent(n int) Option
- func WithMaxEntries(n int) Option
- func WithMissedFire(p MissedFirePolicy) Option
- func WithMissedTolerance(d time.Duration) Option
- func WithObserverBuffer(n int) Option
- func WithObservers(observers ...Observer) Option
- func WithParser(p Parser) Option
- func WithRecorder(r Recorder) Option
- func WithRetry(p RetryPolicy) Option
- func WithSecondsField() Option
- func WithoutRecover() Option
- type ParseError
- type Parser
- type ParserOption
- type QueueDepthEvent
- type Recorder
- type RecorderFunc
- type RejectReason
- type RejectedFireEvent
- type RetryOption
- type RetryPolicy
- type Schedule
- type ScheduleEvent
- type SkipReason
- type SkippedFireEvent
- type SpecAnalysis
- type SpecSchedule
- type StandardParser
- type Upcoming
- type Wrapper
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( ErrCapacityReached = errors.New("cron: capacity reached") ErrAlreadyRunning = errors.New("cron: job already running") ErrJobTimeout = errors.New("cron: job timeout") ErrCronStopping = errors.New("cron: scheduler stopping") ErrEntryNotFound = errors.New("cron: entry not found") ErrSchedulerNotRunning = errors.New("cron: scheduler not running") ErrConcurrencyLimit = errors.New("cron: max concurrent reached") ErrSchedulerStopped = errors.New("cron: scheduler stopped") ErrNilJob = errors.New("cron: nil job") ErrNilSchedule = errors.New("cron: nil schedule") ErrJobPanic = errors.New("cron: job panicked") ErrClaimerRequiresKey = errors.New("cron: distributed claimer requires WithKey") ErrDuplicateKey = errors.New("cron: duplicate entry key") ErrNilContext = errors.New("cron: nil context") ErrInvalidOption = errors.New("cron: invalid option") )
Errors returned by scheduler configuration, lifecycle, and dispatch.
Functions ¶
func IsTriggered ¶
IsTriggered reports whether s came from TriggeredSchedule.
func ValidateSpec ¶
ValidateSpec checks spec with the standard parser.
func ValidateSpecWith ¶
ValidateSpecWith checks spec with p.
Types ¶
type AlignedDelay ¶ added in v0.5.1
AlignedDelay is an epoch-aligned interval for replicas that must compute the same fire times. Non-positive intervals never fire.
func (AlignedDelay) String ¶ added in v0.5.1
func (d AlignedDelay) String() string
type CanceledFireEvent ¶ added in v0.5.3
CanceledFireEvent reports a reserved fire canceled before job execution, such as a scheduler shutdown during jitter.
type Claimer ¶ added in v0.5.3
Claimer lets one scheduler instance claim a fire. false, nil means another instance already claimed it; a non-nil error is a backend failure. Claims must remain reserved long enough that a delayed replica cannot run the same fire later. Implementations own their timeout and retention policy.
type ConstantDelay ¶
ConstantDelay is a process-anchored interval with a one-second floor. Whole-second intervals align to second boundaries.
func (ConstantDelay) String ¶
func (d ConstantDelay) String() string
type Cron ¶
type Cron struct {
// contains filtered or unexported fields
}
Cron is a job scheduler. Construct one with New, register jobs, then Start.
func (*Cron) Add ¶
Add parses spec and registers j. It returns a *ParseError for invalid specs or ErrCapacityReached when WithMaxEntries rejects the registration.
func (*Cron) AddSchedule ¶
AddSchedule registers j against a programmatic Schedule.
func (*Cron) Drain ¶ added in v0.3.0
Drain is Stop without cancelling in-flight jobs: it stops scheduling new fires and waits for running jobs to finish naturally, capped by ctx.
func (*Cron) Pause ¶ added in v0.3.0
Pause suspends automatic fires for id, keeping the entry and its Prev. Manual Trigger still works while paused. Returns false if id is unknown.
func (*Cron) Remove ¶
Remove deregisters id. In-flight invocations continue; future automatic fires and future Trigger calls for id are rejected.
func (*Cron) Resume ¶ added in v0.3.0
Resume re-enables automatic fires for id, scheduling from now. Returns false if id is unknown.
func (*Cron) Running ¶
Running reports whether the scheduler is running. It is observational; use Trigger's returned error for race-free dispatch decisions.
func (*Cron) Start ¶
Start launches the scheduler. It is idempotent while running and returns ErrSchedulerStopped after Stop has been called.
func (*Cron) Stop ¶
Stop halts the scheduler, cancels in-flight jobs (ErrCronStopping as the cause), and waits for the loop, jobs, and observer queue to drain, capped by ctx. Returns ctx.Err() on timeout. Do not call it from inside a Job.
func (*Cron) Trigger ¶
Trigger fires id immediately, bypassing jitter. It returns ErrSchedulerNotRunning, ErrEntryNotFound, or ErrConcurrencyLimit when dispatch is rejected. Paused entries can still be triggered.
func (*Cron) TriggerAndWait ¶ added in v0.3.0
TriggerAndWait fires id like Trigger and blocks until the invocation returns, yielding the job's error. ctx bounds only the wait; on ctx cancellation the job keeps running.
func (*Cron) TriggerByName ¶
TriggerByName fires every entry whose Name matches name. Returns the successful dispatch count and errors.Join of per-Trigger failures. No match returns (0, nil); not running returns (0, ErrSchedulerNotRunning).
type Elector ¶ added in v0.3.0
Elector reports whether this instance currently leads. false, nil is the normal follower state; a non-nil error is a backend failure.
type Entry ¶
type Entry struct {
ID EntryID
Name string
Key string // stable identity for distributed fire claims
Spec string // empty for AddSchedule entries
Schedule Schedule
Prev time.Time // zero if never fired
Next time.Time // zero if exhausted, paused, or TriggeredSchedule
Paused bool
}
Entry is the public read-only view of a scheduled item. Safe to copy.
type EntryInfo ¶ added in v0.4.0
EntryInfo identifies the running invocation inside a job's context.
func EntryInfoFromContext ¶ added in v0.4.0
EntryInfoFromContext returns the identity of the entry whose job is running under ctx. The scheduler injects it for every dispatch, so wrappers and jobs can tell which entry — and which fire — they serve.
type EntryOption ¶
type EntryOption func(*entryConfig) error
EntryOption configures one entry.
func WithEntryChain ¶
func WithEntryChain(wrappers ...Wrapper) EntryOption
WithEntryChain installs per-entry wrappers inside the global chain.
func WithEntryClaimer ¶ added in v0.5.3
func WithEntryClaimer(claimer Claimer) EntryOption
WithEntryClaimer overrides the scheduler's Claimer for one entry. An explicit nil disables distributed claims for the entry.
func WithEntryJitter ¶ added in v0.3.0
func WithEntryJitter(max time.Duration) EntryOption
WithEntryJitter overrides the scheduler's jitter for one entry. Zero disables jitter for the entry.
func WithEntryMissedFire ¶ added in v0.3.0
func WithEntryMissedFire(p MissedFirePolicy) EntryOption
WithEntryMissedFire overrides the scheduler's missed-fire policy for one entry.
func WithEntryRetry ¶
func WithEntryRetry(p RetryPolicy) EntryOption
WithEntryRetry overrides the global retry for one entry. A zero policy disables retry for that entry.
func WithKey ¶ added in v0.5.3
func WithKey(key string) EntryOption
WithKey sets the stable, unique identity used for distributed fire claims. It is independent from the display-oriented Name.
func WithLastRun ¶ added in v0.3.0
func WithLastRun(t time.Time) EntryOption
WithLastRun seeds the entry's schedule anchor, usually the persisted time of the last run before a restart. The first fire is computed from t instead of now, so a missed-fire policy can catch up work missed while the process was down. It also seeds Entry.Prev.
func WithTimeout ¶
func WithTimeout(d time.Duration) EntryOption
WithTimeout caps a Job's runtime with ErrJobTimeout as the cancel cause.
type Event ¶ added in v0.5.3
type Event interface {
// contains filtered or unexported methods
}
Event is one scheduler event. Its concrete types are defined by this package, so consumers can use a type switch without accepting arbitrary implementations.
type JobCompleteEvent ¶ added in v0.5.3
type JobCompleteEvent struct {
Entry EntryRef
ScheduledAt time.Time
StartedAt time.Time
Duration time.Duration
Err error
}
JobCompleteEvent reports the result of a job wrapper chain.
type JobFunc ¶
JobFunc adapts a function to Job.
Example ¶
package main
import (
"context"
"fmt"
"github.com/libtnb/cron"
)
func main() {
j := cron.JobFunc(func(ctx context.Context) error {
fmt.Println("hello")
return nil
})
_ = j.Run(context.Background())
}
Output: hello
type JobStartEvent ¶ added in v0.5.3
JobStartEvent reports a job immediately before its wrapper chain runs.
type MissedFireEvent ¶ added in v0.5.3
type MissedFireEvent struct {
Entry EntryRef
ScheduledAt time.Time
Lateness time.Duration
Policy MissedFirePolicy
}
MissedFireEvent reports a fire that was late enough to invoke the entry's missed-fire policy.
type MissedFirePolicy ¶
type MissedFirePolicy uint8
MissedFirePolicy controls behaviour when a fire is later than WithMissedTolerance. MissedFireEvent is published regardless of policy.
const ( // MissedSkip ignores missed firings and resumes from the next // scheduled time. This is the default. MissedSkip MissedFirePolicy = iota // MissedRunOnce runs the job once for the most recent missed firing // (latest schedule.Next <= now), then resumes normally. MissedRunOnce // MissedRunAll runs the job once per missed firing (newest // missedRunAllCap kept), then resumes normally. MissedRunAll )
func (MissedFirePolicy) String ¶
func (p MissedFirePolicy) String() string
type Observer ¶ added in v0.5.3
type Observer interface {
Observe(Event)
}
Observer receives scheduler events asynchronously and in publication order. One slow observer can fill the shared queue; new events are then dropped.
type ObserverDropEvent ¶ added in v0.5.3
type ObserverDropEvent struct {
Dropped int64
}
ObserverDropEvent reports that the async observer queue dropped an event. Dropped is the cumulative count for this Cron.
type ObserverFunc ¶ added in v0.5.3
type ObserverFunc func(Event)
ObserverFunc adapts a function to Observer.
func (ObserverFunc) Observe ¶ added in v0.5.3
func (f ObserverFunc) Observe(event Event)
type Option ¶
type Option func(*config) error
Option configures a Cron.
func WithBaseContext ¶ added in v0.3.0
WithBaseContext sets the root context jobs inherit from. Cancelling it stops firing and cancels in-flight jobs, like Stop but without waiting.
func WithClaimer ¶ added in v0.5.3
WithClaimer sets the distributed Claimer used for automatic fires. Manual Trigger bypasses coordination. Entries using it must configure WithKey.
func WithElector ¶ added in v0.3.0
WithElector gates automatic fires on leadership. Follower state and backend failure both skip the fire but produce distinct SkipReason values. Manual Trigger bypasses coordination.
func WithJitter ¶
WithJitter adds a random delay in [0, max) to each firing.
func WithLocation ¶
WithLocation sets the default schedule timezone. Default is time.Local. Ignored when WithParser is set: a custom parser owns its timezone.
func WithLogger ¶
WithLogger sets the slog.Logger. Default slog.Default().
func WithMaxConcurrent ¶
WithMaxConcurrent caps in-flight jobs. Zero means unlimited.
func WithMaxEntries ¶
WithMaxEntries caps registered entries. Zero means unlimited.
func WithMissedFire ¶
func WithMissedFire(p MissedFirePolicy) Option
WithMissedFire selects the missed-fire policy. Default MissedSkip.
func WithMissedTolerance ¶
WithMissedTolerance sets the lateness threshold for "missed". Default 1s.
func WithObserverBuffer ¶ added in v0.5.3
WithObserverBuffer sets the async event queue capacity. Zero selects the default. A full queue drops new observer events while the Recorder still receives them.
func WithObservers ¶ added in v0.5.3
WithObservers installs async event observers. Events are serialized through one bounded queue and delivered to observers in option order.
func WithParser ¶
WithParser installs a parser. It takes over timezone resolution, so WithLocation and WithSecondsField no longer apply.
func WithRecorder ¶
WithRecorder installs an inline event recorder. Record may be called concurrently and must be concurrency-safe and fast.
func WithRetry ¶
func WithRetry(p RetryPolicy) Option
WithRetry sets the default RetryPolicy. Overridden by WithEntryRetry.
func WithSecondsField ¶ added in v0.2.2
func WithSecondsField() Option
WithSecondsField enables a leading seconds field in the built-in parser, so the common seconds + WithLocation case composes without WithParser.
func WithoutRecover ¶ added in v0.3.0
func WithoutRecover() Option
WithoutRecover disables the built-in job panic recovery. By default a panicking job is recovered into an ErrJobPanic-wrapped error; with this option the panic propagates and crashes the process.
type ParseError ¶
type ParseError struct {
Spec string
Field string // e.g. "minute"; "" if not applicable
Pos int // 0-based byte offset; -1 if unknown
Reason string
Err error
}
ParseError describes a failure parsing a cron specification.
func (*ParseError) Error ¶
func (e *ParseError) Error() string
func (*ParseError) Unwrap ¶
func (e *ParseError) Unwrap() error
type ParserOption ¶
type ParserOption func(*parserConfig)
ParserOption configures NewStandardParser.
func WithDefaultLocation ¶
func WithDefaultLocation(loc *time.Location) ParserOption
WithDefaultLocation sets the default timezone for specs without TZ=/CRON_TZ=. nil means time.Local.
func WithOptionalSeconds ¶ added in v0.5.3
func WithOptionalSeconds() ParserOption
WithOptionalSeconds accepts both 5- and 6-field specs. A 5-field spec is parsed with second=0.
func WithParserExt ¶
func WithParserExt(ext Parser) ParserOption
WithParserExt consults ext before the standard parser. Returning (nil, nil) falls through to standard parsing.
func WithRequiredSeconds ¶ added in v0.5.3
func WithRequiredSeconds() ParserOption
WithRequiredSeconds requires exactly 6 fields, including leading seconds.
type QueueDepthEvent ¶ added in v0.5.3
type QueueDepthEvent struct {
Depth int
}
QueueDepthEvent reports the number of entries in the scheduling heap.
type Recorder ¶ added in v0.5.3
type Recorder interface {
Record(Event)
}
Recorder receives scheduler events inline. Record may be called concurrently and must be concurrency-safe and fast. A panic is recovered and logged.
type RecorderFunc ¶ added in v0.5.3
type RecorderFunc func(Event)
RecorderFunc adapts a function to Recorder.
func (RecorderFunc) Record ¶ added in v0.5.3
func (f RecorderFunc) Record(event Event)
type RejectReason ¶ added in v0.5.3
type RejectReason uint8
RejectReason classifies a fire rejected before job execution.
const ( RejectUnknown RejectReason = iota RejectConcurrencyLimit )
func (RejectReason) String ¶ added in v0.5.3
func (r RejectReason) String() string
type RejectedFireEvent ¶ added in v0.5.3
type RejectedFireEvent struct {
Entry EntryRef
ScheduledAt time.Time
Reason RejectReason
}
RejectedFireEvent reports a fire rejected before job execution.
type RetryOption ¶
type RetryOption func(*RetryPolicy)
RetryOption configures a RetryPolicy built by Retry.
func RetryInitial ¶
func RetryInitial(d time.Duration) RetryOption
RetryInitial sets the first retry delay (default 1s).
func RetryJitterFrac ¶
func RetryJitterFrac(f float64) RetryOption
RetryJitterFrac is fractional uniform jitter (e.g. 0.1 = ±10%).
func RetryMaxDelay ¶
func RetryMaxDelay(d time.Duration) RetryOption
RetryMaxDelay caps backoff (zero = uncapped).
func RetryMultiplier ¶
func RetryMultiplier(m float64) RetryOption
RetryMultiplier is the per-attempt growth factor (<=1 stays constant).
type RetryPolicy ¶
type RetryPolicy struct {
MaxRetries int
Initial time.Duration
MaxDelay time.Duration
Multiplier float64
JitterFrac float64
}
RetryPolicy describes exponential backoff with optional jitter. MaxRetries == 0 disables retry; negative means unlimited until ctx cancellation. Fields are exported for config-driven assembly; use Retry(...) for programmatic construction.
func Retry ¶
func Retry(maxRetries int, opts ...RetryOption) RetryPolicy
Retry builds a RetryPolicy. maxRetries is the number of retries after the initial attempt; negative retries until ctx cancellation.
func (RetryPolicy) IsZero ¶
func (p RetryPolicy) IsZero() bool
IsZero is keyed only on MaxRetries so half-filled policies (e.g. only Initial set) don't produce a useless wrapper.
func (RetryPolicy) Wrapper ¶
func (p RetryPolicy) Wrapper() Wrapper
Wrapper returns a Wrapper that retries on error per p. Attempt errors are joined via errors.Join; ctx cancellation aborts, recording context.Cause so ErrJobTimeout / ErrCronStopping survive into the joined error.
type Schedule ¶
Schedule yields successive firing times. Next must return the first firing strictly after now, or zero when exhausted.
func Filter ¶ added in v0.3.0
Filter wraps s, skipping firings for which keep returns false — e.g. a holiday calendar. A nil keep passes everything through. The search gives up (returns zero) after filterScanCap consecutive rejections.
func OnceAt ¶ added in v0.3.0
OnceAt fires exactly once, at t. If t is already past, it never fires.
func TriggeredSchedule ¶
func TriggeredSchedule() Schedule
TriggeredSchedule never fires automatically. Combine with Trigger.
type ScheduleEvent ¶ added in v0.5.3
ScheduleEvent reports an entry's newly computed next fire. Next is zero when the schedule has no future fire.
type SkipReason ¶ added in v0.3.0
type SkipReason uint8
SkipReason classifies why distributed coordination suppressed a fire.
const ( SkipUnknown SkipReason = iota SkipNotLeader SkipElectionError SkipAlreadyClaimed SkipClaimError )
func (SkipReason) String ¶ added in v0.3.0
func (r SkipReason) String() string
type SkippedFireEvent ¶ added in v0.5.3
type SkippedFireEvent struct {
Entry EntryRef
ScheduledAt time.Time
Reason SkipReason
Err error
}
SkippedFireEvent reports a fire suppressed by distributed coordination. Err is non-nil only when the coordination backend failed.
type SpecAnalysis ¶
type SpecAnalysis struct {
Spec string
Valid bool
Err error
IsTriggered bool
Descriptor string // "@every", "@hourly", ... or "" for 5/6-field specs
Interval time.Duration // set when Descriptor == "@every"
Location *time.Location // schedule timezone
NextRun time.Time // upcoming firing relative to the now passed in
}
SpecAnalysis is the result of AnalyzeSpec. Most fields are populated only when Valid is true.
func AnalyzeSpec ¶
func AnalyzeSpec(spec string, now time.Time) SpecAnalysis
AnalyzeSpec describes spec relative to now using the standard parser.
func AnalyzeSpecWith ¶
func AnalyzeSpecWith(spec string, p Parser, now time.Time) SpecAnalysis
AnalyzeSpecWith describes spec relative to now using p.
type SpecSchedule ¶
type SpecSchedule struct {
// contains filtered or unexported fields
}
SpecSchedule is a parsed cron expression.
func (*SpecSchedule) Location ¶
func (s *SpecSchedule) Location() *time.Location
Location returns the evaluation timezone.
func (*SpecSchedule) LogValue ¶
func (s *SpecSchedule) LogValue() slog.Value
func (*SpecSchedule) Next ¶
func (s *SpecSchedule) Next(t time.Time) time.Time
Next returns the next firing after t, or zero if none is found.
The hour branch reconstructs the wall clock at the target hour instead of adding an absolute (h-hour)*time.Hour, which would overshoot a DST spring-forward gap and skip the day. The minute/second branches keep their absolute jumps: they never straddle a whole-hour DST boundary, and stepping through the repeated hour on fall-back keeps both firings observable.
type StandardParser ¶
type StandardParser struct {
// contains filtered or unexported fields
}
StandardParser is safe for concurrent use after construction.
func NewStandardParser ¶
func NewStandardParser(opts ...ParserOption) *StandardParser
NewStandardParser handles 5/6-field specs, descriptors, and TZ prefixes.
type Wrapper ¶
Wrapper decorates a Job.
func Chain ¶
Chain composes wrappers so the first wraps outermost.
Example ¶
package main
import (
"context"
"fmt"
"github.com/libtnb/cron"
)
func main() {
mk := func(name string) cron.Wrapper {
return func(j cron.Job) cron.Job {
return cron.JobFunc(func(ctx context.Context) error {
fmt.Println("enter", name)
err := j.Run(ctx)
fmt.Println("leave", name)
return err
})
}
}
core := cron.JobFunc(func(ctx context.Context) error {
fmt.Println("run core")
return nil
})
_ = cron.Chain(mk("outer"), mk("inner"))(core).Run(context.Background())
}
Output: enter outer enter inner run core leave inner leave outer
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
_examples
|
|
|
hello
command
|
|
|
quartz
command
|
|
|
seconds
command
|
|
|
slog
command
|
|
|
workflow
command
|
|
|
contrib
|
|
|
otel
module
|
|
|
prometheus
module
|
|
|
coordination
|
|
|
postgres
module
|
|
|
redis
module
|
|
|
internal
|
|
|
bitmask
Package bitmask provides bit-set scans shared by the schedule implementations.
|
Package bitmask provides bit-set scans shared by the schedule implementations. |
|
heap
Package heap provides a typed min-heap with addressable items.
|
Package heap provides a typed min-heap with addressable items. |
|
parsecache
Package parsecache memoises parser results.
|
Package parsecache memoises parser results. |
|
Package parserext provides optional cron parser extensions.
|
Package parserext provides optional cron parser extensions. |
|
Package workflow builds typed DAGs of cron jobs.
|
Package workflow builds typed DAGs of cron jobs. |
|
Package wrap supplies Job decorators.
|
Package wrap supplies Job decorators. |