dapr / dapr/components-contrib

RabbitMQ pub/sub: option to use an externally managed exchange, queue and binding instead of declaring them

Open
#4,553 0 comments 0 reactions 1 assignee Claimed by @MyMirelHub View on GitHub
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.