Harbinger

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.

Summary

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
Scheduling outcome

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.

Broker cost

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.

Method

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.

The eight arms of the scheduling comparison
ArmPolicy
Harbinger FIFOOne level
Harbinger static, tunedA 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 predictivePer-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, weightedPredictorPredictorThe 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 FIFOClassic 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 priorityMax 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 predictivePriorities 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 FIFOReliable 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.
Question 1

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.
Harbinger FIFO
7.32 s
Redis FIFO
7.01 s
RabbitMQ FIFO
6.64 s
Harbinger predictive
5.30 s
RabbitMQ priority
4.15 s
Harbinger weighted
3.14 s
RabbitMQ + sidecar
2.84 s

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.

The gate. Primary arm: Harbinger predictive. Paired by seed, bootstrap 95% intervals.
CriterionEstimate [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 workEvery message completedPass
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.

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)
RabbitMQ FIFO
18.3 s
RabbitMQ + sidecar
20.9 s
RabbitMQ priority
101.0 s
Secondary criteria, reported but not gated
CriterionEstimate [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
Question 2

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)
Harbinger
5,293
RabbitMQ (prefetch 1)
7,371
Redis
21,182

At a fixed rate

Offered 5,000 msg/s. Kept up means no loss, at least 99% of the rate delivered, and sends on time.
BrokerKept upP50P99
Harbinger5/50.30 ms10.6 ms
RabbitMQ5/50.12 ms0.31 ms
Redis5/50.10 ms0.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.
Conclusions

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.

Keep reading