The Job Scheduler: work that happens without you
Most of what VeloxFactory does happens because something asked for it: a request comes in, a report is rendered, a label goes to a printer. Some work has no caller, though. A shift report that should be in the supervisor's mailbox at six every morning. The nightly cleanup that keeps the history from growing forever. UntilWork nowlike thethat cleanup was wired into the deployment and the morning report was somebody else's cron entryruns on somea otherclock, machine.not on a request.
The Job Scheduler bringsis bothwhere intothat VeloxFactory.clock lives. Every recurring job is a record with a crontab expression, a timezone and an active flag, maintained in the frontend or through the API like any other master data. VeloxFactory itself only runs one fixed entry: every minute it looks for schedules that are due and hands them to the queue.
scheduler.overview.pngThe Job Scheduler overview with type, cron expression, next run, last run and status per schedule.
Two kinds of schedule
| Type | What it does |
|---|---|
| Render Job | Fires a complete render request on a schedule: parameters, data rows, printing, history record and the full mailing segment. Everything a render can do from the API, a schedule can do at four in the morning. |
| Purge Job | The housekeeping: print tasks, mail tasks, history records, orphaned files and the scheduler's own run log, each with its own retention in days. One schedule, one run, a fixed order. |
TheOne Purge Job replacescovers fourall separatefive nightlykinds jobsof thatcleanup, usedso there is a single place to besay configuredwhen inhousekeeping happens and how long each entity is kept. A fresh instance already has the .env file and scheduled in code. It is created for you by php artisan setup:base,schedule, deactivated and without a cron expression, so nothing runs until you decide when it should.
Saying when
A schedule accepts full five field crontab syntax, the same thing every operations person already knows. You do not have to write it by hand: the editor has a guided mode with frequency, time and weekday pickers, and an expression mode for everything the guided mode cannot express. Both write into the same field, and whichever you use, the next five run times are shown underneath while you type.
| Expression | Meaning |
|---|---|
0 6 * * 1-5 |
Weekdays at 06:00, the shift report. |
*/15 * * * * |
Every quarter of an hour. |
0 3 1 * * |
The first of every month at 03:00, the monthly statement. |
| empty | Never fires on its own. The schedule exists and can be started by hand, which is how you test one before switching it on. |
Every schedule carries its own timezone, defaulting to the timezone of the instance. That matters for a company that renders for a plant in another country: the expression says 06:00 and the timezone says which 06:00.
What happens when a schedule is due
Every minute VeloxFactory collects the schedules whose next run has arrived, moves each one to its following slot and hands it to the queue. The order matters: the next slot is written first, so a schedule can never fire the same slot twice, whatever happens afterwards.
Three things are deliberately not done.
- Missed slots are not made up. If the scheduler was stopped for three hours, the slots in between are gone. One late run within the grace window is fired, anything older is recorded as skipped so you can see that it happened.
- Runs of one schedule never overlap. A render that takes longer than its own interval simply keeps running; the next run is recorded as skipped rather than queued behind it. Two different schedules do run side by side.
- A run is not retried. A second attempt would print a second label and send a second mail. A failed run ends as an error and says why.
Who a render job runs as
A scheduled render is a real render request, so it needs somebody to make it. That somebody is the user who created the schedule, and the schedule carries one of that user's API tokens to run with. Nothing else changes: the same permissions apply, the same audit columns are filled, and the history record shows who and which token, exactly as if the request had come over HTTP.
Two consequences are worth knowing before you build a schedule around a colleague who is about to change roles.
- If the owner is set inactive, or the token is revoked or expires, the run stops before anything is rendered. It is recorded as skipped with the reason, and the failure mail goes out.
- Because a render job acts with its owner's permissions, only the owner and administrators may change its configuration or start it by hand. Everyone with the read permission can see it and read its log.
The token is referenced, never stored. VeloxFactory keeps only the hash of a token, so a schedule points at the token record you pick from a list, and revoking that token stops the schedule the same minute.
scheduler.render-form.pngThe form of a render job: cron builder, report config, API token, output settings, parameters with the placeholder switch and the mailing block.
The run log
Every run writes a record, whether it worked or not. The log carries the trace id, so a scheduled render can be followed through the history record, the print task and the mail task it produced, and back again.
| Status | Means |
|---|---|
Success |
Everything the schedule asked for happened. |
Warning |
The job ran, but something inside it did not: the render worked and the mail did not, a placeholder stayed empty, one purge step failed while the others finished. |
Error |
The job itself failed. The message from the API is on the record. |
Skipped |
The run never started: the previous one was still going, the owner or the token was not usable, or the slot was older than the grace window. |
Running |
In progress right now. |
A run also records whether it was fired by the clock or started by hand, so a test run is never mistaken for a scheduled one.
Being told when something breaks
A schedule that quietly stops working is worse than no schedule at all. Every schedule can name a mailer and a list of recipients for its failure mail, which goes out when a run ends as an error or is skipped. A warning does not mail: the render happened, and the detail is already on the history record or the mail task.
A schedule that fires every minute and fails every minute would otherwise fill a mailbox, so at most one failure mail per schedule per hour is sent. The runs in between count how many were suppressed, and the log still has every one of them.
Permissions
The Job Scheduler addshas one permission scope, scheduled-job:*, with the usual read, create, update, delete and full. The run log is covered by the same scope.
Two rules go beyond the usual pattern, both for the same reason: a schedule can act with somebody else's rights, or with nobody's.
- Creating a schedule implies being allowed to render, print and mail with it, so
scheduled-job:creategrants what a render needs, the wayscan2print:usedoes for the scan station. - A Purge Job has no owner at all, so whoever switches a cleanup step on has to hold the permission to delete what that step deletes. Turning on the history step needs the right to delete history records.
Deleting a schedule is not limited to its owner. Deleting only stops a schedule, it can never make one act with somebody's permissions.
php artisan setup:basescheduler:create-purge-job What has to be running
The Job Scheduler needsdepends on the two background processes VeloxFactory alreadyruns uses,in and it needs them for more than it used to.production.
| Process | Why |
|---|---|
| Scheduler | The minute tick that finds due schedules. Without it nothing fires at |
| Horizon | The runs themselves. They have their own queue and their own worker, so a scheduled render that takes minutes never blocks a thumbnail or a mail. |
Both belong under Supervisor in production. The Administration chapter has the details.