Reference · Jobs and services
schedule
schedule(str | int, fn, map | null=) -> nullWhat it does
Runs a function on the server on a timetable: every so often, given as a duration
(seconds, or "30s", "5m", "1h", "1d", "2w", "1y", counted from 1970 in UTC
so that "5m" runs at :00, :05, ...), or at the times of a cron line (minute hour day month weekday, in UTC, with *, 1-5, */15 and lists, or @hourly, @daily,
@weekly, @monthly, @yearly). Written at the top of a program (or in a function
the top level calls as it starts), which quillo run then serves. A job is named after its function, or by { name }; no two alike. It
gives null. See Scheduled jobs.
Examples
fn clean_sessions() = db.exec("delete from sessions where expires < ?", now())fn nightly_report() { for t in db.tenants() { with_tenant(t, report) } }schedule("5m", clean_sessions) // every 5 minutes, on the clockschedule("0 3 * * *", nightly_report) // cron: 03:00 UTCschedule("@daily", () => db.exec("delete from tokens where expires < ?", now()), { name: "expire-tokens" })schedule(60, ping, { name: "ping-minute" }) // secondsEdge cases
- A job first seen (at start, or after a reload) waits for its next time, counted from
when the server first looks: started at 04:01,
"2 * * * *"first runs at 04:02, and a nightly job of a server deployed each day runs that night. It never runs at once. The clock is looked at once a second. - Only as the program starts: called in a route, a
server fn, a job or a tick, it is a runtime error (a 500 in a route), where the job was said to be added and never ran.quillo checksays so too, and reads the arguments written out as the start does. - A job runs like a request: waiting on the database holds up no request, its errors
are printed, and it is logged (
POST /_quillo/job/clean_sessions?at=... -> 200). One still running when it is due again is skipped that time. - Names are letters, digits,
_,-and., at most 64: a lambda isjob-<line>unless{ name }says, and the same function twice needs a name for the second. - Weekdays in a cron line are numbers 0 to 7 (0 and 7 are Sunday), not names: `"0 9 *
- 1-5"
, notmon-fri`. The fields may be separated by spaces or tabs. A duration must be more than 0.
- 1-5"
- One name twice is refused by
quillo checkonly where both calls surely run (the top level's own statements, or those of a function it calls); where both may run, in two arms of anifor amatch, after&&, or in a loop, it is a warning, and the start refuses the second only if it really comes. - Several servers on one database run each job once per due time; with a database per
tenant, each server runs its jobs, and the job loops over the tenants with
with_tenant. Thequillo runoption--no-jobsturns them off on a server (quillo run main.qwe --no-jobs; see the options). - A job may say what it does as any function does, and is held to it each time it runs:
fn nightly() with db, time { ... }, thenschedule("0 3 * * *", nightly). - A program with a
schedulein it is a server:quillo runserves it and keeps running, which is why the examples here are not run.
Errors
A first argument that is neither a duration nor a cron line (a wrong unit, 0, a cron
line without 5 fields, a field out of range, an unknown @name), a second that is not
a function, a bad or repeated name, and options that are not a map are runtime errors
when the program loads, not error values; so is a call once the program has started.
schedule("soon", () => 1)line 1: `schedule` takes a duration such as "5m" or a cron line such as "0 3 * * *", got "soon"schedule("0 9 * * mon-fri", () => 1)line 1: `schedule`: the weekday field "mon-fri" is not a number from 0 to 7, a range, a list or a stepfn clean() = 1schedule("5m", clean)schedule("1h", clean)line 3: `schedule`: there is already a job named "clean"; give this one another with { name: ... }fn clean() = 1route POST /admin/jobs = { schedule("5m", clean); "added" }// `schedule` adds a job as the program starts, at its top level: called here, once it// has started (in a route, a server fn, a job or a tick), the job would never runChaining
A job's function takes no arguments; give it what it needs by closing over it, and keep the work in a named function so the log names it:
fn remind(days: int) = () => db.query("select * from invoices where due < ?", now() + days * 86400000) | map(send_reminder)schedule("@daily", remind(3), { name: "remind-3d" })schedule("@daily", remind(7), { name: "remind-7d" })See also
every, with_tenant, db.tenants, sleep, ready
Thread 0 posts
On schedule(): questions, tips, corrections, and code that uses it.
No posts yet. The first one opens the thread.
Edit this page on GitHub · every example's output was made by running it (tools/refcheck.mjs) · questions on Discord or r/quillo