SQLStreams

the messaging platform that is just Postgres

You last visited on 9999-99-99 Show what's new since then

SQLStreams vs. Job Queues

Edit this page
Posted: 2026-09-09 · Report this thread
brandon Site Admin brandon profile Posts: 677

We owe this category a debt. River, pgmq, graphile-worker, Oban, pg-boss, and Solid Queue spent years proving the thesis SQLStreams is built on: Postgres is a great place for messages. SKIP LOCKED claiming, producing inside the caller’s transaction, careful indexes on pending work — these are the community’s hard-won patterns, and SQLStreams uses all of them.

The difference is scope. A job queue answers one question: “run this work, reliably, soon.” SQLStreams answers that one and the questions that historically forced you to add Kafka and RabbitMQ next year: what happened? (retention), who else needs to know? (fan-out, routing), can we reprocess? (history a new consumer can read).

Feature by feature

Scored on shipped behavior; proposed capabilities are labeled, never checkmarked.

pgmqRiverSQLStreams
At-least-once delivery, SKIP LOCKEDyesyesyes
Transactional produceyesyesyes
Retries with backoffbasic visibility timeoutyesyes
Dead-letteringarchive tableyesyes, per consumer group
Retained history new consumers can readnonoyes — append-only log
Multiple independent consumer groups per messagenonoyes
Routing bindingsnonoyes — routing-key patterns
Per-key orderingnopartial (unique/sequence opts)per-key exclusivity or strict order (opt-in per message)
ModelSQS-style queue, SQL APIGo job runnerlog + queue platform

The structural difference is one design decision: job queues delete (or archive) on completion, so a message belongs to whoever claimed it first. That forecloses fan-out and replay permanently. SQLStreams retains the log and keeps per-group state beside it — a cursor for the happy path, delivery rows only for failures. The architecture page shows exactly how.

”Isn’t this just a job queue with extra steps?”

For your first use case — background work — SQLStreams behaves like a job queue, and you can use it as one: one stream, one consumer group, retries and dead-lettering included.

The bet is about your second and third use cases. The team that adopts a job queue almost always ends up, eighteen months later, also running:

  • an events pipeline (Kafka/Kinesis) because product analytics and a new service both needed the order history the job queue had been deleting,
  • a pub/sub layer (SNS/RabbitMQ) because three services needed the same notification and the job queue could only give it to one,
  • an outbox + relay because the broker introduced the dual-write problem the Postgres queue never had.

Each addition is individually reasonable; the sum is the three-broker sprawl that Why SQLStreams opens with. SQLStreams’s pitch to job-queue users is simple: same start, different ceiling.

What about Hatchet, Inngest, Temporal?

Those are workflow engines — they orchestrate multi-step, long-running functions (often with a Postgres queue underneath, as in Hatchet’s case). Different layer of the stack: a workflow engine needs a durable message platform underneath it; a message platform doesn’t need a workflow engine. SQLStreams is the platform layer.