---
title: "Queen vs Kafka, Pulsar and Redpanda on NVMe"
description: "Queen 2.4, Kafka 4.3.1, Pulsar 4.2.4 and Redpanda 26.2.3 on the same three GCP nodes with local NVMe disks, 2026-10-14 to 16: 3,000,000 msg/s of 256-byte messages from 10 to 10,000,000 partitions, 1,000,000 msg/s of 1 KB messages, the ceiling on 1,000 partitions, what an fsync before the answer costs, Kafka in queue mode, the machines, every setting and how we measured."
---

> Queen MQ documentation, for AI agents
> Complete self-contained summary of Queen MQ: https://queenmq.com/llms-brief.txt
> Fetch that first when the question is about the product rather than about this page.
> Index of all pages: https://queenmq.com/llms.txt

# Queen vs Kafka, Pulsar and Redpanda on NVMe

We ran Queen 2.4, Kafka 4.3.1, Pulsar 4.2.4 and Redpanda 26.2.3 on the same three GCP brokers with
local NVMe disks, with the same load generators, the same load and the same report. At 3,000,000
msg/s of 256-byte messages Kafka has the lowest latency up to 1,000 partitions: a median near 6 ms
and a p99 of 7.5 to 11 ms. Queen 2.4 reads a p99 of 15 to 25 ms at every partition count from 10 to
10,000,000, on 21 to 28 cores. Pulsar reads 20 to 25 ms up to 50,000 partitions on the fewest
cores, and Redpanda 14 ms at 100 and 1,000 partitions and 782 ms at 10,000. Kafka carried 25,000
partitions at a p99 of 105 ms; Kafka and Redpanda did not carry 50,000, and Pulsar did not carry
100,000, so from 100,000 partitions up only Queen carried the rate.

