Skip to content

Make retry/delay queue durable for RabbitMQ 4.3 compatibility#115

Open
Cryde wants to merge 1 commit into
jwage:mainfrom
Cryde:fix/durable-delay-queue
Open

Make retry/delay queue durable for RabbitMQ 4.3 compatibility#115
Cryde wants to merge 1 commit into
jwage:mainfrom
Cryde:fix/durable-delay-queue

Conversation

@Cryde

@Cryde Cryde commented Apr 30, 2026

Copy link
Copy Markdown

RabbitMQ 4.3 changes the default of the transient_nonexcl_queues deprecated feature flag from permitted_by_default to denied_by_default. This flag covers queues declared with both durable=false and exclusive=false.

Connection::setupDelayQueue() calls queue_declare() passing only queue, nowait and arguments. The boolean flags therefore use php-amqplib's defaults (durable=false, exclusive=false, auto_delete=true), which matches the deprecated case. On RabbitMQ 4.3, the declaration fails with:

INTERNAL_ERROR - Feature `transient_nonexcl_queues` is deprecated.

Setting durable=true fixes it. The queue is still auto-cleaned via the existing x-expires argument, and making it durable is arguably better anyway: retry messages currently in flight survive a broker restart during the delay, whereas before the queue and its messages would be lost.

Also adds a unit test that the delay queue is declared with durable=true.

RabbitMQ 4.3 flips the `transient_nonexcl_queues` deprecated feature flag
from `permitted_by_default` to `denied_by_default`. The combo means
declaring a queue with `durable=false` AND `exclusive=false`.

Connection::setupDelayQueue() previously called `queue_declare()` with
only `queue`, `nowait`, and `arguments`, so the boolean flags fell back
to php-amqplib's defaults — `durable=false`, `exclusive=false`,
`auto_delete=true` — which is exactly the deprecated combination.
RabbitMQ 4.3 then rejects the declaration with:

    INTERNAL_ERROR - Feature `transient_nonexcl_queues` is deprecated.

Declaring the queue as durable resolves it. The queue still auto-cleans
via the existing `x-expires` argument, and durability is the more
correct behavior anyway: it preserves in-flight retry messages across
broker restarts mid-delay (with the previous transient declaration the
queue and its messages were lost).

Adds a unit test asserting the delay queue is declared with durable=true.
@jwage

jwage commented Apr 30, 2026

Copy link
Copy Markdown
Owner

Is this backwards compatible? Were queues durable before or does this need to be a setting that defaults to false so it is backwards compatible with existing setups using this library?

@Cryde

Cryde commented May 10, 2026

Copy link
Copy Markdown
Author

Fair point, you're right, it's not fully backwards-compatible, and I should've called that out in the PR description.

The issue: if an existing setup already has a transient delay/retry queue declared, AMQP will reject the new durable declaration with PRECONDITION_FAILED and close the channel. So users with in-flight retry messages at upgrade time will hit errors until the old queues clear.

I didn't add a durable: false opt-in because RabbitMQ 4.3+ refuses the old declaration outright, so defaulting to the previous behavior would ship the lib broken on current Rabbit.

Happy to add an upgrade note in the README/CHANGELOG to make this explicit. I could also expose delay.queue.durable as a config option if you'd prefer an escape hatch

WDYT?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants