Queue
SQLite job queue and cron on app.jobs
Queues, retries and exponential backoff in the same database as everything else — so local development, CI and a single-box deployment all run the real thing.
Background work in the database you already have. No second service to run.
$ npm i @frontierjs/caravan
SQLite job queue and cron on app.jobs
Queues, retries and exponential backoff in the same database as everything else — so local development, CI and a single-box deployment all run the real thing.
The file names the job, and the file is the whole of it
send-email.job.ts is send-email. A defineJob naming anything else is refused at load rather than registered, because a handler answering to send-emial while every dispatch says send-email never runs and never says so.
export default defineJob({
handler: async ({ data }) => { await mail.send(data) },
queue: 'mail',
maxAttempts: 5,
})
cron is an option on the handler, not a second system
A job file declares its own schedule, so moving the file moves the schedule. A name is one schedule — registering it twice replaces, where two entries used to fire the job twice a minute.
dispatch({ id }) is a no-op for all time
A stated id is the jobs table’s primary key, so the same dispatch can never run twice — where a unique key frees itself the moment the job goes terminal.
A job runs as whoever asked for it
The principal travels with the dispatch, so a queued write is gated exactly as the request that queued it would have been. Background work is not a hole in the access model.
Heartbeats, crash resume, two instances on one queue
A running row carries the id of the instance holding it and that instance heartbeats, so a worker killed mid-job is replaced rather than leaving the job claimed forever.
caravan queue … from a shell, JSON out
The verbs an operator needs are a binary, not an admin page — which is what a paused deploy runs inside the serving container.
The rest of the stack