Cron Scheduling
Note
This feature requires a Zizq pro license on the server.
Zizq supports running jobs periodically, using a cron scheduling system that is managed atomically by the Zizq server. Multiple schedules can be defined, and any number of entries can exist on each schedule. Both schedules and individual entries within a schedule can be paused and resumed. Explicit time zones are supported.
The official Zizq Ruby client exposes this functionality through
Zizq::Crontab.
Defining a Schedule
Schedules are defined by your application, not by configuration files on the
Zizq server. Your application includes a Zizq.define_crontab { ... } block
somewhere in its startup code (e.g. in a Rails initializer for a Rails app).
Schedule definitions are idempotent. There is no requirement to designate one particular process as the process that defines the schedule. If you have 50 application processes and they all define the same schedule on startup, that is fine and expected.
Schedule defintions are also mutable. If you change a schedule between application versions, Zizq is smart enough to retain existing entries, replace modified entries, and delete removed entries.
The structure of a crontab definition is one outer Zizq.define_crontab { ... },
and a series of cron.define_entry(...).enqueue(...) calls inside the block.
Each entry is assigned a name known to your application, which uniquely identifies that entry within the schedule.
Both 6-field (with seconds) and standard 5-field cron expressions are accepted.
Zizq.define_crontab("my_cron", timezone: "Europe/London") do |cron|
# Incrementally refresh data in the data warehouse every 15 minutes.
cron.define_entry("refresh_data_warehouse", "*/15 * * * *").enqueue(
RefreshDataWarehoseJob,
incremental: true,
)
# Send the daily digest email at 9am (London) every day.
cron.define_entry("send_daily_digest", "0 9 * * *").enqueue(SendDailyDigestJob)
# Run the log rotation process at midnight UTC.
cron.define_entry("rotate_logs", "0 0 * * *", timezone: "UTC").enqueue_raw(
queue: "system/maintenance",
type: "rotate_logs",
priority: 100,
payload: {},
)
end
The Zizq server will push jobs to the queue and advance the schedule atomically. There is no risk that a job will be enqueued twice for the same schedule tick. However, because Zizq simply enqueues the job without waiting for completion, you could end up with overlapping jobs if they are scheduled too close together and take a long time to run. If this is a concern for your application, you should combine cron scheduling with Unique Jobs in order to prevent duplicate enqueues.
Inspecting Existing Schedules
Once defined, cron schedules can be fetched and inspected by your application
by using Zizq.crontab("..."). Some management operations such as pause/resume
are also available on the returned schedule.
Note
Zizq.crontabis lazy. Requests to fetch the schedule data are only sent to Zizq server when the data is first accessed.
Zizq.crontab("my_cron").paused?
Zizq.crontab("my_cron").entries.each do |name, entry|
puts "#{name}: #{entry.expression} (#{entry.job.type}) last enqueued: #{entry.last_enqueued_at}"
end
Pausing & Resuming Schedules
Schedules can be paused and resumed at two distinct levels:
- At the outer
Crontablevel. - Per-entry within the schedule.
If the Crontab itself is paused, no entries within that schedule will execute
even if they are not paused.
# Pause/resume entire schedule
Zizq.crontab("my_cron").pause!
Zizq.crontab("my_cron").paused? # true
Zizq.crontab("my_cron").paused_at # ~ now
Zizq.crontab("my_cron").resume!
Zizq.crontab("my_cron").paused? # false
Zizq.crontab("my_cron").paused_at # ~ before
Zizq.crontab("my_cron").resumed_at # ~ now
# Pause/resume individual entry
Zizq.crontab("my_cron").entry("refresh_data_warehouse").paused? # false
Zizq.crontab("my_cron").entry("refresh_data_warehouse").pause!
Zizq.crontab("my_cron").entry("refresh_data_warehouse").paused? # true
Zizq.crontab("my_cron").entry("refresh_data_warehouse").paused_at # ~ now
Zizq.crontab("my_cron").entry("refresh_data_warehouse").resume!
Zizq.crontab("my_cron").entry("refresh_data_warehouse").paused? # false
Zizq.crontab("my_cron").entry("refresh_data_warehouse").paused_at # ~ before
Zizq.crontab("my_cron").entry("refresh_data_warehouse").resumed_at # ~ now
When paused, Zizq stops enqeueing jobs but continues to advance the schedule.
It is also possible for the paused state to be specified within the schedule definition itself, which is useful e.g. if schedule pausing needs to be coupled to app deployments (e.g. as part of a phased rollout of a complex change).
Zizq.define_crontab("my_cron", paused: true) do |cron|
# ...
end
Tip
define_entryalso acceptspaused: trueorpaused: false.
Deleting a Schedule
Entire Zizq::Crontab schedules can be deleted, along with all of their
entries by calling #delete! on the crontab instance.
Zizq.crontab("my_cron").delete!
Adding, Replacing or Deleting Entries within a Schedule
In general you should just define your schedule(s) in your application startup
code using Zizq.define_crontab { ... } and let Zizq keep that schedule in
sync. However it is also possible to add/replace entries dynamically by calling
define_entry directly on the Crontab instance.
Zizq.crontab("my_cron").define_entry("example", "* * * * *").enqueue(ExampleJob)
If "example" already exists on this schedule, it is replaced with the new
entry, otherwise the new entry is appended to the schedule.
Call #delete! on an entry to remove it from the schedule.
Zizq.crontab("my_cron").entry("example").delete!