Recurring tasks
Run jobs on a cron schedule — a YAML file (SolidQueue-compatible) or the config hash.
Install the tables#
Recurring tasks need two tables — one for the task definitions, one for the execution ledger that keeps a schedule from firing twice. Add them:
rails generate pgbus:add_recurring # add the migration + config/recurring.yml
rails generate pgbus:add_recurring --database=pgbus # for a separate database
rails db:migrateThe generator also writes a starter config/recurring.yml. A scheduler process (part of the supervisor) reads your tasks, syncs them to the database, and enqueues each on its schedule.
Declare tasks in recurring.yml#
SolidQueue-compatible; environment-scoped or flat.
The generated file is compatible with SolidQueue's recurring.yml, so a migration is mostly a copy. Each task is a key with a schedule and either a class (an ActiveJob class) or a command (Ruby to run):
production:
periodic_cleanup:
class: CleanupJob
queue: maintenance
args: [1000, { batch_size: 500 }]
schedule: every hour
daily_report:
class: DailyReportJob
schedule: "0 8 * * mon-fri"
description: Generate the daily business reportproduction: top-level key) or flat. Point at it with config.recurring_tasks_file if it lives elsewhere.Or declare them in the initializer#
If you'd rather keep everything in Ruby, set config.recurring_tasks to a hash of the same shape:
Pgbus.configure do |c|
c.recurring_tasks = {
periodic_cleanup: {
class: "CleanupJob",
schedule: "every hour",
queue: "maintenance",
args: [1000, { batch_size: 500 }]
}
}
endSchedule syntax#
Cron expressions or natural language, parsed by fugit.
Schedules are parsed by fugit, so both cron expressions and natural-language phrases work:
| Schedule | Means |
|---|---|
"0 2 * * *" | Every day at 2:00 AM |
"*/5 * * * *" | Every 5 minutes |
"every hour" | Every hour at :00 |
"every day at 2am" | Daily at 2:00 AM |
"@daily" | Daily at midnight |
"0 9 * * mon-fri" | Weekdays at 9:00 AM |
class: vs command:#
A task runs either a job class or an inline command — one is required. Use class: to enqueue an ActiveJob (the usual case); use command: for a one-off snippet with no job class:
cleanup_old_records:
command: "OldRecord.where('created_at < ?', 30.days.ago).delete_all"
schedule: every day at 3amcommand: string is evaluated as Ruby in the worker — keep it to trusted, version-controlled snippets. For anything non-trivial, prefer a class: job you can test.