Elarion
Scheduling

Schedules

Define when a job runs with FixedRate, FixedDelay, Cron, or an initial-delay one-off — using compact duration literals and config placeholders.

Every schedule-bearing method sets exactly one of FixedRate, FixedDelay, Cron, or an InitialDelay-only one-time startup schedule. The authoring API is intentionally string-based.

Duration literals

Use compact duration literals or invariant TimeSpan text:

"1000ms"   "1s"   "15m"   "6h"   "1d"   "00:00:01"

There are no numeric TimeUnit-style variants — string values are the single API, because the same property must support literals and configuration placeholders.

Schedule properties

PropertyMeaning
FixedRateGrid-aligned interval between due times. Long runs or host pauses skip missed slots instead of producing a catch-up burst.
FixedDelayDelay measured from completion of one occurrence to the due time of the next. Matches polling loops that should wait after each pass.
CronFive-field or six-field cron expression. Six fields include seconds; five-field Unix syntax defaults seconds to 0.
InitialDelayOptional startup delay for FixedRate / FixedDelay. Not valid with Cron.
RunOnStartWhether an interval job is due immediately at host start. Defaults to true; not valid with Cron.
TimeZoneOptional time zone id for cron evaluation. Defaults to UTC.
EnabledBoolean literal or placeholder evaluated before every occurrence.
GroupOptional serialization key for jobs that share an external resource.
OverlapWhat to do when the same job already has an active occurrence. Defaults to Skip.
MisfirePolicyWhat to do when a grid occurrence is so late that later ones are also due. Defaults to FireOnce.
MaxConcurrentRunsJob-local cap when Overlap = AllowConcurrent. 0 means no job-local cap.

See Overlap & misfire for the last few in detail.

Examples

[ScheduledJob(
    "mailbox.poll",
    FixedDelay = "${Mailbox:PollingInterval:-15m}",
    Group = "mailbox",
    Enabled = "${Mailbox:Enabled:-false}",
    Overlap = ScheduledJobOverlap.Skip)]
public async ValueTask PollAsync(CancellationToken ct) {
    await mailboxProcessor.PollAsync(ct);
}

[ScheduledJob(
    "reports.daily",
    Cron = "0 0 3 * * *",
    TimeZone = "Europe/Vienna")]
public async ValueTask RunDailyReportsAsync(CancellationToken ct) {
    await reports.RunDailyAsync(ct);
}

[ScheduledJob("warmup.searchIndex", InitialDelay = "10s")]
public async ValueTask WarmSearchIndexAsync(CancellationToken ct) {
    await searchIndex.WarmAsync(ct);
}

Disabling a cron trigger

Use Cron = "-" to disable a cron trigger (matching Spring's disabled-cron sentinel). This is most useful with a placeholder default:

[ScheduledJob("reports.daily", Cron = "${Reports:DailyCron:--}")]
public async ValueTask RunDailyReportsAsync(CancellationToken ct) {
    await reports.RunDailyAsync(ct);
}

Configuration placeholders

Placeholders use Spring-style syntax and are re-resolved for every recurring occurrence:

PlaceholderBehavior
${Jobs:Interval}Resolve Jobs:Interval; throw during schedule resolution if not configured.
${Jobs:Interval:-15m}Resolve Jobs:Interval; use 15m when not configured or blank.

If a previously valid schedule later becomes invalid, the scheduler logs the error and keeps the last valid schedule — the recurring chain does not die while configuration is being fixed.

Job references

Generated registries also emit job references so admin/manual-run code does not repeat string literals:

var jobName = MyAppApplicationScheduledJobRegistrationJobReferences.ReportsDaily.Name;

On this page