@intelligo-dev/jobs
A Postgres-backed job queue: enqueue, claim with SKIP LOCKED, retry with backoff and prune. No Redis.
Install
pnpm add @intelligo-dev/jobs drizzle-ormdrizzle-orm is a peer. The queue is one table, jobs, in the framework’s
schema: intelligo migrate from
@intelligo-dev/cli
creates it, and the connection is the DATABASE_URL that
@intelligo-dev/core reads.
Use
Enqueue from anywhere that can reach the database:
import { enqueue } from "@intelligo-dev/jobs";
await enqueue({
kind: "digest.send",
workspaceId, // or null: a job no workspace owns (an anonymous request's work)
payload: { userId },
runAt: new Date(Date.now() + 60_000), // optional: not before
maxAttempts: 5, // optional: 3 by default
});Drain from a cron route, a long-running worker or a script — handlers are
keyed by kind:
import { drain } from "@intelligo-dev/jobs";
const result = await drain(
{
"digest.send": async (job) => {
await sendDigest(job.payload);
},
},
{ limit: 25 }
);
// { claimed, succeeded, failed, unhandled }What the queue guarantees
- No double-processing. Claiming is
FOR UPDATE SKIP LOCKED, so several workers drain the same queue and each takes different rows. - Retry with backoff. A handler that throws puts the job back, one more
minute later per attempt; after
maxAttemptsit isfailedand keeps its last error.listFailedJobs()reads them, newest first. - A dead worker does not strand a job. One still
runningthirty minutes after its handler started is claimed again, so a handler must finish well inside that. - An unknown kind costs nothing. A job with no handler in this worker goes back without spending an attempt, a minute behind the jobs the worker can run — another deployment may know it.
pruneJobs(before) deletes succeeded jobs older than the cutoff and returns
how many. postgresJobQueue is the same enqueue and drain as one
JobQueue value; a different queue implements that type, and call sites stay
as they are.
Entry points
@intelligo-dev/jobs@intelligo-dev/jobs/db