Version v0.1 · dotnet

Scheduling & Cron

How recurring scheduling works, including supported 5-field and 6-field cron formats.

Scheduling & Cron

DurableStack recurring scheduling is cron-driven.

Each recurring job stores its schedule definition, and the scheduler materializes due runs into the run queue.

How scheduling works

At runtime:

  • Recurring job definitions are initialized/upserted at startup.
  • Scheduler checks for due recurring jobs.
  • For each due job, DurableStack enqueues a run and computes the next occurrence.
  • Catch-up behavior is controlled by options.Recurring.CatchUpPolicy.

The same job can still be manually enqueued through IDurableStackClient.

Supported cron formats

DurableStack supports both:

  • 5-field cron: minute hour day-of-month month day-of-week
  • 6-field cron: second minute hour day-of-month month day-of-week

Examples:

  • 5-field: */5 * * * *
  • 6-field: */20 * * * * *

Any other field count is invalid.

Common examples

  • Every minute: * * * * *
  • Every 5 minutes: */5 * * * *
  • Every hour at minute 0: 0 * * * *
  • Every day at 02:00: 0 2 * * *
  • Weekdays at 08:30: 30 8 * * 1-5
  • First day of month at midnight: 0 0 1 * *
  • Every 20 seconds (6-field): */20 * * * * *

Attribute examples

using DurableStack.Core.Abstractions;
using DurableStack.Hosting.DependencyInjection;

[DurableJob(Name = "report-hourly")]
[RecurringJob("0 * * * *", TimeZone = "UTC")]
public sealed class HourlyReportJob : IDurableJob
{
    public Task ExecuteAsync(JobContext context, CancellationToken cancellationToken = default)
        => Task.CompletedTask;
}

[DurableJob(Name = "heartbeat-every-20-seconds")]
[RecurringJob("*/20 * * * * *", TimeZone = "UTC")]
public sealed class SubMinuteHeartbeatJob : IDurableJob
{
    public Task ExecuteAsync(JobContext context, CancellationToken cancellationToken = default)
        => Task.CompletedTask;
}

Catch-up policy interaction

  • SkipMissed (default): jumps to the next future slot after downtime.
  • CatchUp: materializes missed slots incrementally until current time.

Use SkipMissed for most production workloads unless strict missed-slot replay is required.

Time zones

Cron expressions are evaluated in the RecurringJob(TimeZone = "...") IANA zone.

  • Default zone is UTC.
  • Windows time zone IDs are not supported.

For more detail, see Time Zones.

Practical guidance

  • Prefer 5-field cron for normal business schedules.
  • Use 6-field cron only when sub-minute cadence is truly required.
  • Keep AllowConcurrentRuns = false unless overlap is intentional.
  • Validate cron expressions in tests for critical schedules.