Skip to content

Commit a2e4cab

Browse files
committed
Add test case for binding args Khepri regression
This commit adds a test case for a regression/bug that occurs in Khepri. ``` make -C deps/rabbit ct-bindings t=cluster:binding_args RABBITMQ_METADATA_STORE=mnesia ``` succeeds, but ``` make -C deps/rabbit ct-bindings t=cluster:binding_args RABBITMQ_METADATA_STORE=khepri ``` fails. The problem is that ETS table `rabbit_khepri_index_route` cannot differentiate between two bindings with different binding arguments, and therefore deletes entries too early, leading to wrong routing decisions. The solution to this bug is to include the binding arguments in the `rabbit_khepri_index_route` projection, similar to how the binding args are also included in the `rabbit_index_route` Mnesia table. This bug/regression is an edge case and exists if the source exchange type is `direct` or `fanout` and if different bindings arguments are used by client apps. Note that such binding arguments are entirely ignored when RabbitMQ performs routing decisions for the `direct` or `fanout` exchange. However, there might be client apps that use binding arguments to add some metadata to the binding, for example `app-id` or `user` or `purpose` and might use this metadata as a form of reference counting in deciding when to delete `auto-delete` exchanges or just for informational/operational purposes.
1 parent b819507 commit a2e4cab

File tree

1 file changed

+51
-0
lines changed

1 file changed

+51
-0
lines changed

deps/rabbit/test/bindings_SUITE.erl

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ groups() ->
3737
all_tests() ->
3838
[
3939
%% Queue bindings
40+
binding_args,
4041
bind_and_unbind,
4142
bind_and_delete,
4243
bind_and_delete_source_exchange,
@@ -116,6 +117,56 @@ end_per_testcase(Testcase, Config) ->
116117
%% -------------------------------------------------------------------
117118
%% Testcases.
118119
%% -------------------------------------------------------------------
120+
121+
binding_args(Config) ->
122+
Server = rabbit_ct_broker_helpers:get_node_config(Config, 0, nodename),
123+
Ch = rabbit_ct_client_helpers:open_channel(Config, Server),
124+
Q = ?config(queue_name, Config),
125+
?assertEqual({'queue.declare_ok', Q, 0, 0}, declare(Ch, Q, [])),
126+
127+
#'confirm.select_ok'{} = amqp_channel:call(Ch, #'confirm.select'{}),
128+
amqp_channel:register_confirm_handler(Ch, self()),
129+
130+
%% Create two bindings that differ only in their binding arguments.
131+
Exchange = <<"amq.direct">>,
132+
RoutingKey = <<"some-key">>,
133+
BindingArgs1 = [{<<"app">>, longstr, <<"app-1">>}],
134+
BindingArgs2 = [{<<"app">>, longstr, <<"app-2">>}],
135+
#'queue.bind_ok'{} = amqp_channel:call(Ch, #'queue.bind'{exchange = Exchange,
136+
routing_key = RoutingKey,
137+
queue = Q,
138+
arguments = BindingArgs1}),
139+
#'queue.bind_ok'{} = amqp_channel:call(Ch, #'queue.bind'{exchange = Exchange,
140+
routing_key = RoutingKey,
141+
queue = Q,
142+
arguments = BindingArgs2}),
143+
ok = amqp_channel:cast(Ch,
144+
#'basic.publish'{exchange = Exchange,
145+
routing_key = RoutingKey},
146+
#amqp_msg{payload = <<"m1">>}),
147+
receive #'basic.ack'{} -> ok
148+
after 9000 -> ct:fail(confirm_timeout)
149+
end,
150+
151+
?assertMatch({#'basic.get_ok'{}, #amqp_msg{payload = <<"m1">>}},
152+
amqp_channel:call(Ch, #'basic.get'{queue = Q, no_ack = true})),
153+
154+
%% If we delete the 1st binding, we expect RabbitMQ to still route via the 2nd binding.
155+
#'queue.unbind_ok'{} = amqp_channel:call(Ch, #'queue.unbind'{exchange = Exchange,
156+
routing_key = RoutingKey,
157+
queue = Q,
158+
arguments = BindingArgs1}),
159+
ok = amqp_channel:cast(Ch,
160+
#'basic.publish'{exchange = Exchange,
161+
routing_key = RoutingKey},
162+
#amqp_msg{payload = <<"m2">>}),
163+
receive #'basic.ack'{} -> ok
164+
after 9000 -> ct:fail(confirm_timeout)
165+
end,
166+
167+
?assertMatch({#'basic.get_ok'{}, #amqp_msg{payload = <<"m2">>}},
168+
amqp_channel:call(Ch, #'basic.get'{queue = Q, no_ack = true})).
169+
119170
bind_and_unbind(Config) ->
120171
Server = rabbit_ct_broker_helpers:get_node_config(Config, 0, nodename),
121172

0 commit comments

Comments
 (0)