Cron Scheduling
View SourceShigoto includes a built-in cron scheduler that checks configured entries every minute and inserts jobs for matching schedules. On startup, it catches up on any intervals missed while the node was down (up to 60 missed minutes).
Configuration
Erlang (sys.config)
{shigoto, [
{pool, my_app_db},
{cron, [
{<<"daily_cleanup">>, <<"0 2 * * *">>, cleanup_worker, #{<<"days">> => 30}},
{<<"hourly_sync">>, <<"0 * * * *">>, sync_worker, #{}},
{<<"weekday_report">>, <<"30 9 * * 1-5">>, report_worker, #{}}
]}
]}Elixir (config.exs)
config :shigoto,
pool: :my_app_db,
cron: [
{"daily_cleanup", "0 2 * * *", :cleanup_worker, %{"days" => 30}},
{"hourly_sync", "0 * * * *", :sync_worker, %{}},
{"weekday_report", "30 9 * * 1-5", ReportWorker, %{}}
]Each cron entry is a 4-tuple {Name, Schedule, Worker, Args} or a 5-tuple
{Name, Schedule, Worker, Args, Opts} where Opts is a map.
| Field | Type | Description |
|---|---|---|
| Name | binary | Unique identifier |
| Schedule | binary | 5-field cron expression |
| Worker | module | Worker implementing shigoto_worker |
| Args | map | Args map passed to perform/1 |
| Opts | map | Optional. Supports timezone (see below) |
Timezones
By default schedules are matched against UTC. Set a timezone in the entry's
Opts map to match against a local wall-clock time instead:
{cron, [
%% 09:00 Stockholm time, DST-aware, every weekday
{<<"morning_report">>, <<"0 9 * * 1-5">>, report_worker, #{},
#{timezone => <<"Europe/Stockholm">>}},
%% fixed offset, no DST
{<<"utc_plus_2">>, <<"0 3 * * *">>, sync_worker, #{}, #{timezone => 2}}
]}timezone accepts:
| Value | Example | DST |
|---|---|---|
utc (default) | utc | n/a |
| integer hour offset | 2, -5 | no |
"+/-N" or "UTC" binary | <<"+2">> | no |
| named IANA zone | <<"Europe/Stockholm">> | yes |
Named IANA zones are resolved against the operating system's time zone database
($TZDIR or /usr/share/zoneinfo) for each instant, so daylight-saving
transitions are handled automatically. If a named zone cannot be resolved the
entry falls back to UTC and a warning is logged.
Cron Expression Syntax
Standard 5-field format: minute hour day-of-month month day-of-week
┌───────────── minute (0-59)
│ ┌───────────── hour (0-23)
│ │ ┌───────────── day of month (1-31)
│ │ │ ┌───────────── month (1-12)
│ │ │ │ ┌───────────── day of week (0-6, Sunday=0)
│ │ │ │ │
* * * * *| Syntax | Description | Example |
|---|---|---|
* | Any value | * * * * * (every minute) |
N | Exact value | 30 * * * * (at minute 30) |
N-M | Range | 0 9-17 * * * (hours 9 through 17) |
*/N | Step | */15 * * * * (every 15 minutes) |
N-M/S | Step within range | 0-30/10 * * * * (minutes 0, 10, 20, 30) |
N,M,O | List | 0,15,30,45 * * * * (specific minutes) |
Missed Cron Catch-Up
When the cron scheduler starts, it checks the last_scheduled_at for each
entry in the database. If any intervals were missed (e.g., the node was down),
it inserts jobs for up to 60 missed minutes. Jobs are deduplicated via unique
constraints to prevent double-execution.
How It Works
shigoto_crongen_server checks all entries once per minute- Matching entries trigger a job insert into the
defaultqueue - Jobs use a 60-second unique constraint to prevent duplicates
- Cron jobs follow the same retry, backoff, and pruning rules as regular jobs
Timezone notes
Named zones resolve against the operating system's IANA database
(/usr/share/zoneinfo), so every node in a cluster should run a consistent
tzdata version. A zone that cannot be resolved (missing or corrupt tzdata)
falls back to UTC with a logged warning rather than failing.
Cron matches at minute granularity under at-least-once semantics: on a DST fall-back the repeated wall-clock minute may match twice (unique-job de-dup within the 60s window prevents a duplicate enqueue), and on a spring-forward the skipped minute does not match. Schedule around 03:00 local to avoid the DST window if exact-once firing at those minutes matters.