Scheduling¶
Every schedule has one trigger. A trigger answers a single question: given an instant, when is the next occurrence strictly after it?
Intervals¶
beat.schedule(name="ping", task=ping, every="30s") # also "5m", "1h", "1d" or a timedelta
Occurrences fall on start_at + k * every. The default start_at is the Unix epoch, so a
schedule keeps the same grid across restarts and deployments, and delays never accumulate:
a tick that runs late does not push the next occurrence back.
every is elapsed time. "1d" is exactly 24 hours whatever the timezone; use a cron schedule
for "every day at 09:00 local time".
Cron¶
beat.schedule(name="report", task=report, cron="0 9 * * 1-5", timezone="Europe/Paris")
Five fields: minute, hour, day of month, month, day of week. Lists (1,15), ranges (1-5),
steps (*/15, 10-50/10) and names (jan, mon-fri) are supported; 0 and 7 both mean
Sunday. When both day fields are restricted, a day matches if either matches, as in POSIX cron.
Expressions that can never match, such as 0 0 30 2 *, are rejected when declared. Rare but valid
ones, such as February 29 falling on a Sunday, are found: the search covers the whole 400-year
Gregorian cycle.
Cron schedules are evaluated in their own timezone; see timezones for daylight saving rules.
What happens at each tick¶
- The leader reads the schedules whose
next_run_atis due, oldest first, 500 at a time. - For each, the misfire policy decides which occurrences to run.
- Each occurrence is claimed (one row, identified by its idempotency key) and the schedule's
next_run_atadvances, in the same transaction. - Claimed occurrences are submitted to the task backend.
- The scheduler sleeps until the next useful moment: the next due schedule, the next retry or
the next lease renewal, at most
max_sleep(60 s by default). On PostgreSQL a schedule change wakes it immediately throughLISTEN/NOTIFY.
Execution statuses¶
| Status | Meaning |
|---|---|
requested |
an extra run asked for from the admin or the command line |
claimed |
the occurrence is reserved, submission pending |
dispatched |
the backend accepted the task |
submit_failed |
the backend refused it; a retry is scheduled |
failed |
the first submission and max_submit_retries retries (5 by default) all failed |
skipped |
the misfire policy decided not to run it |
completed, task_failed |
the task finished (tracked for Django Tasks) |
Submission retries back off exponentially: 5 s, 10 s, 20 s and so on, capped at 5 minutes.
Code and store schedules¶
Schedules declared in code are published to the store when a scheduler starts (and by the
command line). Schedules created in the Django admin live in the store only. A code schedule
removed from the code stays enabled until you run schedule prune, because during a rolling
deployment an old process would otherwise disable schedules the new release just added.