HalyardDocumentation

Configuration

Every worker option, what it does, and when to change it.

Worker options

Option Type Default Notes
connectionString string Required unless pool is given
pool Pool Reuse an existing pg pool
jobs Job[] [] Handlers this worker will process
concurrency number 10 Jobs run in parallel per worker
pollInterval string "200ms" How often to check for work
shutdownTimeout string "30s" Grace period for in-flight jobs
heartbeatInterval string "5s" Liveness signal for orphan detection
claimTimeout string "5m" Jobs held longer are reclaimed
logger Logger console Any object with info/warn/error

Choosing concurrency

Concurrency is bounded by your database connection pool, not by CPU. Each running job holds a connection for the duration of its transaction.

A reasonable starting point:

concurrency = (pool size - 2) / number of worker processes

The - 2 leaves headroom for polling and heartbeats. Setting concurrency above what the pool can serve produces connection timeouts that look like job failures.

claimTimeout and orphan recovery

When a worker dies mid-job, its rows stay marked running with no live owner. Halyard reclaims them once claimTimeout has elapsed since the last heartbeat.

Set this longer than your slowest job. If a job routinely takes 10 minutes and claimTimeout is 5 minutes, a second worker will pick it up while the first is still working — producing exactly the duplicate execution your idempotency guards then have to absorb.

Graceful shutdown

process.on("SIGTERM", async () => {
  await worker.stop();   // stops claiming, waits for in-flight jobs
  process.exit(0);
});

stop() resolves once in-flight jobs finish or shutdownTimeout expires, whichever comes first. Jobs still running at the timeout are left claimed and recovered by another worker after claimTimeout.

Set your container’s termination grace period higher than shutdownTimeout, or the orchestrator will SIGKILL the process mid-job.