Skip to content

Commit 7a775f7

Browse files
Merge pull request #16997 from rabbitmq/rabbitmq-server-16991
Safer federation upstream deletion
2 parents e1ae854 + 9522020 commit 7a775f7

2 files changed

Lines changed: 54 additions & 4 deletions

File tree

deps/rabbitmq_federation_common/src/rabbit_federation_link_sup.erl

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,16 @@ adjust(Sup, LinkMod, XorQ, {upstream, UpstreamName}) ->
5252
_ = [stop(Sup, OldUpstream, XorQ) || OldUpstream <- OldUpstreams],
5353
[start(Sup, LinkMod, NewUpstream, XorQ) || NewUpstream <- NewUpstreams];
5454

55-
adjust(Sup, _LinkMod, XorQ, {clear_upstream, UpstreamName}) ->
55+
adjust(Sup, _LinkMod, X = #exchange{}, {clear_upstream, UpstreamName}) ->
56+
%% The federation scratch only exists for exchanges. Pruning it for a
57+
%% queue passes the queue name to `update_scratch/3`, which resolves it to
58+
%% a same-named exchange (the Khepri path ignores the resource kind) and
59+
%% overwrites that exchange's decorators. See rabbitmq/rabbitmq-server#16991.
5660
ok = rabbit_federation_db:prune_scratch(
57-
name(XorQ), rabbit_federation_upstream:for(XorQ)),
58-
[stop(Sup, Upstream, XorQ) || Upstream <- children(Sup, UpstreamName)];
61+
name(X), rabbit_federation_upstream:for(X)),
62+
[stop(Sup, Upstream, X) || Upstream <- children(Sup, UpstreamName)];
63+
adjust(Sup, _LinkMod, Q, {clear_upstream, UpstreamName}) when ?is_amqqueue(Q) ->
64+
[stop(Sup, Upstream, Q) || Upstream <- children(Sup, UpstreamName)];
5965

6066
adjust(Sup, LinkMod, X = #exchange{name = XName}, {upstream_set, _Set}) ->
6167
_ = adjust(Sup, LinkMod, X, everything),

deps/rabbitmq_queue_federation/test/queue_SUITE.erl

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ federation_mechanics_tests() ->
6060
dynamic_reconfiguration,
6161
federate_unfederate,
6262
dynamic_plugin_stop_start,
63-
supervisor_shutdown_concurrency_safety
63+
supervisor_shutdown_concurrency_safety,
64+
clear_upstream_leaves_same_named_exchange_intact
6465
]}
6566
]}
6667
].
@@ -399,6 +400,49 @@ supervisor_shutdown_concurrency_safety(Config) ->
399400

400401
expect_federation(Ch, UpQ1, DownQ1, ?EXPECT_FEDERATION_TIMEOUT)
401402
end, upstream_downstream(Config) ++ [q(DownQ2, Args)]).
403+
404+
%% Clearing a queue-federation upstream must leave a same-named exchange
405+
%% untouched. See rabbitmq/rabbitmq-server#16991.
406+
clear_upstream_leaves_same_named_exchange_intact(Config) ->
407+
%% The federated queue and a fanout exchange share {vhost, name}.
408+
Name = <<"fed1.downstream">>,
409+
XName = rabbit_misc:r(<<"/">>, exchange, Name),
410+
with_ch(Config,
411+
fun (Ch) ->
412+
#'exchange.declare_ok'{} =
413+
amqp_channel:call(Ch, #'exchange.declare'{exchange = Name,
414+
type = <<"fanout">>,
415+
durable = true}),
416+
#'queue.bind_ok'{} =
417+
amqp_channel:call(Ch, #'queue.bind'{queue = Name,
418+
exchange = Name}),
419+
420+
await_running_federation(Config,
421+
[{Name, <<"upstream">>}],
422+
?EXPECT_FEDERATION_TIMEOUT),
423+
expect_federation(Ch, <<"upstream">>, Name, ?EXPECT_FEDERATION_TIMEOUT),
424+
425+
%% Before clearing, the decorators are a valid {Route, NoRoute} tuple.
426+
{_, _} = exchange_decorators(Config, XName),
427+
428+
clear_upstream(Config, 0, <<"localhost">>),
429+
430+
%% After clearing they must still be a tuple, not a bare [].
431+
{_, _} = exchange_decorators(Config, XName),
432+
433+
%% Publishing to the exchange still routes to the queue rather
434+
%% than crashing the channel with a decorator function_clause.
435+
Payload = <<"after-clear">>,
436+
publish(Ch, Name, <<>>, Payload),
437+
expect(Ch, Name, [Payload])
438+
end, upstream_downstream(Config)).
439+
440+
%% #exchange.decorators
441+
exchange_decorators(Config, XName) ->
442+
{ok, X} = rabbit_ct_broker_helpers:rpc(Config, 0,
443+
rabbit_exchange, lookup, [XName]),
444+
element(11, X).
445+
402446
restart_upstream(Config) ->
403447
[Rabbit, Hare] = rabbit_ct_broker_helpers:get_node_configs(Config,
404448
nodename),

0 commit comments

Comments
 (0)