Broker compatibility

This library is developed against RabbitMQ 4.x. A large part of the installed base is still on 3.13, so "does it work on 3.13" needs an answer that came from a run rather than from release notes.

It does. The whole integration suite passes unmodified on both, and nothing in this library is gated on a broker version — there is no version detection anywhere in it, so nothing silently degrades. Every row below was produced by running against a real broker of each version; nothing here is transcribed from a changelog.

Verified against RabbitMQ 3.13.7 (Erlang 26.2.5) and 4.3.6 (Erlang 27.3.4)
Minimum supported RabbitMQ 3.13
Developed and released against RabbitMQ 4.x
Re-checked by the compatibility job in ci.yml, on every pull request

What was run

The acemq-transport-rabbitmq integration suite — 53 tests across publishing with confirms, asynchronous and batch publishing, consuming, the retry ladder, dead-letter and parking queues, replay, streams, request and reply, publish options, blocked connections, topology drift and cluster failover — against each broker in turn:

mvn verify -pl acemq-transport-rabbitmq \
    -Dacemq.test.rabbitmq.image=rabbitmq:3.13-management
Broker Tests Failures Errors Skipped
RabbitMQ 4.3.6 53 0 0 2
RabbitMQ 3.13.7 53 0 0 2

The two skipped are LargeClusterIT, which the nightly job starts with an explicit cluster size and an ordinary run does not.

-Dacemq.test.rabbitmq.image is the supported way to point the suite at any broker image, so this is reproducible against whatever version an estate actually runs.

The feature matrix

Everything the library offers works on both versions. There is no feature here that needs 4.x.

