arbiter-worker-0.1.0.0
arbiter-worker
Safe HaskellNone
LanguageGHC2024

Arbiter.Worker.Cron

Description

Cron schedule declarations and worker integration.

Synopsis

Schedules

data CronJob payloadSource #

A cron schedule. Built with cronJob, or cronJobInTimezone for a non-UTC one.

Constructors

CronJob 

Fields

Instances

Instances details
Generic (CronJob payload) Source # 
Instance details

Defined in Arbiter.Worker.Cron.Types

Associated Types

type Rep (CronJob payload) 
Instance details

Defined in Arbiter.Worker.Cron.Types

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.Types

data OverlapPolicySource #

Cron tick overlap policy.

Constructors

SkipOverlap

At most one pending or running job per schedule.

AllowOverlap

One job per tick. Concurrent execution of prior ticks is allowed.

Instances

Instances details
Eq OverlapPolicy Source # 
Instance details

Defined in Arbiter.Worker.Cron.Types

Generic OverlapPolicy Source # 
Instance details

Defined in Arbiter.Worker.Cron.Types

Associated Types

type Rep OverlapPolicy 
Instance details

Defined in Arbiter.Worker.Cron.Types

type Rep OverlapPolicy = D1 ('MetaData "OverlapPolicy" "Arbiter.Worker.Cron.Types" "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.Types

type Rep OverlapPolicy Source # 
Instance details

Defined in Arbiter.Worker.Cron.Types

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

data BackfillPolicySource #

Bound on replay of missed ticks. Applies at startup and mid-flight.

Constructors

NoBackfill

Drop missed minutes silently. Default.

Backfill NominalDiffTime

Replay minutes missed in the last given number of seconds. A SkipOverlap schedule replays only the earliest one.

Instances

Instances details
Eq BackfillPolicy Source # 
Instance details

Defined in Arbiter.Worker.Cron.Types

Generic BackfillPolicy Source # 
Instance details

Defined in Arbiter.Worker.Cron.Types

Associated Types

type Rep BackfillPolicy 
Instance details

Defined in Arbiter.Worker.Cron.Types

type Rep BackfillPolicy = D1 ('MetaData "BackfillPolicy" "Arbiter.Worker.Cron.Types" "arbiter-worker-0.1.0.0-inplace" 'False) (C1 ('MetaCons "NoBackfill" 'PrefixI 'False) (U1 :: Type -> Type) :+: C1 ('MetaCons "Backfill" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 NominalDiffTime)))
Show BackfillPolicy Source # 
Instance details

Defined in Arbiter.Worker.Cron.Types

type Rep BackfillPolicy Source # 
Instance details

Defined in Arbiter.Worker.Cron.Types

type Rep BackfillPolicy = D1 ('MetaData "BackfillPolicy" "Arbiter.Worker.Cron.Types" "arbiter-worker-0.1.0.0-inplace" 'False) (C1 ('MetaCons "NoBackfill" 'PrefixI 'False) (U1 :: Type -> Type) :+: C1 ('MetaCons "Backfill" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 NominalDiffTime)))

data TickKindSource #

Whether a tick is for the current minute or a replay of a past minute.

Constructors

Live

The current scheduler minute.

Replay

A past minute, replayed at startup or in mid-flight catch-up.

Instances

Instances details
Eq TickKind Source # 
Instance details

Defined in Arbiter.Worker.Cron.Types

Generic TickKind Source # 
Instance details

Defined in Arbiter.Worker.Cron.Types

Associated Types

type Rep TickKind 
Instance details

Defined in Arbiter.Worker.Cron.Types

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

Defined in Arbiter.Worker.Cron.Types

type Rep TickKind Source # 
Instance details

Defined in Arbiter.Worker.Cron.Types

type Rep TickKind = D1 ('MetaData "TickKind" "Arbiter.Worker.Cron.Types" "arbiter-worker-0.1.0.0-inplace" 'False) (C1 ('MetaCons "Live" 'PrefixI 'False) (U1 :: Type -> Type) :+: C1 ('MetaCons "Replay" 'PrefixI 'False) (U1 :: Type -> Type))

cronJob :: Text -> Text -> OverlapPolicy -> (TickKind -> UTCTime -> JobWrite payload) -> Either String (CronJob payload)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

Overlap policy

-> (TickKind -> UTCTime -> JobWrite payload)

Job builder. Receives the tick kind and the tick time.

-> Either String (CronJob payload) 

Build a CronJob evaluated in UTC. A bad expression returns Left. cronJobInTimezone is the local-time form. Set backfill and initiallyEnabled by record update.

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

cronJobInTimezone :: Text -> Text -> Text -> OverlapPolicy -> (TickKind -> UTCTime -> JobWrite payload) -> Either String (CronJob payload)Source #

Arguments

:: Text

Schedule name

-> Text

