Skip to content

Commit a8aa910

Browse files
authored
Merge pull request #4400 from esl/fix-event_not_registered-on-shutdown
Fix event not registered on shutdown
2 parents 2bde4e7 + 87f7bf4 commit a8aa910

17 files changed

+301
-70
lines changed

big_tests/default.spec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@
121121
{suites, "tests", cets_disco_SUITE}.
122122
{suites, "tests", start_node_id_SUITE}.
123123
{suites, "tests", tr_util_SUITE}.
124+
{suites, "tests", shutdown_SUITE}.
124125

125126
{config, ["test.config"]}.
126127
{logdir, "ct_report"}.

big_tests/dynamic_domains.spec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@
163163
{suites, "tests", cets_disco_SUITE}.
164164
{suites, "tests", start_node_id_SUITE}.
165165
{suites, "tests", tr_util_SUITE}.
166+
{suites, "tests", shutdown_SUITE}.
166167

167168
{config, ["dynamic_domains.config", "test.config"]}.
168169

big_tests/test.config

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,13 @@
198198
{server, <<"localhost">>},
199199
{password, <<"password">>},
200200
{port, 5252}]},
201+
{adam, [ %% used in mod_global_distrib_SUITE, websockets
202+
{username, <<"adam">>},
203+
{server, <<"localhost">>},
204+
{password, <<"password">>},
205+
{transport, escalus_ws},
206+
{port, 5272},
207+
{wspath, <<"/ws-xmpp">>}]},
201208
{neustradamus, [
202209
{username, <<"neustradamus">>},
203210
{server, <<"localhost">>},
@@ -348,6 +355,7 @@
348355
scope = \"global\"
349356
workers = 10
350357
strategy = \"random_worker\"
358+
connection.database = {{redis_database_number}}
351359
[outgoing_pools.rdbms.default]
352360
scope = \"global\"
353361
workers = 5

big_tests/tests/mod_global_distrib_SUITE.erl

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -688,19 +688,26 @@ test_component_disconnect(Config) ->
688688
test_location_disconnect(Config) ->
689689
try
690690
escalus:fresh_story(
691-
Config, [{alice, 1}, {eve, 1}],
692-
fun(Alice, Eve) ->
691+
Config, [{alice, 1}, {eve, 1}, {adam, 1}],
692+
fun(Alice, Eve, Adam) ->
693693
escalus_client:send(Alice, escalus_stanza:chat_to(Eve, <<"Hi from Europe1!">>)),
694-
695694
escalus_client:wait_for_stanza(Eve),
696695

696+
escalus_client:send(Alice, escalus_stanza:chat_to(Adam, <<"Hi, Adam, from Europe1!">>)),
697+
escalus_client:wait_for_stanza(Adam),
698+
699+
print_sessions_debug_info(asia_node),
697700
ok = rpc(asia_node, application, stop, [mongooseim]),
698701
%% TODO: Stopping mongooseim alone should probably stop connections too
699702
ok = rpc(asia_node, application, stop, [ranch]),
700703

701704
escalus_client:send(Alice, escalus_stanza:chat_to(Eve, <<"Hi again!">>)),
702705
Error = escalus:wait_for_stanza(Alice),
703-
escalus:assert(is_error, [<<"cancel">>, <<"service-unavailable">>], Error)
706+
escalus:assert(is_error, [<<"cancel">>, <<"service-unavailable">>], Error),
707+
708+
escalus_client:send(Alice, escalus_stanza:chat_to(Adam, <<"Hi, Adam, again!">>)),
709+
Error2 = escalus:wait_for_stanza(Alice),
710+
escalus:assert(is_error, [<<"cancel">>, <<"service-unavailable">>], Error2)
704711
end)
705712
after
706713
rpc(asia_node, application, start, [ranch]),
@@ -1410,6 +1417,27 @@ can_connect_to_port(Port) ->
14101417
false
14111418
end.
14121419

1420+
%% Prints information about the active sessions
1421+
print_sessions_debug_info(NodeName) ->
1422+
Node = rpc(NodeName, erlang, node, []),
1423+
Nodes = rpc(NodeName, erlang, nodes, []),
1424+
ct:log("name=~p, erlang_node=~p, other_nodes=~p", [NodeName, Node, Nodes]),
1425+
1426+
Children = rpc(NodeName, supervisor, which_children, [mongoose_c2s_sup]),
1427+
ct:log("C2S processes under a supervisour ~p", [Children]),
1428+
1429+
Sessions = rpc(NodeName, ejabberd_sm, get_full_session_list, []),
1430+
ct:log("C2S processes in the session manager ~p", [Sessions]),
1431+
1432+
Sids = [element(2, Session) || Session <- Sessions],
1433+
Pids = [Pid || {_, Pid} <- Sids],
1434+
PidNodes = [{Pid, node(Pid)} || Pid <- Pids],
1435+
ct:log("Pids on nodes ~p", [PidNodes]),
1436+
1437+
Info = [{Pid, rpc:call(Node, erlang, process_info, [Pid])} || {Pid, Node} <- PidNodes],
1438+
ct:log("Processes info ~p", [Info]),
1439+
ok.
1440+
14131441
%% -----------------------------------------------------------------------
14141442
%% Custom log levels for GD modules during the tests
14151443

@@ -1433,10 +1461,14 @@ custom_loglevels() ->
14331461
{mod_global_distrib_connection, debug},
14341462
%% to check if gc or refresh is triggered
14351463
{mod_global_distrib_server_mgr, info},
1436-
%% To debug incoming connections
1464+
%% To debug incoming connections
14371465
% {mod_global_distrib_receiver, info},
1438-
%% to debug global session set/delete
1439-
{mod_global_distrib_mapping, debug}
1466+
%% to debug global session set/delete
1467+
{mod_global_distrib_mapping, debug},
1468+
%% To log make_error_reply calls
1469+
{jlib, debug},
1470+
%% to log sm_route
1471+
{ejabberd_sm, debug}
14401472
].
14411473

14421474
test_hosts() -> [mim, mim2, reg].

big_tests/tests/shutdown_SUITE.erl

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
-module(shutdown_SUITE).
2+
3+
-compile([export_all, nowarn_export_all]).
4+
-import(distributed_helper, [mim/0, rpc/4]).
5+
6+
-include_lib("common_test/include/ct.hrl").
7+
-include_lib("eunit/include/eunit.hrl").
8+
-include_lib("exml/include/exml.hrl").
9+
10+
all() ->
11+
[{group, main}].
12+
13+
groups() ->
14+
[{main, [], cases()}].
15+
16+
cases() ->
17+
[shutdown,
18+
client_tries_to_connect_before_listener_stop].
19+
20+
init_per_suite(Config) ->
21+
mongoose_helper:inject_module(?MODULE),
22+
escalus:init_per_suite(Config).
23+
24+
end_per_suite(Config) ->
25+
escalus:end_per_suite(Config).
26+
27+
init_per_group(_, Config) ->
28+
escalus:create_users(Config, escalus:get_users([geralt_s, alice])),
29+
Config.
30+
31+
end_per_group(_, Config) ->
32+
escalus:delete_users(Config, escalus:get_users([geralt_s, alice])),
33+
Config.
34+
35+
init_per_testcase(shutdown = TC, Config) ->
36+
logger_ct_backend:start(),
37+
escalus:init_per_testcase(TC, Config);
38+
init_per_testcase(client_tries_to_connect_before_listener_stop = TC, Config) ->
39+
escalus:init_per_testcase(TC, Config);
40+
init_per_testcase(Name, Config) ->
41+
escalus:init_per_testcase(Name, Config).
42+
43+
end_per_testcase(shutdown = TC, Config) ->
44+
logger_ct_backend:stop(),
45+
escalus:end_per_testcase(TC, Config);
46+
end_per_testcase(client_tries_to_connect_before_listener_stop = TC, Config) ->
47+
rpc(mim(), meck, unload, []),
48+
escalus:end_per_testcase(TC, Config);
49+
end_per_testcase(Name, Config) ->
50+
escalus:end_per_testcase(Name, Config).
51+
52+
shutdown(Config) ->
53+
UserSpec = escalus_users:get_userspec(Config, geralt_s),
54+
{ok, Alice, _} = escalus_connection:start(UserSpec),
55+
logger_ct_backend:capture(error),
56+
ejabberd_node_utils:restart_application(mongooseim),
57+
logger_ct_backend:stop_capture(),
58+
FilterFun = fun(_, Msg) -> re:run(Msg, "event_not_registered") /= nomatch end,
59+
[] = logger_ct_backend:recv(FilterFun),
60+
%% Ensure that Alice gets a shutdown stanza
61+
escalus:assert(is_stream_error, [<<"system-shutdown">>, <<>>],
62+
escalus_client:wait_for_stanza(Alice)),
63+
escalus:assert(is_stream_end, escalus_client:wait_for_stanza(Alice)),
64+
true = escalus_connection:wait_for_close(Alice, timer:seconds(1)).
65+
66+
client_tries_to_connect_before_listener_stop(Config) ->
67+
%% Ensures that user would not be able to connect while we are stopping
68+
%% the listeners and other c2s processes
69+
UserSpec = escalus_users:get_userspec(Config, geralt_s),
70+
PortNum = proplists:get_value(port, UserSpec),
71+
%% Ask MongooseIM to pause the stopping process
72+
%% so we can check that listeners were suspended correctly
73+
block_listener(),
74+
%% Check that the listener is working
75+
{ok, ConnPort} = gen_tcp:connect("127.0.0.1", PortNum, []),
76+
%% Trigger the restarting logic in a separate parallel process
77+
RestPid = restart_application_non_blocking(),
78+
%% Wait until we are blocked in mongoose_listener:stop/0
79+
Called = wait_for_called(),
80+
{error, econnrefused} = gen_tcp:connect("127.0.0.1", PortNum, []),
81+
%% Resume to stop the listeners
82+
resume(Called),
83+
%% Check that the old TCP connections are closed
84+
receive_tcp_closed(ConnPort),
85+
%% Wait till mongooseim is fully restarted
86+
wait_for_down(RestPid).
87+
88+
block_listener() ->
89+
rpc(mim(), meck, new, [mongoose_listener, [no_link, passthrough]]),
90+
Pid = self(),
91+
F = fun() ->
92+
wait_for_resume(Pid),
93+
meck:passthrough([])
94+
end,
95+
rpc(mim(), meck, expect, [mongoose_listener, stop, F]).
96+
97+
restart_application_non_blocking() ->
98+
spawn_link(fun() -> ejabberd_node_utils:restart_application(mongooseim) end).
99+
100+
wait_for_called() ->
101+
receive
102+
{called, Called} ->
103+
Called
104+
after 5000 ->
105+
error(wait_for_called_timeout)
106+
end.
107+
108+
%% Blocks the mocked process until resume/1 is called
109+
wait_for_resume(Pid) ->
110+
MonRef = erlang:monitor(process, Pid),
111+
Called = {self(), MonRef},
112+
Pid ! {called, Called},
113+
receive
114+
{'DOWN', MonRef, process, _, _} -> ok;
115+
{resume, MonRef} -> ok
116+
end.
117+
118+
%% Command to the mocked process to resume the operation
119+
resume({Pid, MonRef}) ->
120+
Pid ! {resume, MonRef}.
121+
122+
wait_for_down(Pid) ->
123+
MonRef = erlang:monitor(process, Pid),
124+
receive
125+
{'DOWN', MonRef, process, _, _} -> ok
126+
after 5000 ->
127+
ct:pal("wait_for_down current_stacktrace ~p",
128+
[rpc:pinfo(Pid, current_stacktrace)]),
129+
ct:fail(wait_for_down_timeout)
130+
end.
131+
132+
%% Waits until the TCP socket is closed
133+
receive_tcp_closed(ConnPort) ->
134+
receive
135+
{tcp_closed, ConnPort} ->
136+
ok
137+
after 5000 ->
138+
ct:fail(wait_for_tcp_close_timeout)
139+
end.

include/safely.hrl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
-define(SAFELY(F),
55
try F catch
66
error:R:S -> {exception, #{class => error, reason => R, stacktrace => S}};
7-
throw:R -> {exception, #{class => throw, reason => R}};
7+
throw:R:S -> {exception, #{class => throw, reason => R, stacktrace => S}};
88
exit:R:S -> {exception, #{class => exit, reason => R, stacktrace => S}}
99
end).
1010

rel/fed1.vars-toml.config

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
{hosts, "\"fed1\", \"fed2\""}.
2020
{default_server_domain, "\"fed1\""}.
2121
{cluster_name, "fed"}.
22+
{redis_database_number, "2"}.
2223

2324
%% domain.example.com is for multitenancy preset, muc_SUITE:register_over_s2s
2425
{s2s_addr, "[[s2s.address]]

rel/mim1.vars-toml.config

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
{host_types, "\"test type\", \"dummy auth\", \"anonymous\""}.
2323
{default_server_domain, "\"localhost\""}.
2424
{cluster_name, "mim"}.
25+
{redis_database_number, "0"}.
2526

2627
{mod_amp, ""}.
2728
{host_config,

rel/mim2.vars-toml.config

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
{host_types, "\"test type\", \"dummy auth\""}.
2121
{default_server_domain, "\"localhost\""}.
2222
{cluster_name, "mim"}.
23+
{redis_database_number, "0"}.
2324
{s2s_addr, "[[s2s.address]]
2425
host = \"localhost2\"
2526
ip_address = \"127.0.0.1\""}.

rel/mim3.vars-toml.config

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
{hosts, "\"localhost\", \"anonymous.localhost\", \"localhost.bis\""}.
2424
{default_server_domain, "\"localhost\""}.
2525
{cluster_name, "mim"}.
26+
{redis_database_number, "0"}.
2627

2728
{s2s_addr, "[[s2s.address]]
2829
host = \"localhost2\"

0 commit comments

Comments
 (0)