Feature 3.13 4.x Notes
Publishing with confirms Yes Yes
sendAsync and sendAll Yes Yes
Unroutable-message detection Yes Yes mandatory plus a return listener; identical on both
Consuming, acknowledgement, prefetch Yes Yes Per-consumer QoS only — see global QoS
Classic queues Yes Yes Always declared durable
Quorum queues Yes Yes The default for declareQueue; needs 3.8+
Streams Yes Yes Needs 3.9+
Stream segment size Yes Yes x-stream-max-segment-size-bytes, added by this library in 0.5
Retry ladder Yes Yes Built from x-message-ttl and x-dead-letter-exchange
Dead-letter and parking queues Yes Yes
Replay Yes Yes
Request and reply Yes Yes
Priority queues Yes Yes x-max-priority
Message TTL and queue expiry Yes Yes x-message-ttl, x-expires
Topology drift detection Yes Yes See drift is immune to the x-queue-type change
Cluster failover and recovery Yes Yes
TLS (amqps://) Yes Yes
Payload encryption Yes Yes Client-side; the broker is not involved
All six serialization formats Yes Yes Client-side; the broker is not involved

Where the two brokers actually differ

These are the differences that exist. None of them changes what this library does, and the reason each one does not is worth stating, because "it happens to pass" and "it cannot break" are different claims.

Transient non-exclusive queues are denied in 4

The clearest behavioural break between the versions, and the library cannot reach it.

3.13.7 4.3.6
queue.declare durable=false, exclusive=false Accepted Refused
Deprecation phase permitted_by_default denied_by_default

On 4 the refusal closes the connection, not just the channel — 541 INTERNAL_ERROR, Feature 'transient_nonexcl_queues' is deprecated — which is a harsher failure than a channel-level PRECONDITION_FAILED and worth knowing about if you meet it.

This library never declares one. Every queue and exchange it declares is durable: durable is hard-coded true on every builder path in Topology, and AceMq.declareQueue has no durability parameter to pass. Quorum and stream queues are forced durable regardless, because the broker requires it.

So this deprecation cannot bite through AceMQ. It can still bite the estate around it — anything else declaring transient queues on 3.13 will fail on the connection after an upgrade, and that is a migration item for the operator rather than for this library.

Classic queue mirroring is gone in 4

3.13.7 4.3.6
PUT /api/policies with ha-mode 201 Created 400 Bad Request — "not recognised policy settings"
Deprecation phase permitted_by_default removed

ram_node_type is likewise removed in 4, and queue_master_locator is denied_by_default.

This library sets none of them. It writes no policies at all — policies are an operator's tool, applied out of band — and the only queue arguments it ever sends are x-dead-letter-exchange, x-dead-letter-routing-key, x-expires, x-max-age, x-max-length-bytes, x-max-priority, x-message-ttl, x-queue-type and x-stream-max-segment-size-bytes. All nine are accepted by both versions.

If a 3.13 estate depends on mirrored classic queues for availability, that availability has to be re-established with quorum queues before the upgrade. declareQueue already defaults to quorum, so an AceMQ application is on the right side of that migration without changing anything.

Drift detection is not affected by the x-queue-type change

RabbitMQ 4 records x-queue-type on every queue; 3.13 leaves it off a classic queue. Declaring the same classic queue through this library and then reading it back over the management API:

3.13.7 4.3.6
arguments reported for a classic queue {} {"x-queue-type": "classic"}

That difference is real, and it is exactly the trap that makes an argument-equality check report drift on every classic queue on 4 and none on 3.13.

Drift detection here does not compare argument maps. It re-declares the queue with the arguments the plan asks for and reads the broker's 406 PRECONDITION_FAILED reply code, so the broker itself decides equivalence and the answer is the same on both versions. Re-declaring a classic queue with an explicit x-queue-type: classic against one originally declared without it is equivalent on both — confirmed by running it.

The trap is real for anything that does read /api/queues and compare arguments; acemq-java-rabbitmq-admin documents it for that reason. It does not apply to the message path.

Global QoS is denied in 4, and this library never asked for it

3.13.7 4.3.6
global_qos deprecation phase permitted_by_default denied_by_default

basic.qos with global=true sets one prefetch across a whole channel rather than per consumer. This library calls the single-argument channel.basicQos(prefetch), which is global=false, everywhere it sets a prefetch — on subscribe and on a later prefetch(int) change. Nothing to do.

The stream prefetch refusal reads differently

Both versions refuse a stream consumer with no prefetch, which is what StreamOptions exists to prevent. The message differs:

Text
3.13.7 PRECONDITION_FAILED - consumer prefetch count is not set for 'queue 'x' in vhost '/''
4.3.6 PRECONDITION_FAILED - consumer prefetch count is not set for stream queue 'x' in vhost '/'

This library matches on the numeric reply code, never on the text, so the wording is free to differ. It is listed because anything of your own that string-matches broker errors will break across this upgrade, and that is a common enough shortcut to be worth naming.

Health-check responses differ in shape

Not this library's surface — it exposes no HTTP health check, and protocol-listener belongs to the management API — but it is the difference most likely to break a deployment's readiness probe during an upgrade.

GET /api/health/checks/protocol-listener/{p} 3.13.7 4.3.6
Known protocol 200 {"status":"ok","protocol":"amqp"} 200 {"status":"ok","protocols":["amqp",…]}
Unknown protocol 503, "missing":"nosuchproto" 503, "missing":["nosuchproto"]

Both answer 503 for a missing listener, so a probe that only reads the status code is unaffected. One that reads protocol or treats missing as a string breaks on 4.

What the library does when it meets the older broker

Nothing special, and that is deliberate.

There is no broker-version detection in this library. The RabbitMQ transport reports a fixed capability set and never queries the server's version to decide what to offer. The consequences are worth being explicit about:

The one asymmetry worth planning around is not a library behaviour at all: a 3.13 broker will accept things a 4.x broker refuses, so a topology that works on 3.13 is not automatically one that works on 4. Transient queues and ha-mode policies are the two that matter, and neither comes from here.

Minimum versions by feature

Feature Needs
Everything on the message path RabbitMQ 3.13
Quorum queues 3.8
Streams, stream offsets and retention 3.9
Stream segment size 3.9
Single Active Consumer on a stream 3.11

3.13 is the floor this library tests and supports, so the older numbers are context for an estate mid-upgrade rather than a promise that 3.8 is supported.

Keeping this honest

A matrix nothing re-checks goes stale exactly the way the claim it replaced did. The compatibility job in .github/workflows/ci.yml runs the integration suite against rabbitmq:3.13-management on every pull request, separately from the JDK build matrix — the JDK matrix is about compiling and the broker version is about behaviour, and multiplying them would triple the container time to re-prove what one run already shows.

If that job goes red, this page is wrong and the change that made it red is the one to look at.