ACTIAS

Scheduled work

Cron handlers for site-wide schedules, alarms for deadlines an entity owns.

Cron

A cron handler runs on a schedule for the whole script.

local cache = kv "cache"
local db = database "catalog" { migrations = "migrations/catalog" }

on "cron:*/2 * * * *" (function(event)
    local soon = db:read(
        "SELECT id, title FROM listings WHERE state = 'open' \
         ORDER BY created_at DESC LIMIT 8")
    cache:set("ending-soon", { at = event.scheduled_at, items = soon })
end)
ExpressionFields
*/2 * * * *minute, hour, day, month, weekday
0 */2 * * * *the same, with seconds at the front

The event carries event.cron, the handler's full name including the prefix ("cron:*/2 * * * *"), and event.scheduled_at, the platform clock in milliseconds when the run started.

The schedule arms itself the first time a worker touches the revision. The next occurrence is armed before your handler runs, so nothing the handler does can kill the schedule.

Cron or alarm

Use cron when the schedule belongs to the site: a nightly digest, a cache rebuild, a periodic cleanup.

Use an alarm when the deadline belongs to one entity: this auction closes at 14:02, this trial ends on the 30th. The entity arms it, so no query has to find overdue rows.

One cron scanning a table of deadlines gets slower as the table grows. A million objects with their own alarms cost nothing until they fire.

Gotchas

  • Downtime collapses. A schedule that should have fired four times while no worker was up fires once when one returns, not four times.
  • A failing handler is logged and not retried. There is no cron retry; the next occurrence is the next attempt.
  • scheduled_at is when the run started, so it drifts from the nominal minute. Do not use it as an occurrence identifier.