Schedules
Purpose#
A schedule makes a job run without anyone pressing a button. You give it a cron expression; Abrq DIP triggers the job when the expression comes due.
One rule shapes everything on this page:
Note. Schedules attach to jobs, not to individual tasks. Per-task schedules were removed. If you want a single task to run on a timer, put it in a job of its own and schedule that job. This keeps one answer to "what is running right now" instead of two competing timers on the same table.
Schedules live at /schedules.
Before you begin#
- A job to attach the schedule to. Create it first — see Jobs.
- A working manual run. Schedule a job you have already run by hand at least once. A schedule is a bad place to discover that a connector role does not resolve.
- Times are UTC. Cron expressions are evaluated in UTC, and every time
shown on the schedules dashboard — last fire, next fire — is UTC. There is no
per-schedule timezone. If your business day is
Asia/Riyadh(UTC+3), a 02:00 local batch is0 23 * * *UTC on the previous day. Work the offset out once and write it in the schedule's description.
Required role#
| Action | Global role | Project capability | Typical project role |
|---|---|---|---|
| View schedules | viewer or higher |
schedule.view |
any project member |
| Create, edit, pause, resume, delete a schedule | editor or higher |
schedule.manage |
Developer (unprotected environments only), Maintainer, Owner |
schedule.manage is one of the six environment-gated write capabilities. In a
protected environment a Developer cannot create or change a schedule and is
refused with the reason protected_environment; a Maintainer or Owner can. See
Environments and promotion.
The global admin role is an implicit Owner on every project.
Create a schedule#
- Go to /schedules and choose New schedule, or open a job and add a schedule from there.
- Pick the job to run.
- Enter the cron expression.
- Give it a name and, ideally, a description that records the intent and the local-time equivalent.
- Save. The schedule is enabled, and its next fire time is computed immediately.
Cron expressions#
Expressions are validated with croniter when you save. An expression that croniter cannot parse is rejected at save time, so a schedule never sits in the system quietly failing to be understood.
Standard five-field cron, all times UTC:
┌─ minute (0–59)
│ ┌─ hour (0–23)
│ │ ┌─ day of month (1–31)
│ │ │ ┌─ month (1–12)
│ │ │ │ ┌─ day of week (0–6, Sunday = 0)
│ │ │ │ │
* * * * *
| Intent | Expression |
|---|---|
| Every hour, on the hour | 0 * * * * |
| Every 15 minutes | */15 * * * * |
| Daily at 01:30 UTC | 30 1 * * * |
| Weekdays at 06:00 UTC | 0 6 * * 1-5 |
| First of the month, 00:00 UTC | 0 0 1 * * |
Tip. Avoid scheduling everything on the hour. Ten jobs at
0 * * * *all wake up in the same beat tick and contend for the same connectors and workers. Spread heavy jobs across the hour —7 * * * *,23 * * * *, and so on.
How firing works#
A beat tick runs every 60 seconds. On each tick Abrq DIP:
- Finds every enabled schedule whose next fire time has come due.
- Advances the next fire time first, before triggering anything.
- Triggers the job run.
Step 2 is the important one. Because the next fire time moves forward before the run is dispatched, a job that takes 90 minutes under an hourly schedule is never double-fired by the tick that lands while it is still running — the schedule has already moved on.
Two consequences worth planning around:
- The finest useful granularity is one minute. A cron expression cannot fire more often than the tick.
- A fire can land up to 60 seconds after the nominal cron time. Schedules are for batch work, not for sub-minute latency.
Note. Advancing the fire time first means a missed window is skipped, not queued. If the platform is down across three hourly slots, you get one run when it comes back — not three catch-up runs.
Pause and resume a schedule#
Pausing is the safe way to stop a recurring run without losing its definition.
- Open the schedule from /schedules.
- Choose Pause.
A paused schedule is skipped by every beat tick. Its cron expression, its job binding, and its history stay intact. Resume it and the next fire time is recomputed from the current moment forward — resuming does not replay the runs that were skipped while it was paused.
Pause rather than delete when you are:
- Freezing batch work during a migration or an upgrade window.
- Investigating a failing job and do not want another run starting mid-triage.
- Temporarily handing a load over to a manual process.
Delete a schedule only when the recurrence itself is retired. Deleting a schedule does not delete the job, and it does not delete run history.
Read the schedules dashboard#
/schedules lists every schedule you can see, with:
| Column | What it tells you |
|---|---|
| Name | The schedule's label. |
| Job | The job it triggers. Follow it through to the job's run history. |
| Cron | The expression, as saved. |
| Enabled | Whether beat ticks will act on it. A paused schedule reads as disabled. |
| Next fire | The next UTC time the beat tick will trigger it. |
| Last fire | The last UTC time it triggered. |
Two quick reads that catch most problems:
- A next-fire time in the past means the schedule is paused or the beat worker is not running. Check the schedule's enabled state first, then check with your platform administrator that background processing is healthy — see Background processing.
- A last-fire time that keeps advancing while the job's run history does not means the trigger is firing and the run is being refused. Open the job's runs and read the refusal — the next section is the usual cause.
Field reference#
| Field | Type | Notes |
|---|---|---|
| Name | text | Identifies the schedule in lists and logs. |
| Description | text | Optional. The right place to record the local-time equivalent of the UTC cron. |
| Job | reference | The job to trigger. Required. A schedule cannot target a task. |
| Cron expression | text | Five-field cron, validated with croniter at save time. Evaluated in UTC. |
| Enabled | boolean | Off means paused: beat ticks skip it. |
| Next fire | timestamp (read-only) | UTC. Advanced by the tick before the run is triggered. |
| Last fire | timestamp (read-only) | UTC. |
Failure modes#
A scheduled run against a protected environment is refused#
This is the failure mode most likely to bite you, because it is invisible until you look.
A job run targeting a protected environment is refused before any task executes unless every connector role its tasks require resolves in that environment. When the trigger is a schedule, nobody is watching the screen: the schedule's last-fire time advances, the job records a refused run, and no data moves.
Cause: the environment is missing a source or destination binding that one
of the job's tasks needs — typically because a task was promoted into the
environment but the environment's connector bindings were never completed.
Resolution is fail-loud by design and never falls back to another environment's
connector.
Fix:
- Open the refused job run and read which environment and which role are named.
- Go to that environment and bind a connector to that role — see Environments and promotion.
- Run the job manually once to confirm, then leave the schedule to it.
Tip. Configure a
run_failednotification on the job so a refusal reaches a person instead of sitting in a table. See Notifications and alerts.
Other failures#
| What you see | Cause | Fix |
|---|---|---|
| Save rejected, invalid cron | croniter could not parse the expression. | Correct the expression — five fields, UTC. |
| Schedule fires up to a minute late | Expected. The beat tick runs every 60 seconds. | None. Do not use schedules for sub-minute work. |
| Missed windows are not replayed after downtime | Expected. The next fire time is advanced before triggering; there is no catch-up queue. | Run the job manually once to fill the gap. |
| The job never appears to double-run despite overlapping durations | Expected, and intended — the fire time advances before dispatch. | None. |
Cannot create or pause a schedule; reason protected_environment |
You hold the Developer role and the environment is protected. | Ask a Maintainer or Owner. |
Scheduled run fails with 403 license_expired |
The deployment's license has expired; runs are refused. | See Expiry and clock. |
See also#
- Jobs — what a schedule triggers
- Environments and promotion — protected environments and connector roles
- Executions — the cross-family run monitor
- Notifications and alerts