IANA tz name (e.g. "America/New_York")

-> Text

Cron expression (5-field)

-> OverlapPolicy

Overlap policy

-> (TickKind -> UTCTime -> JobWrite payload)

Job builder

-> Either String (CronJob payload) 

Like cronJob but evaluated in a specific timezone. The tz name is validated eagerly against the bundled tzdata database.

overlapPolicyToText :: OverlapPolicy -> TextSource #

Convert an OverlapPolicy to its text representation.

Schedule updates

validateCronScheduleUpdate :: CronScheduleUpdate -> Either Text ()Source #

Check a cron patch before it is written. At tick time a bad override stops firing or falls back to the default.

updateCronScheduleChecked :: MonadArbiter m => Text -> CronScheduleUpdate -> m (Either Text Int64)Source #

updateCronScheduleUnchecked behind validateCronScheduleUpdate. Returns rows affected, 0 for an unknown name or an empty patch.

Next run

nextRunInTimezone :: Maybe Text -> CronSchedule -> UTCTime -> Maybe UTCTimeSource #

The first tick after now that sched matches in the given timezone. Nothing means UTC. An unknown tz name returns Nothing.

nextRunFromExpression :: Maybe Text -> Text -> UTCTime -> Maybe UTCTimeSource #

nextRunInTimezone over an unparsed expression. A bad one returns Nothing.

Testing internals

Exposed for the arbiter test suites. Not covered by the PVP.

formatMinute :: UTCTime -> TextSource #

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

resolveTZ :: Text -> Maybe TZSource #

Look up an IANA tz name in the bundled tzdata database.

matchesInTimezone :: Maybe Text -> CronSchedule -> UTCTime -> BoolSource #

Match a cron schedule against a UTC tick in the given timezone. Nothing means UTC. An unknown tz name returns False. A fixed-time schedule skips the second reading of a repeated local hour. A wildcard one runs through both.

enumMinutes :: UTCTime -> UTCTime -> [UTCTime]Source #

Enumerate all minute-boundary times from start through end (inclusive). Both start and end should be truncated to minute boundaries.

truncateToMinute :: UTCTime -> UTCTimeSource #

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

enumerateCatchUpTicks :: BackfillPolicy -> Maybe UTCTime -> UTCTime -> [UTCTime]Source #

Minutes to evaluate for a processCronCatchUp call. Returns [] when the watermark is at or past currentTick.

mkDedupKeyFromParts :: Text -> OverlapPolicy -> UTCTime -> TextSource #

The dedup key for a schedule's tick. An AllowOverlap key includes the UTC tick minute.

computeDelayMicros :: UTCTime -> IntSource #

Delay in microseconds until the next minute boundary, clamped to zero through maxTickDelay.

data CronLogSource #

The scheduler's logger with the gates its repeating failures report through.

newCronLog :: MonadIO m => LogConfig -> m CronLogSource #

A CronLog with no gate tripped, for one scheduler run.

runCronScheduler :: QueueOperation m payload => LogConfig -> TVar WorkerState -> TVar Bool -> SchemaName -> Text -> [CronJob payload] -> m ()Source #

Arguments

:: QueueOperation m payload 
=> LogConfig 
-> TVar WorkerState 
-> TVar Bool

Set by the run-now listener when a schedule this pool owns is requested.

-> SchemaName 
-> Text

Queue name (recorded on each schedule row).

-> [CronJob payload] 
-> m () 

Scheduler entry point. Exits when the worker state becomes ShuttingDown.

processCronCatchUp :: QueueOperation m payload => CronLog -> SchemaName -> Text -> [CronJob payload] -> UTCTime -> m ()Source #

Arguments

:: QueueOperation m payload 
=> CronLog 
-> SchemaName 
-> Text

Queue name

-> [CronJob payload] 
-> UTCTime

Current wall-clock time

-> m () 

Scheduler catch-up step. Each tick commits independently. Backfill ticks acquire a per-schedule advisory lock for their transaction.

A failed tick is logged and skipped once a later tick fires. The watermark of a Backfill schedule stays behind a failed newest tick, and the next pass retries it.

processRunRequests :: forall payload m. QueueOperation m payload => CronLog -> SchemaName -> [CronJob payload] -> UTCTime -> m ()Source #

Claim and fire every schedule with a pending run request. A SkipOverlap schedule reuses its constant dedup key, so its manual run is skipped while one of its jobs is active.

The claim and the insert are atomic. If either fails the other rolls back.

initCronSchedules :: MonadArbiter m => LogConfig -> Text -> [CronJob payload] -> m ()Source #

Arguments

:: MonadArbiter m 
=> LogConfig 
-> Text

Queue name

-> [CronJob payload] 
-> m () 

Upsert the default expression, overlap, timezone and initial enabled flag of each CronJob into the cron_schedules table. Preserves user overrides. An existing row keeps its enabled state.