schedule:run

* * * * * cd /srv/app && php mitosis schedule:run

Requires Clarity 1.5.0 or newer. That one line is the entire crontab, for the life of the application. Every tick works out which registered jobs are due, claims each one for the cluster, runs them, and records what happened. The schedule itself lives in app/routes/cli.php — see Scheduler.

Put the line on every node. Claiming a job is an insert against a unique index, so three nodes give three chances a due job runs and no chance it runs three times.

Leave off the /dev/null redirect

A tick where nothing was due, or where every due job was already claimed by another node, exits 0 in silence. That is on purpose: every line this command writes becomes a cron mail, and a heartbeat that greets you sixty times an hour teaches you to filter it — after which the one line that mattered is filtered too.

So the output is the alert, and the reflexive > /dev/null 2>&1 throws the failures away along with the quiet. The kernel writes errors to stdout like everything else, so there is nothing for 2>&1 to rescue either. Leave both off.

This departs from the always-print-success habit of cache:clear and the rest, deliberately. Four things get exactly one line each:

✓ sessions:prune: ran in 41ms.
✗ reports:build: failed after 1122ms — SQLSTATE[42S02]: Base table or view not found
ℹ sessions:prune: skipped, the previous run is still going.
✗ reports:build: 1 abandoned run reaped — started over 240 minutes ago and never reported an outcome.

The tick exits 1 if any job failed or any dead run was reaped, and 0 otherwise — including when it did nothing at all.

Options

--verbose   narrate the whole tick: jobs that were not due, and slots another node claimed
--context   select a database connection, as everywhere else

--verbose is for a human running the command by hand. Do not put it in the crontab, or you have re-created the mailbox problem the silence exists to avoid.

A failing job does not stop the tick

Every job runs inside its own try/catch. The kernel catches a Throwable, prints its message with no trace and abandons the process — so an escaping exception would kill every later job in the same tick and reduce your diagnostic to a single line. Instead the failure is recorded against its own run row, the message is printed, and the next job runs.

If the table is missing

✗ Scheduler is not installed: the `scheduled_runs` table is missing, so no job can be claimed or recorded. Run `php mitosis schedule:install` once.

Checked up front and exits 1, rather than being discovered as a database error once a minute for as long as the crontab entry outlives the install step. Run schedule:install. A tick with no jobs registered at all does not so much as open a connection.

What it guarantees, and what it doesn't

At most one run per job per minute, cluster-wide. Not at-least-once. A minute in which every node is down is a minute in which the job does not run, and no later tick makes it up: only the current minute is ever evaluated. A tick that starts more than 60 seconds late skips a minute entirely.

Expressions are read on the server's local clock, matching what the system cron itself reads — so every node in a cluster must be set to the same timezone, or they are running two different schedules.

Next steps

  • Scheduler — registering jobs, expressions, and the staleness window.
  • schedule:install — the table this command claims against.
  • schedule:list — what is registered, and how each job's last run went.