ADR-004 — pg-boss job queue on Postgres¶
Status¶
Accepted (2026-07-03, inherited from heig-classroom). Amended 2026-10-03: the
durable Postgres queue remains; the inherited singleton keys, dead-letter replay
screen and queue metrics, never built in Quiz, are withdrawn. Quiz's queue policies are in apps/api/src/jobs.ts and each
module's job registration; the inherited record is not their configuration.
Context¶
Grading, external notifications and GitHub work must outlive an HTTP request and, in production, survive a process restart without a second stateful service.
Decision¶
- Use pg-boss on PostgreSQL.
jobs.tsexposes the sharedJobQueueinterface; the manifest/lockfile own its version. PGlite development uses an in-process, non-durable adapter; production refuses PGlite. - Register concurrency and retry policy per queue. There is no universal ten-worker/five-retry rule: grading, LLM, notifications, scheduled tasks and project leases have different failure semantics.
- Do not assume
singletonKeydeduplicates a standard queue. Quiz does not expose it inSendOptions; idempotent handlers, database claims and leases own duplicate protection. See ADR-006/011/064 and the queue's owner. - Use ADR-055 and the deployment runbook for monitoring. The inherited generic dead-letter replay screen is not an implemented operator contract.
Consequences¶
One database backup covers durable jobs and business data. Failure recovery
is part of each handler: for example grading.llm has no queue retry
(ADR-063), whereas notification delivery retries with backoff (ADR-030).
The development adapter is not evidence of production durability or concurrency.
Rejected alternatives¶
Redis/BullMQ or a dedicated broker add another service to operate. System cron and hand-built queue tables would recreate persistence, retries and worker coordination. The inherited record retains the original trade-offs.