Scheduler
Contents
The scheduler runs named tasks at a time, at an interval, or on a cron schedule, in a cluster or on a single node.
Scheduled jobs
A scheduled job names a task by its descriptor, the config to run it with, the user to run it as, and a calendar that says when. Jobs are stored in the system repository and survive restarts. They are created, listed, modified and deleted through the scheduler library, or created from the platform’s scheduler configuration file. The full shape of a job is the ScheduledJob type.
Calendars
A job’s calendar has a type and a value, whose format depends on the type. The exact rules for each are in the Schedule type.
One-time
Runs once, at an instant in UTC. A time already in the past runs as soon as the job is created. The job stays afterwards, with lastRun recording that it is done, so its name cannot be reused to run it again.
Where the name is used for a single occasion, deleteAfterRun: true removes the job once its task has been submitted.
{type: 'ONE_TIME', value: '2021-01-01T10:30:00.00Z', deleteAfterRun: true}
Cron
Runs at the calendar positions a UNIX cron expression names, in the given time zone. Use it for work that has to happen at a particular time of day.
{type: 'CRON', value: '0 5 * * *', timeZone: 'GMT+2:00'} // 05:00 every day
Fixed rate
Runs at an interval, given as an ISO-8601 duration and measured between the starts of two runs. Use it for work that belongs at an interval rather than at a time of day. The interval is not anchored to the clock and does not survive a restart or a move to another node, so the next run may shift by up to one interval.
{type: 'FIXED_RATE', value: 'PT5M'} // every five minutes
Where jobs run
Exactly one node in a cluster runs the scheduler at a time, chosen among the nodes that allow it. The platform’s acceptScheduling setting excludes a node, typically the master nodes. The scheduling node only submits the tasks; each task then runs on any node that has the application providing it and accepts distributable tasks.
A job does not start a run while its own previous run is still going. What becomes of the run that was due depends on the calendar: a cron occurrence is skipped, since a cron occurrence is a calendar position and a missed one is missed, while a fixed-rate run waits and starts once its predecessor finishes.
| Not overlapping is best effort, not a guarantee. Two runs of the same job can still overlap when the cluster is partitioned, or when the record of the previous task is no longer available to be checked. A task that must never run concurrently with itself needs a lock of its own. |
| The guarantee is per job, not per task. Several jobs may name the same descriptor, and nothing relates them - they can run at the same time as each other. |
What a scheduled task receives
A scheduled task runs with the config of its job as parameters, exactly as if it had been submitted through the task API.
It also learns which task ran for the job last, through the schedule.lastTaskId attribute of its context. That is useful where each run continues from where the last one reached - fetching the previous run’s result to process only what has changed since. The value is absent on a job’s first run and on a job whose last run left no record.
import {get as getContext} from '/lib/xp/context';
import {get as getTask} from '/lib/xp/task';
export function run(): void {
const lastTaskId = getContext().attributes['schedule.lastTaskId'] as string | undefined;
const previous = lastTaskId ? getTask(lastTaskId) : null;
if (previous) {
log.info('Previous run: %s', previous.state);
}
}
The chain only ever names tasks the scheduler let run, so an id that is there is one whose run actually happened.
System jobs
XP ships two jobs of its own, disabled by default: vacuum, which deletes unused blobs, and audit log cleanup, which deletes old audit records. They are enabled and tuned in the platform’s scheduler configuration.