# UNDF: UNDF-2026-000000473
# rmq-0005: rabbit_mgmt_wm_definitions export_binding O(B×Q) → O(B+Q)

## Location
`deps/rabbitmq_management/src/rabbit_mgmt_wm_definitions.erl`
Lines 54–58 (`all_definitions/2`) and 115–120 (`vhost_definitions/2`)

## Severity
MEDIUM — triggered by `GET /api/definitions` (HTTP API export). Automation tools,
monitoring pipelines, and GitOps workflows hit this endpoint on a schedule. In large
deployments it serialises the entire broker definition and becomes a CPU hotspot.

## Description
`all_definitions/2` builds `QNames` as a plain list of `{Name, VHost}` tuples, then
calls `export_binding(B, QNames)` for every binding `B` in a list comprehension.
Inside `export_binding/2`, `lists:member({Dest, VHost}, QNames)` performs a linear
scan of all queue-name tuples for every binding processed.

With B bindings and Q queues this is **O(B × Q)**. In a busy broker (e.g., a topic
exchange with 10 k routing keys matched against 1 k queues) the export scans 10 M
tuple comparisons per API call. If called every 15 seconds by a monitoring tool, this
is 666 k redundant comparisons/second sustained.

## Root Cause

```erlang
%% all_definitions/2 — deps/rabbitmq_management/src/rabbit_mgmt_wm_definitions.erl
Qs     = [Q || Q <- rabbit_mgmt_wm_queues:basic(ReqData), export_queue(Q)],
QNames = [{pget(name, Q), pget(vhost, Q)} || Q <- Qs],     %% ← plain list
Bs     = [B || B <- rabbit_mgmt_wm_bindings:basic(ReqData),
               export_binding(B, QNames)],                  %% ← O(B) calls

%% export_binding/2 — same file
export_binding(Binding, Qs) ->
    ...
    ( (DestType =:= queue andalso lists:member({Dest, VHost}, Qs))  %% ← O(Q) scan
    ...
```

`vhost_definitions/2` has the identical pattern at lines 115–120 using `QNames`.

## Fix
Convert `QNames` to an `ordsets:ordset()` (or `sets:set()` with `{version, 2}`) before
the binding comprehension. Pass the set to `export_binding/2` and use
`sets:is_element/2` in place of `lists:member/2`.

```erlang
%% all_definitions/2 — fixed
Qs      = [Q || Q <- rabbit_mgmt_wm_queues:basic(ReqData), export_queue(Q)],
QNames  = [{pget(name, Q), pget(vhost, Q)} || Q <- Qs],
QNamesS = sets:from_list(QNames, [{version, 2}]),          %% ← O(Q log Q) once
Bs      = [B || B <- rabbit_mgmt_wm_bindings:basic(ReqData),
               export_binding(B, QNamesS)],                 %% ← O(B) calls

%% export_binding/2 — fixed
export_binding(Binding, Qs) ->
    ...
    ( (DestType =:= queue andalso sets:is_element({Dest, VHost}, Qs))  %% ← O(1)
    ...
```

The same change applies to `vhost_definitions/2`.

## Complexity

| | Before | After |
|---|---|---|
| Build QNames | O(Q) | O(Q) + O(Q log Q) set build |
| Per-binding check | O(Q) scan | O(1) set lookup |
| Total export | O(B × Q) | O(Q log Q + B) |

At B=10 k bindings, Q=1 k queues: 10 M ops → 14 k ops (714x improvement).
At B=100 k bindings, Q=10 k queues: 10^9 ops → 133 k ops (7500x improvement).

## Context
`GET /api/definitions` is the standard RabbitMQ backup/export endpoint. It is called:
- By `rabbitmqctl export_definitions`
- By monitoring pipelines (Datadog, Prometheus exporters)
- By GitOps automation to detect config drift

Slow exports block the HTTP listener and increase memory pressure from large response
bodies held in flight while CPU-bound scan is underway.
