Skip to main content

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 cleanup that keeps the history from growing forever. Work like that runs on a clock, not on a request.

The Job Scheduler is where that 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 runs one fixed entry: every minute it looks for schedules that are due and hands them to the queue.

Screenshot placeholder: scheduler.overview.png
The 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.

One Purge Job covers all five kinds of cleanup, so there is a single place to say when housekeeping happens and how long each entity is kept. A fresh instance already has the 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.

Daylight saving time: an expression that points into the hour the clock skips or repeats runs twice in October and not at all in March. For a job that has to happen exactly once a day, pick a time outside 02:00 to 03: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.

Screenshot placeholder: scheduler.render-form.png
The 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 has 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:create grants what a render needs, the way scan2print:use does 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.

The Purge Job is deletable like any other schedule. With no active purge schedule, nothing cleans up history records, print tasks, mail tasks or orphaned files any more, and the disk keeps filling. The overview warns while that is the case. To bring the schedule back, use New and then Purge Job in the frontend, or run php artisan scheduler:create-purge-job on the server.

What has to be running

The Job Scheduler depends on the two background processes VeloxFactory runs in production.

Process Why
Scheduler The minute tick that finds due schedules. Without it nothing fires at all.
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.