Queen, Pulsar and Redpanda answer a write once two of the three nodes have fsynced it. Kafka, as we
ran it, answers when its replicas hold the write in memory; with an fsync on every append its p99
is 198 ms at 1,000 partitions and 281 ms at 10,000
([the runs](#an-fsync-before-the-answer)). With 1 KB messages at 1,000,000 msg/s Queen's median is
the lower one from 100 partitions up, and Kafka keeps the lower p99 up to 1,000. On one topic of
1,000 partitions Kafka carried about 8,000,000 msg/s and one Queen queue 5,000,000; at 5,000,000
Kafka read a p99 of 8.9 ms, Redpanda 82, Queen 91, and Pulsar, its disks full, did not carry the
rate ([all four at 5,000,000](#all-four-at-5000000-msgs)).

Queen 2.4 writes 147 bytes a message where 2.3.0 wrote 248, about what Kafka writes for this load;
the queue log's fsync went from about 3 ms to 0.5, and the p99 of the same matrix fell from 42 to
76 ms on 2.3.0 to 15 to 25. The 2.3.0 tables of this page are in its git history.

## The machines

GCP VMs in one zone (`europe-west1-b`) and one VPC, Ubuntu 24.04. Every system ran on three
brokers of the same machine type, one system at a time, with nothing else on them.

| Role | Machine type | CPU | Memory | Disk for the data |
|---|---|---|---|---|
| 3 brokers | `c4-standard-24-lssd` | 24 vCPU, Intel Xeon 6985P-C | 88 GB | the four local NVMe disks of the machine type (4 × 375 GB) in one RAID0, XFS `noatime` |
| 3 load generators (6 for Pulsar at 50,000 and 100,000 partitions and at 5,000,000 msg/s) | `c4-highcpu-16` | 16 vCPU, Intel Xeon Platinum 8581C | 32 GB | boot disk only |

The runs come from three sets of these VMs, made on 2026-10-14 (Kafka), 10-15 (Queen 2.4) and 10-16
(Pulsar, Redpanda, and Queen at 1,000 and 50,000 partitions). We measured every broker's disks
before each set was used, because one slow disk on a follower becomes the p99 of every client of
that follower: on every broker of every set a 32 KB write followed by `fdatasync` took 0.05 to
0.06 ms at the median, 16,000 to 17,700 a second; on the first set sequential 1 MB writes ran at
1,340 MB/s, GCP's limit of about 335 to 350 MB/s for each local disk, four times. On 10-16 we ran Queen
and Kafka again at 3,000,000 msg/s on 1,000 partitions: Queen read 6.9 / 16.5 ms (four of its five
runs on 10-15 read a p99 of 15 to 20 ms) and Kafka 6.0 / 7.9 ms (8.1 on 10-14), so the three sets
compare.

## How we ran it

- **The load.** Nine load processes, three on each load generator, open loop: each process keeps
  its schedule when the broker slows down, so a stall shows up as latency instead of lowering the
  load. Producers send 100 messages a request to one partition, in turn over all the partitions.
  Kafka and Redpanda share one load generator (`kload`, on franz-go); Pulsar's (`pload`) uses
  pulsar-client-go and Queen's (`qload`) Queen's HTTP API, on the same core of pacing, payloads and
  histograms. Each Queen process talks to one of the three brokers; the other clients talk to each
  partition's leader.
- **Pulsar at 50,000 and 100,000 partitions and at 5,000,000 msg/s** ran on six load generators,
  18 processes. On three,
  the Go client used all 16 cores of each load generator at 50,000 partitions, about 2.2 to 2.7
  cores for every 100,000 msg/s against 0.7 at 10,000, and the run measured the clients: 1,680,000
  msg/s pushed with the brokers at 13 of 72 cores. On six it carried the rate.
- **The messages.** JSON events of 256 bytes, or about 1,027 bytes in the 1 KB runs, the same
  generator for every system.
- **Consumers.** One consumer group or subscription. 9 consumers in all for 10 partitions, 99 for
  100 and 297 for every larger count (306 for Pulsar on six load generators). Queen's consumers pop
  up to 10 partitions and 1,000 messages at a time from 1,000 partitions up (one partition and up
  to 20,000 or 5,000 messages with 10 and 100) and acknowledge without waiting; Kafka's and
  Redpanda's poll up to 1,000 records; Pulsar's failover subscription gives each partition to one
  consumer, which acknowledges every message.
- **A point.** A fresh cluster, the queue or topic created with all its partitions and warmed
  before the timed run, then 70 seconds of load, the first 10 a ramp (90 seconds for Queen's 10-16
  runs). No system was measured while it created partitions. Pulsar's warm-up sends one message to
  every partition from one process; at 100,000 partitions it ran for more than 25 minutes, so that
  point ran without it, its producers created before the start.
- **Latency.** End to end, from the moment a message was scheduled to be sent to the moment a
  consumer received it. Each load process keeps a histogram for every 10-second window; for each
  window we take the slowest process. The tables give the median of the three windows in seconds
  30 to 60 and, in brackets, the worst of them (the statistic of our other benchmark pages). The
  figures use the median: one spike in one window is shown in the brackets and does not decide the
  line.
- **Carried.** A point is carried when at least 99.5% of the offered rate is pushed, nothing is
  shed or errs, and at least 99% of what was pushed is consumed.
- **Resources.** CPU is the three brokers' cores added up over the loaded window. Redpanda's is its
  shards' busy time: its reactors poll, so the operating system counts 66 to 71 of the 72 cores at
  every point. Memory is the resident memory of the busiest broker, disk the mean write rate of a
  broker's data volume. Each broker and load generator was sampled every one or two seconds.
- **One run a point.** We ran most points once. Repeats on the same machines moved Queen's p99 by
  a few milliseconds, and one of six runs of its 1,000-partition point read 65 ms (below).

## The settings

| | Queen 2.4 | Kafka 4.3.1 | Pulsar 4.2.4 | Redpanda 26.2.3 |
|---|---|---|---|---|
| Build | the image `ghcr.io/queen-mq/queen:2.4.0-beta.1`, its defaults (the 10-15 runs: the same code built before the version commit) | the Apache release, JDK 21, KRaft with the three brokers as controllers | the Apache release; ZooKeeper, a bookie and a broker on each of the three nodes | Redpanda's packages, production mode, `rpk redpanda tune all` and `rpk iotune` |
| Durability | three replicas in one raft group; a write is answered after the leader and one follower have fsynced it | three replicas, `acks=all`, `min.insync.replicas=2`; a write is answered when the in-sync replicas hold it in memory, with no fsync | ensemble 3, write quorum 3, ack quorum 2, `journalSyncData=true`: answered after two bookies have fsynced it in their journal | three replicas in a raft group per partition, `write_caching` off (its default): answered after two of three have fsynced it |
| Producer | binary push body compressed with zstd | idempotent, lz4, `linger.ms=5`, 4 producers a process, batches up to 1 MiB (franz-go) | a producer per partition, lz4, batches of up to 1,000 messages or 128 KB sent every 5 ms (pulsar-client-go 0.21) | as Kafka's |
| Consumer | framed pops, acknowledgements answered as counts | the KIP-848 consumer protocol, `fetch.max.wait.ms=500`, `fetch.min.bytes=1` | failover subscription, an ack per message | the classic group protocol with cooperative-sticky assignment (26.2.3 does not answer KIP-848) |
| Broker | deduplication off, completed messages kept 1,800 s (nothing deleted during a point); 10,000,000 partitions with `QUEEN_CONSUME_IDLE_UNLOAD_S=30` | 6 GB heap with G1 and a 20 ms pause goal; 24 request handler threads (`num.io.threads`), 12 network threads, 12 replica fetchers; retention 10 minutes | bookie: 6 GB heap and 16 GB direct memory, a 4 GB write cache and 2 GB read-ahead, two journal directories; broker: 12 GB heap and 12 GB direct memory, a 4 GB managed-ledger cache, 48 bundles; ZooKeeper 2 GB | its defaults, with the segment preallocation and the memory per partition set for the partition count |
| Page cache | background writeback from 5% of memory | background writeback from 1 GiB (`vm.dirty_background_bytes`) | from 1 GiB | from 1 GiB (Redpanda writes with direct I/O) |

All four ran with the same kernel settings otherwise (`vm.swappiness=1`, `vm.dirty_ratio=60`,
`vm.min_free_kbytes=524288`, 1,048,576 open files, larger socket buffers); Redpanda's tuners changed
IRQ affinity, the disk scheduler and more for its runs, and were undone after them. Queen does not
depend on the writeback setting, because it fsyncs what it writes: on 2.3.0 it read the same on
ext4, ext4 with `lazytime` and XFS. Kafka's thread pools are larger than in our September
comparison and on 2026-10-13 (16 request handlers, 8 network threads and 8 replica fetchers, sized
for 16-vCPU brokers); its other settings are the same as there. Pulsar's memory is sized for the
88 GB brokers, about twice the September settings for 31 GB machines.

## 3,000,000 msg/s of 256-byte messages

End-to-end latency, p50 / p99 in milliseconds (worst window's p99 in brackets), and the cores of
the three brokers together.

| Partitions | Queen 2.4 | Kafka | Pulsar | Redpanda |
|---|---|---|---|---|
| 10 | 10 / 24 (27), 21.0 cores | 6.4 / 11 (45), 14.9 | 13 / 25 (26), 5.4 | 14 / 44 (45), 8.8 |
| 100 | 7.5 / 17 (17), 27.7 | 6.0 / 7.5 (8), 18.5 | 13 / 25 (26), 11.4 | 6.9 / 14 (14), 19.9 |
| 1,000 | 6.9 / 16.5 (19), 24.6 | 6.0 / 8.1 (8), 21.0 | 6.2 / 20 (21), 15.1 | 6.8 / 14 (20), 22.4 |
| 10,000 | 7.1 / 20 (26), 24.8 | 8.4 / 21 (78), 52.8 | 6.7 / 20 (21), 17.2 | 68 / 782 (815), 30.7 |
| 25,000 | not run | 58 / 105 (146), 57.0 | not run | not run |
| 50,000 | 7.7 / 20 (23), 24.8 | not carried: 601,000 msg/s pushed, p99 32 s; 50.2 | 6.7 / 21 (21), 21.7, six load generators | not carried: 2,820,000 msg/s pushed, p99 8.8 s; 33.8 |
| 100,000 | 7.1 / 15 (17), 25.0 | not carried: 174,000 msg/s pushed, p99 38 s; 60.5 | not carried: 2,520,000 msg/s pushed, p99 28 s; 22.8, six load generators | not run |
| 1,000,000 | 7.5 / 20 (21), 24.9 | not run | not run | not run |
| 10,000,000 | 8.9 / 25 (29), 25.4 | not run | not run | not run |

**Figure.** p99 end-to-end latency at 3,000,000 msg/s against the partition count (log scale), the median of three 10-second windows, on the same three GCP nodes. Queen 2.4 reads 24 ms at 10 partitions, 17 at 100, 16.5 at 1,000, 20 at 10,000, 20 at 50,000, 15 at 100,000, 20 at 1,000,000 and 25 at 10,000,000. Kafka reads 11 ms at 10, 7.5 at 100, 8.1 at 1,000, 21 at 10,000 and 105 at 25,000, and does not carry 50,000. Pulsar reads 25 ms at 10 and 100, 20 at 1,000, 20 at 10,000 and 21 at 50,000, and does not carry 100,000. Redpanda reads 44 ms at 10, 14 at 100 and 1,000 and 782 at 10,000, and does not carry 50,000.

At 3M msg/s Kafka has the lowest p99 up to 1,000 partitions (7.5 to 11 ms). Queen 2.4 reads 15 to 25 ms from 10 to 10M partitions; Pulsar 20 to 25 ms up to 50,000; Redpanda 14 ms at 100 and 1,000. Off the axis: Kafka 105 ms at 25,000 and Redpanda 782 ms at 10,000. Only Queen carried 100,000 and more.

Values: p99 end-to-end latency.

| partitions in the queue or topic | Queen 2.4 | Kafka 4.3.1 | Pulsar 4.2.4 | Redpanda 26.2.3 |
|---|---|---|---|---|
| 10 | 24 ms | 11 ms | 25 ms | 44 ms |
| 100 | 17 ms | 7.5 ms | 25 ms | 14 ms |
| 1k | 17 ms | 8.1 ms | 20 ms | 14 ms |
| 10k | 20 ms | 21 ms | 20 ms |  |
| 50k | 20 ms |  | 21 ms |  |
| 100k | 15 ms |  |  |  |
| 1M | 20 ms |  |  |  |
| 10M | 25 ms |  |  |  |

Source: `benchmark-queen/2026-10-15-bytes-per-msg/runs/gcp, the fm3-* folders, and benchmark-queen/2026-10-12-three-node-new-forms/runs, a24-* (Queen); benchmark-queen/2026-09-30-kafka-pulsar/runs: kafka/*-3000k-gcp24-xfs-t24, pulsar/1x*-3000k-gcp24*, redpanda/1x*-3000k-gcp24`.

**Figure.** Cores used by the three brokers together at 3,000,000 msg/s against the partition count (log scale). Queen 2.4 uses 21 cores at 10 partitions and 24.6 to 27.7 from 100 to 10,000,000. Kafka uses 14.9 at 10, 18.5 at 100, 21.0 at 1,000, 52.8 at 10,000 and 57.0 at 25,000, then 50.2 at 50,000 and 60.5 at 100,000, where it did not carry the rate. Pulsar uses 5.4 at 10, 11.4 at 100, 15.1 at 1,000, 17.2 at 10,000 and 21.7 at 50,000, and 22.8 at 100,000, where it did not carry the rate. Redpanda's shards are busy 8.8 cores at 10, 19.9 at 100, 22.4 at 1,000 and 30.7 at 10,000, and 33.8 at 50,000, where it did not carry the rate.

Pulsar uses the least CPU at every count it carried. Queen's does not move with the partition count, 21 to 28 cores. Kafka needs about twice Queen's from 10,000 partitions; Redpanda's grows from 9 to 31 busy cores up to 10,000. Hollow: not carried.

Values: cores, three brokers together (of 72).

| partitions in the queue or topic | Queen 2.4 | Kafka 4.3.1 | Pulsar 4.2.4 | Redpanda 26.2.3 (busy shards) |
|---|---|---|---|---|
| 10 | 21 | 15 | 5.4 | 8.8 |
| 100 | 28 | 19 | 11 | 20 |
| 1k | 25 | 21 | 15 | 22 |
| 10k | 25 | 53 | 17 | 31 |
| 25k |  | 57 |  |  |
| 50k | 25 | 50 (see caption) | 22 | 34 (see caption) |
| 100k | 25 | 61 (see caption) | 23 (see caption) |  |
| 1M | 25 |  |  |  |
| 10M | 25 |  |  |  |

Source: `the grid rows of the Queen runs (cores of the three brokers); report/report.py over the Kafka, Pulsar and Redpanda runs, Redpanda's busy.txt`.

Up to 1,000 partitions Kafka has the lowest latency: a median of 6.0 to 6.4 ms and a p99 of 7.5 to
11 ms, steady within about a millisecond across the windows at 100 and 1,000, on 15 to 21 cores. It
answers a write when the replicas hold it in memory, where the other three wait for two fsyncs.
From 10,000 partitions Kafka pays for every partition: at 10,000 its p99 is level with Queen's and
Pulsar's (21 ms against 20) on 53 cores, more than twice theirs, and at 25,000 it reads 58 / 105 ms
on 57 cores. At 50,000 and 100,000 it pushed 601,000 and 174,000 of the 3,000,000 messages a second
offered.

Queen's latency, CPU and disk do not follow the partition count: a median of 6.9 to 10 ms and a p99
of 15 to 25 ms from 10 to 10,000,000 partitions, on 21 to 28 cores. The leader held 5 to 8 GB of
memory up to a million partitions and 17 GB at ten million, and each broker wrote 138 to 186 bytes
a message, 440 MB/s at 1,000 partitions where a Kafka broker wrote 443. Its 1,000-partition point
ran six times on two days: a p99 of 15, 16.5, 17, 18 and 20 ms, and once 65 ms with the median at
12, in a run where the log's group commit settled on fewer, larger groups from the first seconds;
why, we did not find.

Pulsar uses the fewest cores at every count it carried, 5 to 22, and its p99 stays between 20 and
25 ms up to 50,000 partitions. Its median is 13 ms at 10 and 100 partitions and 6.2 to 6.7 ms from
1,000 up. At 100,000 partitions it pushed 2,520,000 msg/s with its brokers at 23 of 72 cores and
the six load generators at about 13 of their 16; these runs do not tell whether the brokers or the
client stopped it. Each broker wrote 860 to 900 MB/s, every entry going to a bookie's journal and
then to its ledger storage.

Redpanda reads 14 ms at 100 and 1,000 partitions, 44 ms at 10, and 68 / 782 ms at 10,000, where it
still pushed the rate. A partition is a raft group served by one core: at 10 partitions each one
carries 300,000 msg/s, and the brokers' own produce latency (Redpanda's request histogram, in
buckets that double) had its p99 in the bucket up to 33 ms against up to 8 ms at 100 partitions.
At 10,000 partitions the brokers' produce p99 rose to the bucket up to 262 ms on two brokers and up
to 1 s on the third, whose disks showed four times the I/O pressure of
the others at the same write rate; at 10, 100 and 1,000 partitions the three were alike. The cause
was not determined. At 50,000 Redpanda pushed 2,820,000 of the 3,000,000 and fell behind. Its
disks took about 85,000 writes a second of 7.5 KB each, 640 to 680 MB/s a broker, at every
partition count.

## 1,000,000 msg/s of 1 KB messages

The same matrix with 1 KB JSON messages (1,027 bytes on average) at 1,000,000 msg/s, about 1 GB/s
of messages, everything else as above. Pulsar and Redpanda were not run with 1 KB messages.

| Partitions | Queen 2.4 | Queen cores | Kafka | Kafka cores |
|---|---|---|---|---|
| 10 | 9.7 / 20 (24) | 15.2 | 7.9 / 9.4 (9) | 9.2 |
| 100 | 6.7 / 19 (19) | 18.2 | 7.4 / 9.4 (10) | 9.5 |
| 1,000 | 6.8 / 16 (17) | 17.1 | 7.4 / 9.4 (9) | 11.9 |
| 10,000 | 6.9 / 16.5 (17) | 17.4 | 8.8 / 19 (20) | 31.4 |
| 25,000 | not run | | 15 / 45 (49) | 55.9 |
| 50,000 | not run | | not carried: 159,000 msg/s pushed, p99 33 s | 46.5 |
| 100,000 | 6.8 / 20 (21) | 17.5 | not carried: 153,000 msg/s pushed, p99 35 s | 60.6 |
| 1,000,000 | 7.4 / 17 (17) | 17.4 | not run | |
| 10,000,000 | 7.0 / 22 (25) | 17.7 | not run | |

**Figure.** p99 end-to-end latency at 1,000,000 msg/s of 1 KB messages against the partition count (log scale), Queen 2.4 and Kafka 4.3.1 on the same three GCP nodes, the median of three 10-second windows. Queen reads 20 ms at 10 partitions, 19 at 100, 16 at 1,000, 16.5 at 10,000, 20 at 100,000, 17 at 1,000,000 and 22 at 10,000,000. Kafka reads 9.4 ms at 10, 100 and 1,000 partitions, 19 at 10,000 and 45 at 25,000, and does not carry 50,000 or 100,000.

With 1 KB messages at 1M msg/s Kafka's p99 is 9.4 ms up to 1,000 partitions, 19 at 10,000 and 45 at 25,000; it does not carry 50,000 or 100,000 (not drawn). Queen 2.4's is 16 to 22 ms from 10 to 10M partitions, under Kafka's at 10,000.

Values: p99 end-to-end latency.

| partitions in the queue or topic | Queen 2.4 | Kafka 4.3.1 |
|---|---|---|
| 10 | 20 ms | 9.4 ms |
| 100 | 19 ms | 9.4 ms |
| 1k | 16 ms | 9.4 ms |
| 10k | 17 ms | 19 ms |
| 25k |  | 45 ms |
| 100k | 20 ms |  |
| 1M | 17 ms |  |
| 10M | 22 ms |  |

Source: `benchmark-queen/2026-10-15-bytes-per-msg/runs/gcp, the f1k-* folders (Queen); benchmark-queen/2026-09-30-kafka-pulsar/runs/kafka, the *-1000k-p1k-gcp24-xfs-t24 folders (Kafka)`.

**Figure.** Cores used by the three brokers together at 1,000,000 msg/s of 1 KB messages against the partition count (log scale). Queen 2.4 uses 15.2 cores at 10 partitions and 17.1 to 18.2 from 100 to 10,000,000. Kafka uses 9.2 at 10, 9.5 at 100, 11.9 at 1,000, 31.4 at 10,000 and 55.9 at 25,000, then 46.5 at 50,000 and 60.6 at 100,000, where it did not carry the rate.

With 1 KB messages Kafka uses less CPU than Queen up to 1,000 partitions, about twice Queen's at 10,000, 56 cores at 25,000, and fails at 50,000 and 100,000 (hollow). Queen stays at 15 to 18 cores up to ten million.

Values: cores, three brokers together (of 72).

| partitions in the queue or topic | Queen 2.4 | Kafka 4.3.1 |
|---|---|---|
| 10 | 15 | 9.2 |
| 100 | 18 | 9.5 |
| 1k | 17 | 12 |
| 10k | 17 | 31 |
| 25k |  | 56 |
| 50k |  | 47 (see caption) |
| 100k | 18 | 61 (see caption) |
| 1M | 17 |  |
| 10M | 18 |  |

Source: `the grid rows of the f1k-* Queen runs; report/report.py over the *-1000k-p1k-gcp24-xfs-t24 Kafka runs`.

With larger messages Queen's median is the lower one from 100 partitions up, 6.7 to 7.4 ms against
Kafka's 7.4 to 8.8. Kafka keeps the lower p99 up to 1,000 partitions, 9.4 ms against Queen's 16 to
20, with its worst window within a millisecond of the median; at 10,000 Queen's p99 is the lower,
16.5 ms against 19, on 17 cores against 31. Kafka needs less CPU up to 1,000 partitions (9 to 12
cores against 15 to 18), carried 25,000 at a p99 of 45 ms on 56 cores, and did not carry 50,000 or
100,000. Queen's p99 is 16 to 22 ms from 10 to ten million partitions. Each Queen broker wrote 538
to 569 bytes a message, about 550 MB/s, and each Kafka broker about 610 MB/s: both compress these
JSON messages, Queen with zstd and Kafka with lz4. A Queen leader held 2 to 7 GB of memory up to a
million partitions and 14 GB at ten million.

## The ceiling at 1,000 partitions

How far one queue or topic of 1,000 partitions goes on these brokers, a fresh cluster for each
rate. Queen 2.4:

| msg/s | e2e p50 / p99 | cores | disk writes a broker |
|---|---|---|---|
| 3,000,000 | 6.6 / 15 (16) and 6.7 / 17 (18), two runs | 24.5 | 440 MB/s |
| 3,500,000 | 10 / 28 (31) | 25.8 | 493 MB/s |
| 4,000,000 | 16 / 52 (52) | 28.4 | 550 MB/s |
| 4,500,000 | 26 / 62 (68) | 31.4 | 609 MB/s |
| 5,000,000 | 39 / 87 (97) | 35.5 | 669 MB/s |
| 5,500,000 | not carried: 5,060,000 pushed | | |

Kafka 4.3.1, configured as above:

| msg/s | e2e p50 / p99 | cores | disk writes a broker |
|---|---|---|---|
| 3,000,000 | 6.0 / 8.1 (8) | 21.0 | 443 MB/s |
| 4,000,000 | 5.7 / 8.3 (8) | 27.6 | 590 MB/s |
| 5,000,000 | 5.5 / 8.8 (9) | 32.4 | 740 MB/s |
| 6,000,000 | 5.6 / 10.8 (11) | 35.1 | 887 MB/s |
| 7,000,000 | 5.9 / 10.3 (10) | 37.8 | 1,029 MB/s |
| 8,000,000 | 5.8 / 12.5 (293) | 40.6 | 1,178 MB/s |
| 10,000,000 | 9.4 / 375 (379) | 44.8 | 1,393 MB/s |

**Figure.** p99 end-to-end latency against the offered rate on 1,000 partitions, Queen 2.4 and Kafka 4.3.1 on the same three GCP nodes, the median of three 10-second windows. Queen reads 16 ms at 3,000,000 msg/s, 28 at 3,500,000, 52 at 4,000,000, 62 at 4,500,000 and 87 at 5,000,000, and does not carry 5,500,000. Kafka reads 8.1 ms at 3,000,000, 8.3 at 4,000,000, 8.8 at 5,000,000, 10.8 at 6,000,000, 10.3 at 7,000,000 and 12.5 at 8,000,000, and 375 ms at 10,000,000.

On 1,000 partitions Kafka's p99 stays between 8 and 13 ms from 3M to 8M msg/s; at 10M it reads 375 ms (not drawn). Queen 2.4's rises from 16 ms at 3M to 87 ms at 5M, and 5.5M is not carried.

Values: p99 end-to-end latency.

| offered msg/s | Queen 2.4 | Kafka 4.3.1 |
|---|---|---|
| 3M | 16 ms | 8.1 ms |
| 3.5M | 28 ms |  |
| 4M | 52 ms | 8.3 ms |
| 4.5M | 62 ms |  |
| 5M | 87 ms | 8.8 ms |
| 6M |  | 11 ms |
| 7M |  | 10 ms |
| 8M |  | 13 ms |

Source: `benchmark-queen/2026-10-15-bytes-per-msg/runs/gcp, the bpm*-fix and fxc-p1000-r* folders (Queen); benchmark-queen/2026-09-30-kafka-pulsar/runs/kafka, the 1x1000-<rate>k-gcp24-xfs-t24 folders (Kafka)`.

Kafka carries about 8,000,000 msg/s on 1,000 partitions with its p99 near 12 ms, one window of 293
ms aside. At 10,000,000, the top of the rates we offered, it is carried by the rule, but two of the
three windows read a p99 near 375 ms, with the disks writing 1,393 MB/s, their limit.

Queen 2.4 carries 5,000,000 msg/s with a p99 under 100 ms (2.3.0 stopped between 3,500,000 and
4,000,000). At 5,500,000 the brokers took 5,060,000 and the producers waited at their in-flight
limit; the leader used 19 of 24 cores and each broker wrote 670 MB/s, half what its disks take, so
neither was full, and what stopped it is not profiled yet. We did not measure Pulsar's or
Redpanda's ceiling.

## All four at 5,000,000 msg/s

One queue or topic of 1,000 partitions at 5,000,000 msg/s of 256-byte messages, a fresh cluster
for each system, 70 seconds of load, 2026-10-16, Pulsar on six load generators. p50 / p99 in
milliseconds (worst window in brackets), the cores of the three brokers (Redpanda's shards' busy
time) and the mean write rate of a broker's disks:

| System | e2e p50 / p99 | cores | disk writes a broker |
|---|---|---|---|
| Kafka 4.3.1 | 5.7 / 8.9 (14) | 30.8 | 737 MB/s |
| Redpanda 26.2.3 | 30 / 82 (268) | 32.6 | 1,045 MB/s |
| Queen 2.4 | 41 / 91 (111) | 35.1 | 672 MB/s |
| Pulsar 4.2.4 | not carried: 4,420,000 msg/s pushed, 514,000 shed, p50 / p99 1.7 / 5.6 s | 15.7 | 1,308 MB/s |

Kafka barely moves from 3,000,000 msg/s (6.0 / 7.9 ms) and reads what its ceiling ladder read on
10-14 (5.5 / 8.8). Queen reads what its own ladder read on 10-15 (39 / 87 ms), with the leader at
18 of its 24 cores. Redpanda carries the rate with its median up from 6.8 to 30 ms; its brokers'
own produce p99 sat in the buckets up to 33 and 66 ms. Pulsar stopped at its disks: a bookie writes
every entry to its journal and again to its ledger storage, both on the one RAID of these
machines, and each broker wrote 1,308 MB/s of the about 1,340 the RAID takes, with the brokers at 16
of 72 cores and the load generators at about 5 of their 16. BookKeeper's documentation advises a
disk of its own for the journal; these machines have one. Each point ran once, and only on 1,000
partitions.

## An fsync before the answer

Queen, Pulsar and Redpanda answer a write after two nodes have fsynced it; Kafka above answers when
its replicas hold it in memory. To see what durability costs Kafka we ran it once more with
`log.flush.interval.messages=1`, an fsync of every append on the leader and on the followers before
it counts, everything else as above. At 3,000,000 msg/s, p50 / p99 in milliseconds (worst window in
brackets) and the cores of the three brokers:

| Partitions | Kafka, fsync on every append | Redpanda | Pulsar | Queen 2.4 | Kafka, no fsync |
|---|---|---|---|---|---|
| 100 | 7.5 / 12 (13), 29.3 | 6.9 / 14 (14), 19.9 | 13 / 25 (26), 11.4 | 7.5 / 17 (17), 27.7 | 6.0 / 7.5 (8), 18.5 |
| 1,000 | 7.6 / 198 (285), 30.2 | 6.8 / 14 (20), 22.4 | 6.2 / 20 (21), 15.1 | 6.9 / 16.5 (19), 24.6 | 6.0 / 8.1 (8), 21.0 |
| 10,000 | 95 / 281 (301), 39.1 | 68 / 782 (815), 30.7 | 6.7 / 20 (21), 17.2 | 7.1 / 20 (26), 24.8 | 8.4 / 21 (78), 52.8 |

With few partitions durability costs Kafka little: 1.5 ms on the median and 4 ms on the p99 at 100
partitions, where it is still the fastest. The cost grows with the partition count, because Kafka
syncs a file per partition: at 1,000 partitions its p99 swung from 89 to 440 ms over the first 50
seconds and settled at 12 ms in the last two windows, and at 10,000 it read 95 / 281 ms. Redpanda,
which fsyncs every write by default, reads 14 ms at 1,000 partitions, fourteen times under Kafka
with an fsync, and 782 ms at 10,000 against Kafka's 281. Pulsar and Queen read 20 ms at 10,000:
a bookie's journal takes the entries of all its ledgers, and Queen's log those of all the
partitions of a raft group, so more partitions do not mean more fsyncs. With 1 KB messages at 1,000,000 msg/s on 1,000
partitions, Kafka with an fsync read 8.1 / 11 ms on 15.9 cores against 7.4 / 9.4 without, and Queen
6.8 / 16. Kafka's disks wrote about twice the bytes with the fsync (978 against 435 MB/s a broker at
100 partitions): a page that is still filling is written again on every flush. Each Kafka point ran
once, and its ceiling with an fsync on every append was not measured.

## Kafka in queue mode

Every Kafka number above uses a consumer group: each partition goes to one consumer, which commits
one offset for everything it has read. Queen keeps a lease and an ack for every message. Kafka has
a mode that does the same: a share group ([KIP-932](https://cwiki.apache.org/confluence/display/KAFKA/KIP-932%3A+Queues+for+Kafka)),
where the partition's leader locks each record for the consumer that fetched it (30 s by
default), takes an ack per record, redelivers what is released or times out, and writes that state
to an internal topic through a share coordinator. Several consumers read one partition at once, so
a share group keeps no order within a partition; Queen gives a partition to one worker at a time.
We ran it at 3,000,000 msg/s with the same 297 consumers as the consumer-group runs, which needs
`group.share.max.size` raised from its default of 200 to 300. Each consumer accepts every record it
polls, and the acks travel with its next fetch. p50 / p99 in milliseconds (worst window in
brackets) and the cores of the three brokers:

| Partitions | Kafka, share group | cores | Kafka, consumer group | cores | Queen 2.4 | cores |
|---|---|---|---|---|---|---|
| 100 | 6.3 / 9.3 (9) | 24.6 | 6.0 / 7.5 (8) | 18.5 | 7.5 / 17 (17) | 27.7 |
| 1,000 | 6.4 / 23 (23) | 35.0 | 6.0 / 8.1 (8) | 21.0 | 6.9 / 16.5 (19) | 24.6 |
| 10,000 | 78 / 163 (206) | 52.2 | 8.4 / 21 (78) | 52.8 | 7.1 / 20 (26) | 24.8 |

**Figure.** p99 end-to-end latency at 3,000,000 msg/s against the partition count (log scale): Queen 2.4, Kafka 4.3.1 with a consumer group and Kafka 4.3.1 with a share group. Queen reads 24 ms at 10 partitions, 17 at 100, 16.5 at 1,000, 20 at 10,000 and 50,000, 15 at 100,000, 20 at 1,000,000 and 25 at 10,000,000. Kafka with a consumer group reads 11, 7.5, 8.1 and 21 ms from 10 to 10,000 partitions and 105 at 25,000 (not drawn). Kafka with a share group reads 9.3 ms at 100 partitions, 23 at 1,000 and 163 at 10,000.

In queue mode Kafka's p99 rises from 9 ms at 100 partitions to 23 at 1,000 and 163 at 10,000, where Queen 2.4 reads 16.5 and 20. In streaming it stays at 8 to 21 ms up to 10,000 (105 at 25,000, off the axis).

Values: p99 end-to-end latency.

| partitions in the queue or topic | Queen 2.4 | Kafka, consumer group (streaming) | Kafka, share group (queue mode) |
|---|---|---|---|
| 10 | 24 ms | 11 ms |  |
| 100 | 17 ms | 7.5 ms | 9.3 ms |
| 1k | 17 ms | 8.1 ms | 23 ms |
| 10k | 20 ms | 21 ms | 163 ms |
| 50k | 20 ms |  |  |
| 100k | 15 ms |  |  |
| 1M | 20 ms |  |  |
| 10M | 25 ms |  |  |

Source: `benchmark-queen/2026-10-12-three-node-new-forms/runs, the fm3-* folders of benchmark-queen/2026-10-15-bytes-per-msg/runs/gcp and a24-* (Queen); benchmark-queen/2026-09-30-kafka-pulsar/runs/kafka, the *-3000k-gcp24-xfs-t24 and *-3000k-gcp24-xfs-t24-share folders (Kafka)`.

**Figure.** p50 end-to-end latency at 3,000,000 msg/s against the partition count (log scale). Queen 2.4 reads 10 ms at 10 partitions and 6.9 to 8.9 from 100 to 10,000,000. Kafka with a consumer group reads 6.0 to 8.4 ms from 10 to 10,000 partitions and 58 at 25,000. Kafka with a share group reads 6.3 ms at 100 partitions, 6.4 at 1,000 and 78 at 10,000.

Kafka's median stays near 6 ms in both modes up to 1,000 partitions, against Queen 2.4's 7 to 10. In queue mode it reaches 78 ms at 10,000, where Queen reads 7.1.

Values: p50 end-to-end latency.

| partitions in the queue or topic | Queen 2.4 | Kafka, consumer group (streaming) | Kafka, share group (queue mode) |
|---|---|---|---|
| 10 | 10 ms | 6.4 ms |  |
| 100 | 7.5 ms | 6 ms | 6.3 ms |
| 1k | 6.9 ms | 6 ms | 6.4 ms |
| 10k | 7.1 ms | 8.4 ms | 78 ms |
| 25k |  | 58 ms |  |
| 50k | 7.7 ms |  |  |
| 100k | 7.1 ms |  |  |
| 1M | 7.5 ms |  |  |
| 10M | 8.9 ms |  |  |

Source: `as the p99 figure above`.

**Figure.** Cores used by the three brokers together at 3,000,000 msg/s against the partition count (log scale). Queen 2.4 uses 21 cores at 10 partitions and 24.6 to 27.7 from 100 to 10,000,000. Kafka with a consumer group uses 14.9 at 10, 18.5 at 100, 21.0 at 1,000, 52.8 at 10,000 and 57.0 at 25,000, and does not carry 50,000 or 100,000. Kafka with a share group uses 24.6 at 100, 35.0 at 1,000 and 52.2 at 10,000.

Queue mode costs Kafka cores: 6 more than streaming at 100 partitions and 14 more at 1,000, 42% above Queen there. Queen's CPU does not move with the partition count.

Values: cores, three brokers together (of 72).

| partitions in the queue or topic | Queen 2.4 | Kafka, consumer group (streaming) | Kafka, share group (queue mode) |
|---|---|---|---|
| 10 | 21 | 15 |  |
| 100 | 28 | 19 | 25 |
| 1k | 25 | 21 | 35 |
| 10k | 25 | 53 | 52 |
| 25k |  | 57 |  |
| 50k | 25 | 50 (see caption) |  |
| 100k | 25 | 61 (see caption) |  |
| 1M | 25 |  |  |
| 10M | 25 |  |  |

Source: `the grid rows of the Queen 2.4 runs; report/report.py over the Kafka folders above`.

Every point was carried. Keeping a state for every record costs Kafka CPU first: 6 more cores at
100 partitions and 14 more at 1,000, where it uses 42% more than Queen. At 1,000 partitions its p99
nearly triples, to 23 ms, above Queen's 16.5, while its median stays near 6 ms. At 10,000 partitions
the share group reads 78 / 163 ms against Queen's 7.1 / 20, on twice Queen's cores. Kafka still
answers a write from memory here, where Queen waits for two fsyncs. Each point ran once, and the
share group was not run at 10 partitions or above 10,000.

## Kafka and the filesystem

Our first Kafka runs used the brokers' ext4 as it came, with `noatime`, and our hosts' background
writeback from 5% of memory: on 88 GB brokers about 3.6 GB of dirty pages, written out at the
RAID's limit about every 12 seconds. Kafka's p99 came in bursts. At 3,000,000 msg/s it read 938 ms
at 10 partitions and 43 to 96 ms from 100 to 10,000 in the median window, with worst windows up to
1,401 ms; at 2,000,000 msg/s on 1,000 partitions it read 71 and 95 ms in the worst window of two
runs, against 25 ms on the 16-vCPU DigitalOcean machines on 2026-10-13. In every 10-second
window all nine load processes on all three load generators read the same p99, and the producers'
p99 equalled the end-to-end p99: a stall on the write path of the whole cluster at once, with the
brokers at 4 of 24 cores.

A sampler listed every 20 ms the Kafka threads blocked in the kernel. Request handlers and replica
fetchers stopped for up to 300 ms at a time, nearly always in the same place: a `write()` updating
the file's modification time (`ext4_dirty_inode`) waited for write access to an inode block that
the journal was committing (`jbd2_journal_get_write_access`), and the commit waited behind the
page cache writeback queued on the local disks, 50 to 190 ms a write in the worst seconds. So
every append could wait for a journal commit, and the commit for the disks.

We changed one thing at a time, a fresh cluster for every run. Background writeback from 1 GiB
writes the page cache out in smaller steps; it lowered the worst windows but not every median.
Mounting ext4 with `lazytime` keeps timestamp updates in memory, so an append no longer touches
the journal: it removed most of the stall (2,000,000 msg/s on 1,000 partitions went to 6.3 / 9.9
ms in the worst window). XFS, with the thread pools sized for 24 cores, removed the spikes that
were left. We changed the filesystem and the thread pools together, so these runs do not split
that last step between them. Kafka's p99 at 3,000,000 msg/s, the median of the three windows
(worst window in brackets):

| Partitions | ext4, writeback from 5% | ext4, from 1 GiB | ext4 `lazytime`, 1 GiB | XFS, 1 GiB, threads for 24 vCPU |
|---|---|---|---|---|
| 10 | 938 (1,401) | 506 (514) | 272 (350) | 11 (45) |
| 100 | 96 (103) | 50 (61) | 13 (15) | 7.5 (8) |
| 1,000 | 43 (148) | 88 (104) | 15 (29) | 8.1 (8) |
| 10,000 | 46 (138) | 61 (110) | 34 (46) | 21 (78) |

**Figure.** Kafka's p99 end-to-end latency at 3,000,000 msg/s by partition count, the median of three 10-second windows, with four settings: ext4 with writeback at 5% of memory; ext4 with writeback from 1 GiB; ext4 mounted lazytime with 1 GiB; XFS with 1 GiB and thread pools for 24 vCPU. 10 partitions: 938, 506, 272 and 11 ms. 100: 96, 50, 13 and 7.5. 1,000: 43, 88, 15 and 8.1. 10,000: 46, 61, 34 and 21.

Kafka's p99 at 3M msg/s, the median of three windows. Writeback from 1 GiB helped at 10 and 100 partitions only; ext4 lazytime cut the p99 at every count; XFS with thread pools for 24 vCPU brought it to 7.5 to 21 ms.

Values: p99 end-to-end latency, median window.

| | ext4, writeback at 5% of memory | ext4, writeback from 1 GiB | ext4 lazytime, 1 GiB | XFS, 1 GiB, threads for 24 vCPU |
|---|---|---|---|---|
| 10 partitions | 938 ms | 506 ms | 272 ms | 11 ms |
| 100 | 96 ms | 50 ms | 13 ms | 7.5 ms |
| 1,000 | 43 ms | 88 ms | 15 ms | 8.1 ms |
| 10,000 | 46 ms | 61 ms | 34 ms | 21 ms |

Source: `benchmark-queen/2026-09-30-kafka-pulsar/runs/kafka, the *-3000k-gcp24, *-3000k-gcp24-dbb1g, *-3000k-gcp24-lazy and *-3000k-gcp24-xfs-t24 folders`.

If you run Kafka on machines like these, put its log directories on XFS, as Kafka's documentation
recommends, or at least mount ext4 with `lazytime`, and bound background writeback
(`vm.dirty_background_bytes`) so the page cache goes to disk in small steps. That is why every
system on this page ran on XFS.

## Where each one leads

Kafka, run as on this page, leads on latency up to 1,000 partitions, a median near 6 ms and a p99
of 7.5 to 11 ms at 3,000,000 msg/s, and on throughput, about 8,000,000 msg/s on one topic of 1,000
partitions where one Queen queue carried 5,000,000. It answers from memory: with an fsync on every
append it keeps that lead at 100 partitions and loses it at 1,000 (198 ms) and 10,000 (281 ms).
From 10,000 partitions it needs twice the CPU of Queen or Pulsar, and it did not carry 50,000.

Queen leads on the partition count. It is the one system that carried 3,000,000 msg/s at 100,000
partitions and more, with the same latency, a p99 of 15 to 25 ms, and the same 21 to 28 cores
from 10 partitions to ten million, an fsync on two nodes before every answer. With 1 KB messages
its median is under Kafka's from 100 partitions up, and at 10,000 its p99 too.

Pulsar uses the least CPU wherever it carried the load and holds a p99 of 20 to 25 ms up to 50,000
partitions, with the same two fsyncs before the answer; its Go client needed six load generators at
50,000 partitions, and at 100,000 it did not carry the rate. Redpanda, which fsyncs every write,
reads 14 ms at 100 and 1,000 partitions, against 198 ms for Kafka with an fsync at 1,000, and falls
behind from 10,000.

## What these runs do not show

- Pulsar and Redpanda with 1 KB messages or in transactions, and their ceilings: both ran at
  5,000,000 msg/s on 1,000 partitions only, and Pulsar with its journal sharing the RAID.
- Redpanda with write caching on, where it answers from memory as Kafka does here; Redpanda at
  100,000 partitions.
- Pulsar at 100,000 partitions with enough load generators to rule its client out.
- Reads from the disks. Every consumer read the newest messages, which the brokers still held in
  memory; consumers catching up on data older than what fits in 88 GB were not run.
- Kafka's ceiling with an fsync on every append, and repeats of its fsync points (one run each).
- Kafka on XFS with the thread pools of 16-vCPU brokers, or ext4 with the larger pools: we changed
  the filesystem and the thread pools together.
- Repeated runs for every point: most points ran once, on three sets of VMs of the same type.
- Kafka at 1,000,000 and 10,000,000 partitions, Pulsar and Redpanda past 100,000, Queen at 25,000.
- Kafka's share group at 10 partitions, above 10,000, with 1 KB messages, or at its ceiling, and
  with consumers that release or reject records.

## The runs

Queen 2.4: [`benchmark-queen/2026-10-15-bytes-per-msg/`](https://github.com/queen-mq/queen/tree/master/benchmark-queen/2026-10-15-bytes-per-msg),
the `runs/gcp/fm3-*` folders (3,000,000 msg/s), `fm3r1-*` and `fm3r2-*` (repeats at 1,000
partitions), `f1k-*` (1 KB), `bpm*` and `fxc-*` (the ceiling), with the README's tables for every
node's memory and disk; and `a24-*` in
[`benchmark-queen/2026-10-12-three-node-new-forms/runs/`](https://github.com/queen-mq/queen/tree/master/benchmark-queen/2026-10-12-three-node-new-forms)
(1,000 and 50,000 partitions, and 5,000,000 msg/s on 1,000, 10-16). Kafka: the `*-gcp24-xfs-t24` folders of
`benchmark-queen/2026-09-30-kafka-pulsar/runs/kafka/` for the tables, `1x1000-<rate>k-gcp24-xfs-t24`
for its ceiling, `*-fsync` with an fsync on every append, `*-share` the share groups,
`1x1000-3000k-gcp24-xfs-t24-1016` and `1x1000-5000k-gcp24-xfs-t24-1016` the runs of 10-16; on ext4, `*-gcp24` (writeback from 5%),
`*-gcp24-dbb1g` (from 1 GiB) and `*-gcp24-lazy` (`lazytime`); `*-probe` and `*-lazytime` hold the
kernel samples in `dstate/`. Pulsar: `runs/pulsar/1x<partitions>-3000k-gcp24`, with
`1x50000-3000k-gcp24-6l-b5`, `1x100000-3000k-gcp24-6l-b5-nowarm-r` and `1x1000-5000k-gcp24-6l`
on six load generators. Redpanda: `runs/redpanda/1x<partitions>-3000k-gcp24` and
`1x1000-5000k-gcp24`. The load generators are `qload`, `kload` and
`pload` of
[`benchmark-queen/2026-09-30-kafka-pulsar/mqload`](https://github.com/queen-mq/queen/tree/master/benchmark-queen/2026-09-30-kafka-pulsar/mqload).
[How we measure](/benchmarks/methodology/) describes the harness.

Source: https://queenmq.com/benchmarks/queen-vs-kafka/index.mdx
