Skip to content

Schedules ​

A schedule starts Runs of a Task on a clock. You declare it in code, and the server creates the Runs.

Add a schedule to a Task ​

Pass runcoil.Schedule when you add the Task. This code is from the schedule example. It builds a daily report at 09:00 UTC, with SkipOverlap:

go
sched := schedule.Cron("0 9 * * *", schedule.SkipOverlap())

// ...

worker.NewTask("daily-report", runcoil.HandlerFunc(handler.dailyReport),
	runcoil.Schedule(sched),
	runcoil.Retry(runcoil.RetryPolicy{MaxAttempts: 3}),
)

A Task has at most one schedule, and a scheduled Run has no input. A new registration of the Task without runcoil.Schedule removes its schedule.

Pick a schedule ​

The schedule package has three constructors.

ConstructorTicksExample
schedule.Every(d)Every d, at least 1 secondschedule.Every(time.Minute)
schedule.Cron(expr)A five-field cron expressionschedule.Cron("0 9 * * *")
schedule.Daily(times...)Each time of day you listschedule.Daily(schedule.At(9, 0))

schedule.Every counts ticks from the Unix epoch, so every tick is a multiple of d. An interval that does not divide 24 hours falls at a different time of day on each day. For a fixed time of day, use Cron or Daily.

schedule.At(hour, minute) takes an hour from 0 to 23 and a minute from 0 to 59. A Cron expression must not start with TZ= or CRON_TZ=.

Read schedules in UTC ​

Every schedule runs in UTC. There is no other time zone. The server computes the ticks.

Skip overlapping ticks ​

Pass schedule.SkipOverlap() to Cron or Every. At a tick, if the previous Run of the schedule has not finished, the server creates no Run for that tick. Without it, a new Run starts beside the old one.

A Run that is still queued counts as unfinished.

Use the buttons to compare what happens with and without SkipOverlap, and when the server is down.

schedule.Every(time.Minute, schedule.SkipOverlap())

A schedule ticks every minute in UTC. With SkipOverlap, the server creates a Run for a tick only when the previous Run of the schedule has finished. Run 2 is still running at the 12:02 and 12:03 ticks, so those two ticks create no Run.

  1. 12:00 UTC tick. The server creates Run 1.
  2. 12:01 UTC tick. The server creates Run 2.
  3. 12:02 UTC tick. Run 2 is still running and SkipOverlap is set, so the server creates no Run for this tick.
  4. 12:03 UTC tick. Run 2 is still running and SkipOverlap is set, so the server creates no Run for this tick.
  5. 12:04 UTC tick. The server creates Run 3.
  6. 12:05 UTC tick. The server creates Run 4.
  7. 12:06 UTC tick. The server creates Run 5.

Fail a Run that starts late ​

Pass runcoil.StartDeadline(d) to fail a Run that has not started d after it was due. For example, no worker may have been ready. Without a deadline, a late Run still runs.

Missed ticks create no Run ​

If a tick passes while the server is down, the server creates no Run for it. The server does not catch up when it returns. The next tick creates a Run as usual.

Know which tick created a Run ​

Inside a handler, runcoil.Info(ctx).ScheduledAt is the tick that created the Run. It is zero for a Run that a schedule did not create. A retry keeps the original tick. The schedule example uses it to build the same report day after a retry past midnight:

go
info := runcoil.Info(ctx)
day := info.ScheduledAt.UTC().Truncate(24*time.Hour).AddDate(0, 0, -1)

Try it locally ​

The schedule example runs daily at 09:00 UTC. To see it tick without waiting, set REPORT_INTERVAL, which replaces the daily schedule with schedule.Every:

sh
REPORT_INTERVAL=3s go run ./examples/schedule

Runcoil is pre-release. The API may change before 1.0.