12
12
-include_lib (" amqp_client/include/amqp_client.hrl" ).
13
13
-compile ([nowarn_export_all , export_all ]).
14
14
15
+ % % The reconciler has two modes of triggering itself
16
+ % % - timer based
17
+ % % - event based
18
+ % % The default config of this test has Interval very short - 5 second which is lower than
19
+ % % wait_until timeout. Meaninig that even if all domain triggers (node_up/down, policy_set, etc)
20
+ % % are disconnected tests would be still green.
21
+ % % So to test triggers it is essential to set Interval high enough (the very default value of 60 minutes is perfect)
22
+ % %
23
+ % % TODO: test `policy_set` trigger
15
24
16
25
all () ->
17
26
[
18
- {group , unclustered }
27
+ {group , unclustered },
28
+ {group , unclustered_triggers }
19
29
].
20
30
21
31
groups () ->
22
32
[
23
- {unclustered , [],
33
+ {unclustered , [], % % low interval, even if triggers do not work all tests should pass
24
34
[
25
35
{quorum_queue_3 , [], [auto_grow , auto_grow_drained_node , auto_shrink ]}
36
+ ]},
37
+ % % uses an interval longer than `wait_until` (30s by default)
38
+ {unclustered_triggers , [],
39
+ [
40
+ % % see also `auto_grow_drained_node`
41
+ {quorum_queue_3 , [], [auto_grow , auto_shrink ]}
26
42
]}
27
43
].
28
44
29
45
% % -------------------------------------------------------------------
30
46
% % Testsuite setup/teardown.
31
47
% % -------------------------------------------------------------------
32
48
33
- init_per_suite (Config0 ) ->
49
+ init_per_suite (Config ) ->
34
50
rabbit_ct_helpers :log_environment (),
51
+ rabbit_ct_helpers :run_setup_steps (Config , []).
52
+
53
+ end_per_suite (Config ) ->
54
+ rabbit_ct_helpers :run_teardown_steps (Config ).
55
+
56
+ init_per_group (unclustered , Config0 ) ->
35
57
Config1 = rabbit_ct_helpers :merge_app_env (
36
58
Config0 , {rabbit , [{quorum_tick_interval , 1000 },
37
59
{quorum_membership_reconciliation_enabled , true },
38
60
{quorum_membership_reconciliation_auto_remove , true },
39
61
{quorum_membership_reconciliation_interval , 5000 },
40
62
{quorum_membership_reconciliation_trigger_interval , 2000 },
41
63
{quorum_membership_reconciliation_target_group_size , 3 }]}),
42
- rabbit_ct_helpers :run_setup_steps (Config1 , []).
43
-
44
- end_per_suite (Config ) ->
45
- rabbit_ct_helpers :run_teardown_steps (Config ).
46
- init_per_group (unclustered , Config ) ->
47
- rabbit_ct_helpers :set_config (Config , [{rmq_nodes_clustered , false }]);
64
+ rabbit_ct_helpers :set_config (Config1 , [{rmq_nodes_clustered , false }]);
65
+ init_per_group (unclustered_triggers , Config0 ) ->
66
+ Config1 = rabbit_ct_helpers :merge_app_env (
67
+ Config0 , {rabbit , [{quorum_tick_interval , 1000 },
68
+ {quorum_membership_reconciliation_enabled , true },
69
+ {quorum_membership_reconciliation_auto_remove , true },
70
+ {quorum_membership_reconciliation_interval , 50000 },
71
+ {quorum_membership_reconciliation_trigger_interval , 2000 },
72
+ {quorum_membership_reconciliation_target_group_size , 3 }]}),
73
+ % % shrink timeout is set here because without it, when a node stopped right after a queue was created,
74
+ % % the test will pass without any triggers because cluster change will likely happen before the trigger_interval,
75
+ % % scheduled in response to queue_created event.
76
+ % % See also a comment in `auto_shrink/1`.
77
+ rabbit_ct_helpers :set_config (Config1 , [{rmq_nodes_clustered , false },
78
+ {quorum_membership_reconciliation_interval , 50000 },
79
+ {shrink_timeout , 2000 }]);
48
80
init_per_group (Group , Config ) ->
49
81
ClusterSize = 3 ,
50
82
Config1 = rabbit_ct_helpers :set_config (Config ,
@@ -57,6 +89,8 @@ init_per_group(Group, Config) ->
57
89
58
90
end_per_group (unclustered , Config ) ->
59
91
Config ;
92
+ end_per_group (unclustered_triggers , Config ) ->
93
+ Config ;
60
94
end_per_group (_ , Config ) ->
61
95
rabbit_ct_helpers :run_steps (Config ,
62
96
rabbit_ct_broker_helpers :teardown_steps ()).
@@ -72,34 +106,17 @@ init_per_testcase(Testcase, Config) ->
72
106
]),
73
107
rabbit_ct_helpers :run_steps (Config2 , rabbit_ct_client_helpers :setup_steps ()).
74
108
75
- merge_app_env (Config ) ->
76
- rabbit_ct_helpers :merge_app_env (
77
- rabbit_ct_helpers :merge_app_env (Config ,
78
- {rabbit , [{core_metrics_gc_interval , 100 }]}),
79
- {ra , [{min_wal_roll_over_interval , 30000 }]}).
80
-
81
109
end_per_testcase (Testcase , Config ) ->
82
110
[Server0 , Server1 , Server2 ] =
83
111
rabbit_ct_broker_helpers :get_node_configs (Config , nodename ),
112
+ Ch = rabbit_ct_client_helpers :open_channel (Config , Server1 ),
113
+ amqp_channel :call (Ch , # 'queue.delete' {queue = rabbit_data_coercion :to_binary (Testcase )}),
84
114
reset_nodes ([Server2 , Server0 ], Server1 ),
85
115
Config1 = rabbit_ct_helpers :run_steps (
86
116
Config ,
87
117
rabbit_ct_client_helpers :teardown_steps ()),
88
118
rabbit_ct_helpers :testcase_finished (Config1 , Testcase ).
89
119
90
- reset_nodes ([], _Leader ) ->
91
- ok ;
92
- reset_nodes ([Node | Nodes ], Leader ) ->
93
- ok = rabbit_control_helper :command (stop_app , Node ),
94
- case rabbit_control_helper :command (forget_cluster_node , Leader , [atom_to_list (Node )]) of
95
- ok -> ok ;
96
- {error , _ , <<" Error:\n {:not_a_cluster_node, ~c \" The node selected is not in the cluster.\" }" >>} -> ok
97
- end ,
98
- ok = rabbit_control_helper :command (reset , Node ),
99
- ok = rabbit_control_helper :command (start_app , Node ),
100
- reset_nodes (Nodes , Leader ).
101
-
102
-
103
120
% % -------------------------------------------------------------------
104
121
% % Testcases.
105
122
% % -------------------------------------------------------------------
@@ -134,6 +151,10 @@ auto_grow(Config) ->
134
151
end ).
135
152
136
153
auto_grow_drained_node (Config ) ->
154
+ % % NOTE: with large Interval (larger than wait_until) test will fail.
155
+ % % the reason is that entering/exiting drain state does not emit events
156
+ % % and even if they did via gen_event, they going to be only local to that node.
157
+ % % so reconciliator has no choice but to wait full Interval
137
158
[Server0 , Server1 , Server2 ] =
138
159
rabbit_ct_broker_helpers :get_node_configs (Config , nodename ),
139
160
Ch = rabbit_ct_client_helpers :open_channel (Config , Server1 ),
@@ -169,7 +190,6 @@ auto_grow_drained_node(Config) ->
169
190
3 =:= length (M )
170
191
end ).
171
192
172
-
173
193
auto_shrink (Config ) ->
174
194
[Server0 , Server1 , Server2 ] =
175
195
rabbit_ct_broker_helpers :get_node_configs (Config , nodename ),
@@ -186,6 +206,18 @@ auto_shrink(Config) ->
186
206
Server1 }),
187
207
3 =:= length (M )
188
208
end ),
209
+
210
+ % % QQ member reconciliation does not act immediately but rather after a scheduled delay.
211
+ % % So if this test wants to test that the reconciliator reacts to, say, node_down or a similar event,
212
+ % % it has to wait at least a trigger_interval ms to pass before removing node. Otherwise
213
+ % % the shrink effect would come from the previous trigger.
214
+ % %
215
+ % % When a `queue_created` trigger set up a timer to fire after a trigger_interval, the queue has 3 members
216
+ % % and stop_app executes much quicker than the trigger_interval. Therefore the number of members
217
+ % % will be updated even without a node_down event.
218
+
219
+ timer :sleep (rabbit_ct_helpers :get_config (Config , shrink_timeout , 0 )),
220
+
189
221
ok = rabbit_control_helper :command (stop_app , Server2 ),
190
222
ok = rabbit_ct_broker_helpers :rpc (Config , 0 , rabbit_db_cluster , forget_member ,
191
223
[Server2 , false ]),
@@ -196,7 +228,27 @@ auto_shrink(Config) ->
196
228
2 =:= length (M )
197
229
end ).
198
230
231
+ % % -------------------------------------------------------------------
232
+ % % Helpers.
233
+ % % -------------------------------------------------------------------
199
234
235
+ merge_app_env (Config ) ->
236
+ rabbit_ct_helpers :merge_app_env (
237
+ rabbit_ct_helpers :merge_app_env (Config ,
238
+ {rabbit , [{core_metrics_gc_interval , 100 }]}),
239
+ {ra , [{min_wal_roll_over_interval , 30000 }]}).
240
+
241
+ reset_nodes ([], _Leader ) ->
242
+ ok ;
243
+ reset_nodes ([Node | Nodes ], Leader ) ->
244
+ ok = rabbit_control_helper :command (stop_app , Node ),
245
+ case rabbit_control_helper :command (forget_cluster_node , Leader , [atom_to_list (Node )]) of
246
+ ok -> ok ;
247
+ {error , _ , <<" Error:\n {:not_a_cluster_node, ~c \" The node selected is not in the cluster.\" }" >>} -> ok
248
+ end ,
249
+ ok = rabbit_control_helper :command (reset , Node ),
250
+ ok = rabbit_control_helper :command (start_app , Node ),
251
+ reset_nodes (Nodes , Leader ).
200
252
201
253
add_server_to_cluster (Server , Leader ) ->
202
254
ok = rabbit_control_helper :command (stop_app , Server ),
0 commit comments