arbiter-worker-0.1.0.0: Worker framework for arbiter
Safe HaskellNone
LanguageGHC2024

Arbiter.Worker.Cron

Description

Cron scheduler for the Arbiter worker pool.

When cronJobs is non-empty in WorkerConfig, runWorkerPool spawns an additional thread that inserts jobs based on 5-field cron expressions. Uses IgnoreDuplicate dedup keys for multi-instance idempotency.

All cron expressions are evaluated in UTC. There is no local-timezone support — "0 3 * * *" means 03:00 UTC, not 03:00 in the server's local time. Account for your timezone offset when writing expressions.

When a connection pool and schema are provided, the scheduler consults the cron_schedules table for runtime overrides (expression, overlap, enabled).

Synopsis

Types

data CronJob payload Source #

A cron schedule definition.

Use cronJob to construct — it parses the cron expression eagerly, so invalid expressions are caught at construction time.

Constructors

CronJob 

Fields

Instances

Instances details
Generic (CronJob payload) Source # 
Instance details

Defined in Arbiter.Worker.Cron

Associated Types

type Rep (CronJob payload) 
Instance details

Defined in Arbiter.Worker.Cron

type Rep (CronJob payload) = D1 ('MetaData "CronJob" "Arbiter.Worker.Cron" "arbiter-worker-0.1.0.0-inplace" 'False) (C1 ('MetaCons "CronJob" 'PrefixI 'True) ((S1 ('MetaSel ('Just "name") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 Text) :*: S1 ('MetaSel ('Just "cronExpression") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 Text)) :*: (S1 ('MetaSel ('Just "schedule") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 CronSchedule) :*: (S1 ('MetaSel ('Just "overlap") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 OverlapPolicy) :*: S1 ('MetaSel ('Just "builder") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 (UTCTime -> JobWrite payload))))))

Methods

from :: CronJob payload -> Rep (CronJob payload) x #

to :: Rep (CronJob payload) x -> CronJob payload #

type Rep (CronJob payload) Source # 
Instance details

Defined in Arbiter.Worker.Cron

type Rep (CronJob payload) = D1 ('MetaData "CronJob" "Arbiter.Worker.Cron" "arbiter-worker-0.1.0.0-inplace" 'False) (C1 ('MetaCons "CronJob" 'PrefixI 'True) ((S1 ('MetaSel ('Just "name") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 Text) :*: S1 ('MetaSel ('Just "cronExpression") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 Text)) :*: (S1 ('MetaSel ('Just "schedule") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 CronSchedule) :*: (S1 ('MetaSel ('Just "overlap") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 OverlapPolicy) :*: S1 ('MetaSel ('Just "builder") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 (UTCTime -> JobWrite payload))))))

data OverlapPolicy Source #

Determines how overlapping cron ticks are deduplicated.

Constructors

SkipOverlap

At most one pending or running job per schedule name.

AllowOverlap

One job per tick. Allows concurrent execution of prior ticks.

Instances

Instances details
Generic OverlapPolicy Source # 
Instance details

Defined in Arbiter.Worker.Cron

Associated Types

type Rep OverlapPolicy 
Instance details

Defined in Arbiter.Worker.Cron

type Rep OverlapPolicy = D1 ('MetaData "OverlapPolicy" "Arbiter.Worker.Cron" "arbiter-worker-0.1.0.0-inplace" 'False) (C1 ('MetaCons "SkipOverlap" 'PrefixI 'False) (U1 :: Type -> Type) :+: C1 ('MetaCons "AllowOverlap" 'PrefixI 'False) (U1 :: Type -> Type))
Show OverlapPolicy Source # 
Instance details

Defined in Arbiter.Worker.Cron

Eq OverlapPolicy Source # 
Instance details

Defined in Arbiter.Worker.Cron

type Rep OverlapPolicy Source # 
Instance details

Defined in Arbiter.Worker.Cron

type Rep OverlapPolicy = D1 ('MetaData "OverlapPolicy" "Arbiter.Worker.Cron" "arbiter-worker-0.1.0.0-inplace" 'False) (C1 ('MetaCons "SkipOverlap" 'PrefixI 'False) (U1 :: Type -> Type) :+: C1 ('MetaCons "AllowOverlap" 'PrefixI 'False) (U1 :: Type -> Type))

Smart Constructor

cronJob Source #

Arguments

:: Text

Schedule name (used in dedup keys and logging)

-> Text

Cron expression (5-field: minute hour day-of-month month day-of-week)

-> OverlapPolicy 
-> (UTCTime -> JobWrite payload) 
-> Either String (CronJob payload) 

Smart constructor for CronJob. Parses the cron expression eagerly.

Returns Left with an error message if the cron expression is invalid.

Note: cron expressions are evaluated in UTC. "0 3 * * *" fires at 03:00 UTC regardless of the server's local timezone.

Example:

cronJob "nightly-report" "0 3 * * *" SkipOverlap
  (\_ -> defaultJob (GenerateReport "nightly"))

Helpers

overlapPolicyToText :: OverlapPolicy -> Text Source #

Convert an OverlapPolicy to its text representation.

DB Init

initCronSchedules :: Connection -> Text -> [CronJob payload] -> LogConfig -> IO () Source #

Initialize the cron_schedules table and upsert defaults for all cron jobs.

Called once at scheduler startup. Upserts default_expression and default_overlap for each CronJob, preserving any user overrides and enabled state.

Internal

runCronScheduler :: forall m (registry :: JobPayloadRegistry) payload. (MonadUnliftIO m, QueueOperation m registry payload) => Connection -> LogConfig -> Text -> [CronJob payload] -> m () Source #

Run the cron scheduler loop. Called by runWorkerPool when cronJobs is non-empty.

On each minute boundary, checks all schedules and inserts matching jobs with appropriate dedup keys. Exceptions per-schedule are caught and logged; the loop continues.

The provided Connection is used to consult the cron_schedules table for effective expression, overlap, and enabled state on each tick.

processCronTick :: forall m (registry :: JobPayloadRegistry) payload. (MonadUnliftIO m, QueueOperation m registry payload) => Connection -> LogConfig -> Text -> [CronJob payload] -> UTCTime -> m () Source #

Process a single cron tick at the given time.

For each CronJob, consults the DB for effective expression, overlap, and enabled. If the schedule is disabled, it is skipped. If the effective expression fails to parse, it is logged and skipped.

truncateToMinute :: UTCTime -> UTCTime Source #

Truncate a UTCTime to the current minute (zero out seconds).

formatMinute :: UTCTime -> Text Source #

Format a UTCTime as YYYY-MM-DDTHH:MM for dedup key buckets.

makeDedupKey :: CronJob payload -> UTCTime -> Text Source #

Compute the dedup key for a cron job at the given tick time.

Uses the code-defined overlap policy. If the schedule has a DB override, use makeDedupKeyFromParts with the effective policy instead.

computeDelayMicros :: UTCTime -> Int Source #

Compute the delay in microseconds until the next minute boundary, clamped to [0, 120_000_000].