HalyardDocumentation

Introduction

What Halyard is, what it is not, and whether it fits your problem.

Halyard is a job queue that stores its state in the Postgres database you already run. It has no broker, no separate service to deploy, and no dependency beyond your database driver.

Why another queue

Most applications reach for a queue long before they need a distributed one. The usual options — Redis-backed, or a hosted broker — introduce a second durability model alongside your database. That means a job can commit while its transaction rolls back, or the reverse, and reconciling the two becomes a recurring source of bugs.

Halyard sidesteps this by enqueueing jobs in the same transaction as your business writes:

await db.transaction(async (tx) => {
  const order = await tx.insert(orders).values({ userId, total });
  await halyard.enqueue(tx, "send-receipt", { orderId: order.id });
});

If the transaction rolls back, the job was never enqueued. There is no window in which the two can disagree.

When not to use it

Be honest about the ceiling. Halyard is a good fit up to roughly 5,000 jobs per second on modest hardware. Past that, the polling model and row-level locking start to cost more than a purpose-built broker.

You should reach for something else if you need:

  • Fan-out to many consumers. Halyard is a work queue, not a pub/sub bus.
  • Multi-tenant isolation at the broker level. One Postgres role sees all jobs.
  • Sub-millisecond dispatch. Polling latency is 50–200ms by default.
  • Cross-datacenter replication of the queue itself.

If your queue is doing tens of thousands of jobs per second, you have earned a real broker. Until then, one fewer moving part is worth a great deal.

What you get

  • Jobs enqueued transactionally with your data
  • At-least-once delivery with configurable retries and backoff
  • Scheduled and recurring jobs
  • Dead-letter handling with replay
  • A worker pool that shuts down without dropping in-flight work