dapr / dapr/components-contrib
RabbitMQ pub/sub: option to use an externally managed exchange, queue and binding instead of declaring them
- Dominant language
- Go
- Stars
- 602
- Forks
- 580
- Avg merge
- 4d 9h
- Merged PRs (30d)
- 6
Description
## Describe the feature
The RabbitMQ pub/sub component always issues an **active** declare for the topology objects it uses: `exchange.declare` for the topic (exchange name = topic name) and `queue.declare` + `queue.bind` for each subscription. There is no way to tell it that the topology already exists and is owned by something else.
That makes the component incompatible with a broker whose topology is managed declaratively — for example the [RabbitMQ Cluster Kubernetes Operator's Topology Operator](https://www.rabbitmq.com/kubernetes/operator/using-topology-operator#exchanges-bindings), which owns `Exchange`, `Queue` and `Binding` custom resources, but equally Terraform, `rabbitmqadmin`, or any other provisioning step.
### Why it breaks
AMQP declaration is idempotent only when *every* property matches. Where it doesn't, RabbitMQ answers `PRECONDITION_FAILED (406)` and closes the channel. The defaults on each side differ on every property:
| | exchange kind | durable | autoDelete |
|---|---|---|---|
| Topology Operator `Exchange` CR defaults | `direct` | `false` | `false` |
| What the component declares | `fanout` | `true` | `true` |
So out of the box the two collide. Coexistence is possible today only by hand-aligning each CR to the component's exact declaration — name, kind, `durable`, `autoDelete`, arguments — which inverts the point of managing topology declaratively, and has to be redone whenever component defaults change.
For an exchange kind the component cannot declare at all (see #3423 and the linked issue for `x-consistent-hash`), there is no alignment that works: the declare can never match, so the component fails permanently on that topic.
### Proposed feature
A passive declare mode, so the component asserts that a topology object exists rather than creating it:
```yaml
spec:
type: pubsub.rabbitmq
version: v1
metadata:
- name: exchangeDeclareMode
value: "passive" # declare (default, today's behaviour) | passive
- name: queueDeclareMode
value: "passive" # declare (default, today's behaviour) | passive
```
- `exchangeDeclareMode: passive` — assert the topic exchange (and the dead-letter exchange, when `enableDeadLetter` is set) exists; never create or modify it.
- `queueDeclareMode: passive` — assert the consumer queue (and dead-letter queue) exists, and leave `queue.bind` to the external owner, since a binding belongs to whoever owns the queue.
Two independent knobs rather than one switch, because the useful middle case is real: an operator-owned exchange paired with queues the component still declares, since consumer queue names are only known at runtime.
`amqp091-go` already exposes `ExchangeDeclarePassive` and `QueueDeclarePassive`, so this is additive. Both default to `declare`, leaving existing behaviour untouched.
Worth pairing with clearer errors: today a `406` surfaces as a raw AMQP exception with no indication that a topology conflict is what happened, or what to do about it.
## Release Note
RELEASE NOTE: **ADD** `exchangeDeclareMode` and `queueDeclareMode` to the RabbitMQ pub/sub component, for brokers whose topology is managed externally (for example by the RabbitMQ Topology Operator).
Contributor guide
Assessment
This issue has not been assessed yet.