This page compares designs, not speeds. There are no throughput or latency comparisons here, and that is a deliberate omission: every such number in Queen’s older documentation was measured against a C++ broker that no longer exists, on a storage engine that no longer exists. Republishing them would be inventing data. Queen’s own measurements, with the configuration that produced each one, are in Benchmarks.
What is worth comparing is structure, because the systems below make genuinely different bets. One question separates them more sharply than any other: how do you get strict ordering per entity, when there are many entities, the set changes constantly, and one slow entity must not delay the others?
Queen’s answer, for reference: a partition is a row in a PostgreSQL table, created by the first push that names it, and ordering inside it comes from taking that row’s lock before allocating an offset. Consumption state is one cursor per (partition, consumer group). A new entity is a new row. Nothing is preallocated, nothing is assigned, and nothing rebalances.
Kafka
The shape. A topic is split into a fixed number of physical partitions. Each partition is an append-only log with its own files and its own replica set on the brokers. A producer chooses a partition, and by default it hashes the record key modulo the partition count. Within a consumer group, at most one consumer reads a given partition; membership and assignment are negotiated through a group coordinator, and adding or removing a consumer triggers a rebalance. Offsets are committed back into the cluster. You operate the cluster: brokers, a metadata quorum, and a JVM.
Where it diverges from Queen.
- Entities are hash-modded onto shards. Because the partition count is fixed and much smaller than the number of entities, many entities share a partition. A consumer stuck on one entity stalls every entity it happens to collide with, which is exactly the head-of-line problem Queen exists to avoid. Queen’s partitions are logical, so an entity gets its own lane and a stall is contained to it.
- The partition count is a capacity decision made in advance. You can add partitions, but that changes the key-to-partition mapping, so ordering across the change is not preserved for existing keys. In Queen the number of lanes is whatever your data produced; there is nothing to resize.
- Rebalancing is a protocol you wait out. Queen has no assignment at all: a pop is a claim on a span, recorded as a lease with an expiry. A worker that dies does not trigger a group-wide reassignment; its lease lapses.
- Kafka replicates itself; Queen does not. Kafka’s durability and failover are its own. Queen’s are PostgreSQL’s, which is an advantage if you already run PostgreSQL well and a limitation if you wanted the message broker to be independently replicated. One PostgreSQL is one failure domain.
- Ecosystem. Kafka has connectors, stream-processing frameworks and a very large operational literature. Queen has five language SDKs, an operator CLI and an HTTP API.
RabbitMQ
The shape. Producers publish to exchanges; bindings route messages into queues; consumers take messages from queues with per-message acknowledgement, negative acknowledgement and dead-letter exchanges. Order is a property of a queue, and it holds when that queue is drained by a single consumer. Competing consumers on one queue interleave, and requeues reorder. Queues are live server-side objects with their own state on the broker.
Where it diverges from Queen.
- Per-entity ordering means a queue per entity. That is the only construction that gives you ordering per key, and it makes the number of broker-side objects scale with entity count. Sharding plugins that hash keys onto a fixed set of queues reintroduce Kafka’s collision problem. Queen’s per-entity lane is a row.
- No replay. A message that has been consumed and acknowledged is gone. Queen’s consumption state is a cursor, so a second consumer group can read the same history from the start, and a group can be moved back to a timestamp.
- Routing is RabbitMQ’s real strength, and Queen has none of it. There are no
exchanges, no bindings, no topic patterns and no header matching. The producer
names the queue and the partition. The nearest thing is consumer-side
selection: queue names carry a dotted
namespace.taskconvention, andGET /api/v1/pop?namespace=…&task=…pops across every queue that matches. If you need content-based routing, Queen will not give it to you. - Where Queen borrowed from RabbitMQ. Leases with expiry, explicit
completed/failed/retry/dlqoutcomes, a retry budget and a real dead-letter table are all recognisably from this side of the family, with one difference that matters more than it sounds: Queen’s ack is an offset commit, not a per-message receipt, so acking a message implicitly completes every earlier unacked message in that partition for that group.
Amazon SQS
The shape. A fully managed queue. Standard queues are at-least-once with no ordering guarantee. FIFO queues add ordering within a message group id: messages sharing a group id are delivered in order and a group is consumed serially, which is structurally the same idea as a Queen partition. A consumer receives messages under a visibility timeout, deletes them when done, and failures go to a dead-letter queue via a redrive policy. FIFO queues also deduplicate on a deduplication id or a content hash, inside a window the service fixes. FIFO comes with per-queue throughput quotas that standard queues do not have.
Where it diverges from Queen.
- There is no server, and that is the whole point. Nothing to run, nothing to size, nothing to back up. Queen cannot match that: it is a binary plus a PostgreSQL you operate. If the appeal is “no infrastructure”, stop here.
- No consumer groups. Fan-out is a topology you assemble (a topic in front of
several queues), and each subscriber gets its own copy stored separately. In
Queen fan-out is a second
consumerGroupstring on the pop, reading the same stored messages through its own cursor. - No replay. A deleted message is gone; there is no cursor to rewind and no
history to re-read. Queen’s replay is a
seekon a group. - The deduplication window is the service’s, not yours. Queen’s
dedupWindowSecondsis a per-queue setting, keyed on thetransactionIdyou supply, enforced inside the database before an offset is allocated. - You cannot query the backlog. Queen’s queues, partitions, cursors, dead
letters and lag are tables: you can
SELECTthem with the client you already have, and take apg_dumpof the whole thing. - Ordering domain limits are different in kind. SQS constrains FIFO throughput per queue and per group by quota; Queen constrains it by physics: one partition is one writer and one in-flight leased batch per group.
pgmq
pgmq is the closest comparison, because it makes the same core bet: PostgreSQL is the right place to keep messages.
The shape. A PostgreSQL extension. Creating a queue creates a table (plus an archive table). Sending inserts a row. Reading updates the row to set a visibility timeout and returns it; deleting removes it; archiving moves it. There is no broker process: clients connect to PostgreSQL and call SQL functions.
Where it diverges from Queen.
- Fan-out. pgmq has no consumer-group concept, so delivering the same stream to several independent consumers means a queue per consumer, and one stored copy of every message per consumer. Queen stores each message once and gives each consumer group its own cursor over it: the group is a string on the pop, and adding one writes no additional message data.
- Ordering per entity. Rows carry monotone ids, but concurrent readers taking rows under visibility timeouts do not preserve per-key order, so per-entity FIFO again means a queue per entity. Queen’s ordering domain is the partition, and partitions are rows inside one queue.
- Per-message row state versus a cursor. pgmq tracks delivery per message: an
UPDATEto lease it, aDELETEor a move to archive to finish it. Queen keeps no per-message state at all: acknowledgement advances onecommittedoffset per (partition, group). That is why Queen has no per-message delete to do, and also why it cannot delete one message on request. - Extension versus schema. pgmq is installed as an extension, which some
managed PostgreSQL offerings do not allow. Queen needs
CREATE SCHEMAand no extensions; it applies its own DDL and stored procedures at boot. - A broker tier, for better and worse. Queen’s separate process is what buys HTTP clients in any language, long-polling, and the coalescing of many concurrent pushes into shared commits. It is also a process to run, a network hop that pgmq does not have, and one more thing that can be misconfigured. pgmq’s answer (no broker) is genuinely simpler when your consumers are all happy speaking SQL.
Choosing something else
Each of these systems is the right answer to a question Queen answers badly.
| Choose | When |
|---|---|
| Kafka | Your ordering domains are few and stable rather than one-per-entity; you want the broker to replicate itself independently of a database; you need the connector and stream-processing ecosystem; you already operate a JVM cluster well |
| RabbitMQ | You need real routing (exchanges, topic patterns, header matching) or protocol breadth beyond HTTP; ordering per queue is enough and you do not need replay |
| Amazon SQS | You want no server and no database to operate at all; your fan-out is built from a topic plus several queues; your ordering domains fit FIFO’s quotas; you never need to re-read history |
| pgmq | You want PostgreSQL-backed queuing with no broker process; your consumers can all speak SQL; a work queue with visibility timeouts is enough, without consumer groups or replay; you can install extensions |
| Queen | You need strict FIFO per entity at high and unpredictable cardinality, where one slow entity must not block others; consumer groups with replay over the same stored messages; ack-and-push in one PostgreSQL transaction; one binary and no cluster, next to a PostgreSQL you already run |
Before committing either way, read Limits and non-goals. It is the honest inverse of this page, and it will disqualify Queen faster than any comparison table.