AceMQ AMQP workloads

A load generator for AMQP brokers, in three shapes: a studio you design a topology in and watch it run, a command line a pipeline calls, and a Java DSL a test uses. One open-loop rate schedule under all three, honest latency percentiles, and a report that says what it measured rather than what you should do about it.

java -jar acemq-workloads-studio.jar     # then open http://localhost:8480
java -jar acemq-workload.jar -f workload.yaml --report reports/ --format html,json
name: orders-peak
broker: amqp://guest:${BROKER_PASSWORD}@localhost:5672

topology:   { exchange: orders, queue: orders.new, routingKey: order.created }
publishers: { threads: 4, rate: 50000, messageSize: 1024 }
consumers:  { concurrency: 8, prefetch: 100, handlerTime: 1ms }

warmup: 10s
runFor: 2m

expect:
  throughputAtLeast: 45000
  p99Below: 50ms

Or the same thing from Java:

WorkloadReport report = Workload.named("orders-peak")
        .topology(t -> t.exchange("orders", "topic")
                        .queue("orders.new")
                        .boundTo("orders", "order.created"))
        .publishers(p -> p.threads(4).rate(50_000).messageSize(1024))
        .consumers(c -> c.concurrency(8).prefetch(100)
                         .handlerTime(Duration.ofMillis(1)))
        .warmup(Duration.ofSeconds(10))
        .runFor(Duration.ofMinutes(2))
        .expect(Objective.throughputAtLeast(45_000))
        .expect(Objective.p99Below(Duration.ofMillis(50)))
        .run("amqp://localhost");

The one thing that makes this worth using

Coordinated omission. Almost every homemade load tool has this bug, and it makes the output confidently wrong.

If publishers loop as fast as they can, then when the broker slows down the offered load drops with it — so latency percentiles come out better the worse the broker behaves. The stall is invisible, because the generator stalled with it and never offered the load it was supposed to.

This tool publishes on a schedule and measures latency from when a message was due. How, and why it matters.

Validity before results

The most valuable checks here are not the ones that judge the broker. They are the ones that decide whether the run measured the broker at all.

[INVALID] generator-kept-up
    observed:  publishes ran 6220.2ms behind their own schedule at p99;
               the configured rate was 2,000/s and the achieved rate was 1,048/s
    means:     the configured load was never offered, so this run does not show
               what the broker can take

  result: INVALID — this run did not measure what it was asked to

Reporting "300,000/s: FAILED" when the generator could only produce 90,000 blames the wrong machine. An invalid run never passes. The rules.

Diagnosis, not prescription

Every finding carries the measurement that produced it, and none of them tell you what to change.

A tool that prints "increase prefetch to 250" is guessing — it does not know your handler's processing time, your message sizes, your network, or what else shares the broker. And a confident recommendation from a tool gets followed, which makes a wrong one worse than silence.

Where to start

What this is not