Comparison
Same workload. Different broker.
On the tested synthetic workload, predictive scheduling beats RabbitMQ and Redis FIFOFIFOFirst in, first out. Messages are served strictly in arrival order, like a single supermarket line.Learn more ↗, while Harbinger has higher brokerBrokerThe server in the middle. Producers hand messages to it, and consumers pull messages from it.Learn more ↗ cost. The v4 follow-up isolates agingAgingA message that has waited too long is moved up one priority level, so low-priority work is delayed but never stuck forever.Learn more ↗'s contribution and tests a stronger scheduling baseline.
Two questions, two answers
- −21.7%mean latency vs RabbitMQ FIFO
- −25.5%mean latency vs Redis FIFO
- −57.6%mean latency with the predictor as a RabbitMQ sidecar
- 5,293/sHarbinger's peak throughputRabbitMQ 7,371, Redis 21,182
A win against the tested FIFO baselines
On the same production-flow workload, predictive routing cut meanMeanThe ordinary average. It is pulled up by a few very slow items, so it shows the overall experience better than the median does.Learn more ↗ latencyLatencyHow long a message takes from being sent to being finished, including time spent waiting in the queue.Learn more ↗ by more than 20% against both FIFOFIFOFirst in, first out. Messages are served strictly in arrival order, like a single supermarket line.Learn more ↗ brokers and the short-job medianMedianThe middle value: half the jobs are faster, half slower. It describes a typical job and ignores rare outliers.Learn more ↗ by about 90%. The gateGateA pass/fail test agreed before running an experiment. Predictive routing is only allowed if every gate passes. passed.
Harbinger the broker loses
With empty handlers it delivers about 5,300 messages per second, 72% of RabbitMQ and a quarter of Redis, and falls behind at 20,000 per second.
The setup
Identical producersProducerA program that creates messages and sends them to the queue.Learn more ↗, handlers, workersWorkerA process that takes one message at a time and runs the work for it., arrival schedule and seedsSeedA starting number for a random generator. Same seed, same random-looking data, so a run can be repeated exactly.Learn more ↗. Only the brokerBrokerThe server in the middle. Producers hand messages to it, and consumers pull messages from it.Learn more ↗ and its policy change. The comparison was pre-registeredPre-registrationWriting down the hypotheses, setup and pass/fail rules before running the experiment, so results can't be quietly reinterpreted afterwards.Learn more ↗ and frozen before any registered run.
| Arm | Policy |
|---|---|
| Harbinger FIFO | One level |
| Harbinger static, tuned | A well-configured static priority mapStatic priorityA hand-written rule that fixes each job type's priority in advance and never changes with what's observed. |
| Harbinger predictive | Per-keyKeyWhat Harbinger groups messages by when learning: the producer, optionally plus a job_type label (and size). Each key gets its own duration history. predictorPredictorThe part of the broker that estimates how long a message will take, using what earlier messages with the same key took., strict priority, default agingAgingA message that has waited too long is moved up one priority level, so low-priority work is delayed but never stuck forever.Learn more ↗ (primary arm) |
| Harbinger predictive, weighted | PredictorPredictorThe part of the broker that estimates how long a message will take, using what earlier messages with the same key took. + level weightsLevel weightsInstead of always serving the highest level first, each level gets a share of worker time (for example 8 : 3 : 1). Lower levels still make steady progress. [8, 3, 1] + pausing agingPausing agingAging that holds off promoting a message while the level above is still working through earlier promoted messages. It stops a long backlog from pushing everything to the top. |
| RabbitMQ FIFO | Classic queue, manual ackAckShort for acknowledgement. A consumer tells the broker it finished a message, and here it also reports how long the work took.Learn more ↗, prefetch 1 |
| RabbitMQ priority | Max priority 3, the same static mapStatic priorityA hand-written rule that fixes each job type's priority in advance and never changes with what's observed., no agingAgingA message that has waited too long is moved up one priority level, so low-priority work is delayed but never stuck forever.Learn more ↗ |
| RabbitMQ predictive | Priorities from a sidecar running Harbinger's own predictorPredictorThe part of the broker that estimates how long a message will take, using what earlier messages with the same key took. |
| Redis FIFO | Reliable list queue: LPUSH, BLMOVE to a per-workerWorkerA process that takes one message at a time and runs the work for it. list, LREM |
Ground rules
- AWS us-east-1a in one placement group. BrokerBrokerThe server in the middle. Producers hand messages to it, and consumers pull messages from it.Learn more ↗ on a c8i.xlarge (4 vCPU), clients on a c8i.2xlarge (8 vCPU), with a fresh broker container for every arm, seedSeedA starting number for a random generator. Same seed, same random-looking data, so a run can be repeated exactly.Learn more ↗ and rate.
- Versions: RabbitMQ 3.12.1 (Erlang 25.3), Redis 7.0.15. Default settings except those listed.
- Nothing is durable on any brokerBrokerThe server in the middle. Producers hand messages to it, and consumers pull messages from it.Learn more ↗: Harbinger is in memory, RabbitMQ uses transient messages with no publisher confirms, and Redis has no RDB or AOF.
- RabbitMQ runs with prefetch 1, which is how its priorities reorder anything at all, and which also limits its throughputThroughputHow many messages get finished per second.Learn more ↗.
- Retries follow each brokerBrokerThe server in the middle. Producers hand messages to it, and consumers pull messages from it.Learn more ↗'s own rules: near the head of RabbitMQ's queue, the tail of Redis's, and the original priority on Harbinger.
Scheduling outcome
The production flow: nine job types, a flash sale and a mid-run cost shift, five seedsSeedA starting number for a random generator. Same seed, same random-looking data, so a run can be repeated exactly.Learn more ↗, 300 s of arrivals and 4 workersWorkerA process that takes one message at a time and runs the work for it.. The primary arm is Harbinger predictive.
Mean latency, median across seeds (s). Lower is better.
Grey bars are FIFO. Bars with a dot are Harbinger's own routing. The paired changes are medians of per-seed differences, so they differ slightly from the ratio of the medians.
| Criterion | Estimate [95% interval] | Result |
|---|---|---|
| P1 meanMeanThe ordinary average. It is pulled up by a few very slow items, so it shows the overall experience better than the median does.Learn more ↗ vs RabbitMQ FIFOFIFOFirst in, first out. Messages are served strictly in arrival order, like a single supermarket line.Learn more ↗ (≤ −15%) | −21.7% [−26.2, −18.4] | Pass |
| P2 short-job medianMedianThe middle value: half the jobs are faster, half slower. It describes a typical job and ignores rare outliers.Learn more ↗ vs RabbitMQ FIFOFIFOFirst in, first out. Messages are served strictly in arrival order, like a single supermarket line.Learn more ↗ (≤ −30%) | −89.5% [−98.8, −75.5] | Pass |
| P3 meanMeanThe ordinary average. It is pulled up by a few very slow items, so it shows the overall experience better than the median does.Learn more ↗ vs Redis FIFOFIFOFirst in, first out. Messages are served strictly in arrival order, like a single supermarket line.Learn more ↗ (≤ −15%) | −25.5% [−29.5, −22.6] | Pass |
| P4 short-job medianMedianThe middle value: half the jobs are faster, half slower. It describes a typical job and ignores rare outliers.Learn more ↗ vs Redis FIFOFIFOFirst in, first out. Messages are served strictly in arrival order, like a single supermarket line.Learn more ↗ (≤ −30%) | −90.7% [−99.0, −78.0] | Pass |
| G1 all-message P99P95 / P99 (tail latency)P99 is the time that 99% of messages beat. It describes the slowest 1%, the "tail", which matters because users notice the slow ones.Learn more ↗ vs RabbitMQ FIFOFIFOFirst in, first out. Messages are served strictly in arrival order, like a single supermarket line.Learn more ↗ (≤ 1.25×) | 1.10× [1.07, 1.12] | Pass |
| G2 export max wait vs RabbitMQ priority (≤ 1.0×) | 0.20× [0.18, 0.23] | Pass |
| G3 no lost work | Every message completed | Pass |
| G4 throughputThroughputHow many messages get finished per second.Learn more ↗ vs RabbitMQ FIFOFIFOFirst in, first out. Messages are served strictly in arrival order, like a single supermarket line.Learn more ↗ (≥ 0.98×) | 1.00× [0.99, 1.00] | Pass |
What it shows
- The gateGateA pass/fail test agreed before running an experiment. Predictive routing is only allowed if every gate passes. passes by less than expected. The pre-registrationPre-registrationWriting down the hypotheses, setup and pass/fail rules before running the experiment, so results can't be quietly reinterpreted afterwards.Learn more ↗ expected −25% to −28%, and against RabbitMQ it is −21.7%. Part of the gap is Harbinger's own FIFOFIFOFirst in, first out. Messages are served strictly in arrival order, like a single supermarket line.Learn more ↗ arm running 11% slower than RabbitMQ's, just outside the ±10% bar.
- The idea transfers: on this workload the RabbitMQ sidecar cut meanMeanThe ordinary average. It is pulled up by a few very slow items, so it shows the overall experience better than the median does.Learn more ↗ latencyLatencyHow long a message takes from being sent to being finished, including time spent waiting in the queue.Learn more ↗ by 57.6% against its FIFOFIFOFirst in, first out. Messages are served strictly in arrival order, like a single supermarket line.Learn more ↗, versus Harbinger predictive at −21.7% and weighted at −52.4% against RabbitMQ FIFO.
- This original cross-brokerBrokerThe server in the middle. Producers hand messages to it, and consumers pull messages from it.Learn more ↗ experiment does not isolate agingAgingA message that has waited too long is moved up one priority level, so low-priority work is delayed but never stuck forever.Learn more ↗. RabbitMQ priorities never age, while Harbinger's default aging promotes waiting long jobs. Separate v4 experiments now measure the cost of that protection within Harbinger.
Aging isolation results ↗Tail costs and real-trace isolation →
Protecting long jobs
Static priorities without agingAgingA message that has waited too long is moved up one priority level, so low-priority work is delayed but never stuck forever.Learn more ↗ greatly increase long-job waits here. The longest export waited 101 s on RabbitMQ priority, 5.2× FIFOFIFOFirst in, first out. Messages are served strictly in arrival order, like a single supermarket line.Learn more ↗. Harbinger's predictive routing held that wait to a fifth of it. Finite observed waits do not establish general starvation freedom for either policy.
Longest export wait, median across seeds (s)
| Criterion | Estimate [95% interval] | Result |
|---|---|---|
| S1 Harbinger FIFOFIFOFirst in, first out. Messages are served strictly in arrival order, like a single supermarket line.Learn more ↗ vs RabbitMQ FIFO, meanMeanThe ordinary average. It is pulled up by a few very slow items, so it shows the overall experience better than the median does.Learn more ↗ (±10%) | +11.0% [+10.4, +11.6] | Fail |
| S2 Harbinger FIFOFIFOFirst in, first out. Messages are served strictly in arrival order, like a single supermarket line.Learn more ↗ vs Redis FIFO, meanMeanThe ordinary average. It is pulled up by a few very slow items, so it shows the overall experience better than the median does.Learn more ↗ (±10%) | +5.6% [+4.9, +6.2] | Pass |
| S3 RabbitMQ predictive vs RabbitMQ FIFOFIFOFirst in, first out. Messages are served strictly in arrival order, like a single supermarket line.Learn more ↗, meanMeanThe ordinary average. It is pulled up by a few very slow items, so it shows the overall experience better than the median does.Learn more ↗ (≤ −15%) | −57.6% [−58.6, −56.8] | Pass |
| S4 RabbitMQ predictive vs RabbitMQ priority, meanMeanThe ordinary average. It is pulled up by a few very slow items, so it shows the overall experience better than the median does.Learn more ↗ (≤ +10%) | −26.3% [−31.5, −21.1] | Pass |
| S6 Harbinger weighted vs RabbitMQ FIFOFIFOFirst in, first out. Messages are served strictly in arrival order, like a single supermarket line.Learn more ↗, meanMeanThe ordinary average. It is pulled up by a few very slow items, so it shows the overall experience better than the median does.Learn more ↗ (≤ −15%) | −52.4% [−53.4, −51.0] | Pass |
| S7 Harbinger weighted vs RabbitMQ FIFOFIFOFirst in, first out. Messages are served strictly in arrival order, like a single supermarket line.Learn more ↗, P99P95 / P99 (tail latency)P99 is the time that 99% of messages beat. It describes the slowest 1%, the "tail", which matters because users notice the slow ones.Learn more ↗ (≤ 1.25×) | 1.21× [1.20, 1.24] | Pass |
| S8 RabbitMQ priority vs RabbitMQ FIFOFIFOFirst in, first out. Messages are served strictly in arrival order, like a single supermarket line.Learn more ↗, export max wait (≤ 2×) | 5.23× [4.79, 5.55] | Fail, expected |
What each broker costs
Empty handlers, 64-byte payloads, 4 producersProducerA program that creates messages and sends them to the queue.Learn more ↗ and 4 consumersConsumerA program that pulls messages from the queue and does the work they describe.Learn more ↗, 30 s runs with a 5 s warm-up and five repetitions. This tests the brokerBrokerThe server in the middle. Producers hand messages to it, and consumers pull messages from it.Learn more ↗ itself, with no scheduling in play.
Peak sustained delivery rate, median (messages per second)
At a fixed rate
| Broker | Kept up | P50 | P99 |
|---|---|---|---|
| Harbinger | 5/5 | 0.30 ms | 10.6 ms |
| RabbitMQ | 5/5 | 0.12 ms | 0.31 ms |
| Redis | 5/5 | 0.10 ms | 0.31 ms |
Harbinger is at its limit. Two of its five runs built a queue (P50MedianThe middle value: half the jobs are faster, half slower. It describes a typical job and ignores rare outliers.Learn more ↗ 91 ms and 462 ms, P99P95 / P99 (tail latency)P99 is the time that 99% of messages beat. It describes the slowest 1%, the "tail", which matters because users notice the slow ones.Learn more ↗ 446 ms and 1,227 ms). The other three stayed at 0.30 ms P50.
What it shows
- Harbinger is the slowest of the three, as the pre-registrationPre-registrationWriting down the hypotheses, setup and pass/fail rules before running the experiment, so results can't be quietly reinterpreted afterwards.Learn more ↗ expected: about 72% of RabbitMQ's throughputThroughputHow many messages get finished per second.Learn more ↗ and a quarter of Redis's.
- Its latencyLatencyHow long a message takes from being sent to being finished, including time spent waiting in the queue.Learn more ↗ matches the others only at 1,000 msg/s. The pre-registrationPre-registrationWriting down the hypotheses, setup and pass/fail rules before running the experiment, so results can't be quietly reinterpreted afterwards.Learn more ↗ expected the same sub-millisecond range at 5,000 msg/s, and that held in only three of five runs.
- RabbitMQ's undelivered messages at peak rate need care. It accepted about 56,000 publishes per second but delivered about 7,400, and 8–14% of messages were still undelivered when the 60 s drain ended. Without publisher confirms the benchmark cannot tell whether they were still queued, so they count as not delivered. Every fixed-rate RabbitMQ run lost nothing.
Taken together
- The scheduling idea is separable from Harbinger's brokerBrokerThe server in the middle. Producers hand messages to it, and consumers pull messages from it.Learn more ↗. The same predictorPredictorThe part of the broker that estimates how long a message will take, using what earlier messages with the same key took. lowers meanMeanThe ordinary average. It is pulled up by a few very slow items, so it shows the overall experience better than the median does.Learn more ↗ latencyLatencyHow long a message takes from being sent to being finished, including time spent waiting in the queue.Learn more ↗ on RabbitMQ by more than half and beats a hand-tuned RabbitMQ priority map by 26%.
- In the production flow brokerBrokerThe server in the middle. Producers hand messages to it, and consumers pull messages from it.Learn more ↗ cost is small. Handlers take milliseconds to seconds at about 116 messages per second, far below Harbinger's limit, so it shows only as the 11% gap in the FIFOFIFOFirst in, first out. Messages are served strictly in arrival order, like a single supermarket line.Learn more ↗ arms.
- If handlers are trivial and the rate is tens of thousands per second, this benchmark says Redis or RabbitMQ will serve better today. Harbinger fell behind at 20,000 msg/s.
- Prioritising short jobs has a long-job cost. In this workload RabbitMQ static priorityStatic priorityA hand-written rule that fixes each job type's priority in advance and never changes with what's observed.'s longest export waited 5.2× longer than under FIFOFIFOFirst in, first out. Messages are served strictly in arrival order, like a single supermarket line.Learn more ↗; v4 Harbinger agingAgingA message that has waited too long is moved up one priority level, so low-priority work is delayed but never stuck forever.Learn more ↗-off runs separately measure higher P99P95 / P99 (tail latency)P99 is the time that 99% of messages beat. It describes the slowest 1%, the "tail", which matters because users notice the slow ones.Learn more ↗ and long waits.
Limits of the comparison
- One synthetic workload, one cloud provider and region, two instance types.
- The brokerBrokerThe server in the middle. Producers hand messages to it, and consumers pull messages from it.Learn more ↗ comparison and throughputThroughputHow many messages get finished per second.Learn more ↗ measurements are the original v3 results. V4 adds separate baseline and agingAgingA message that has waited too long is moved up one priority level, so low-priority work is delayed but never stuck forever.Learn more ↗ studies, not a new RabbitMQ/Redis rerun.
- Default brokerBrokerThe server in the middle. Producers hand messages to it, and consumers pull messages from it.Learn more ↗ settings, except those listed. RabbitMQ prefetch 1 limits its throughputThroughputHow many messages get finished per second.Learn more ↗ and is noted wherever it matters.
- Harbinger's scheduling arms ran the benchmark build of the brokerBrokerThe server in the middle. Producers hand messages to it, and consumers pull messages from it.Learn more ↗, which has observation hooks. The broker-cost test ran the production server.
- Non-durable modes only. Kafka, Pulsar, managed cloud queues and durable modes were out of scope.