Enqueueing jobs
Transactional enqueue, scheduling, priorities, and deduplication.
Transactional enqueue
The main reason to use Halyard is enqueueing inside the transaction that produces the work:
await db.transaction(async (tx) => {
const user = await tx.insert(users).values({ email });
await halyard.enqueue(tx, "send-welcome", { userId: user.id });
});
Pass the transaction handle as the first argument. If you pass the pool instead, the job commits immediately and independently — which is sometimes what you want, but it is a decision rather than a default.
Scheduling
Run a job at a specific time:
await halyard.enqueue(tx, "send-reminder", { bookingId }, {
runAt: addHours(new Date(), 24),
});
Or after a delay:
await halyard.enqueue(tx, "retry-sync", { id }, { delay: "5 minutes" });
Scheduled jobs are not guaranteed to run at exactly runAt — they run at the
first poll after that time. With default settings that is within 200ms.
Priority
Lower numbers run first. The default is 100.
await halyard.enqueue(tx, "process-payment", { id }, { priority: 10 });
Priority applies only among jobs that are ready to run. A high-priority job scheduled for tomorrow does not preempt a normal job scheduled for now.
Deduplication
Give a job a key to make enqueueing idempotent:
await halyard.enqueue(tx, "rebuild-index", { tenantId }, {
key: `rebuild-index:${tenantId}`,
});
While a job with that key is queued or running, further enqueues with the same key are discarded. This is the right tool for “rebuild this when something changes, but not fifty times”.
Note that the key is released when the job completes, not when it starts. A job that takes an hour will absorb an hour of duplicate requests.