1 |
|
-module(mod_global_distrib_outgoing_conns_sup). |
2 |
|
|
3 |
|
-behaviour(supervisor). |
4 |
|
|
5 |
|
-include("mongoose.hrl"). |
6 |
|
|
7 |
|
-export([start_link/0, init/1]). |
8 |
|
-export([add_server/1, get_connection/1, ensure_server_started/1]). |
9 |
|
|
10 |
|
-ignore_xref([add_server/1, start_link/0]). |
11 |
|
|
12 |
|
%%-------------------------------------------------------------------- |
13 |
|
%% API |
14 |
|
%%-------------------------------------------------------------------- |
15 |
|
|
16 |
|
-spec start_link() -> {ok, pid()} | {error, any()}. |
17 |
|
start_link() -> |
18 |
21 |
supervisor:start_link({local, ?MODULE}, ?MODULE, []). |
19 |
|
|
20 |
|
-spec add_server(Server :: jid:lserver()) -> ok | {error, any()}. |
21 |
|
add_server(Server) -> |
22 |
83 |
SupName = mod_global_distrib_utils:server_to_sup_name(Server), |
23 |
83 |
ServerSupSpec = #{ |
24 |
|
id => SupName, |
25 |
|
start => {mod_global_distrib_server_sup, start_link, [Server]}, |
26 |
|
restart => temporary, |
27 |
|
shutdown => 5000, |
28 |
|
type => supervisor, |
29 |
|
modules => dynamic |
30 |
|
}, |
31 |
83 |
case supervisor:start_child(?MODULE, ServerSupSpec) of |
32 |
|
{ok, Pid} -> |
33 |
78 |
?LOG_INFO(#{what => gd_outgoing_conn_started, |
34 |
:-( |
server => Server, gd_pid => Pid}), |
35 |
78 |
ok; |
36 |
|
{error, {already_started, Pid}} -> |
37 |
5 |
?LOG_INFO(#{what => gd_outgoing_conn_already_started, |
38 |
:-( |
server => Server, gd_pid => Pid}), |
39 |
5 |
ok; |
40 |
|
Error -> |
41 |
:-( |
?LOG_ERROR(#{what => gd_outgoing_conn_start_failed, |
42 |
:-( |
server => Server, reason => Error}), |
43 |
:-( |
Error |
44 |
|
end. |
45 |
|
|
46 |
|
%% Call to get_connection blocks until a connection is available. |
47 |
|
%% Currently the timeout is infinity. |
48 |
|
%% This function is safe for concurrent calls if the outgoing pool is not present yet. |
49 |
|
%% The first caller will be the one initiating pool startup and the others are blocked |
50 |
|
%% in the meantime; then, everyone will use the pool initiated by the first caller. |
51 |
|
%% TODO: Revise negative cases for this function. |
52 |
|
-spec get_connection(Server :: jid:lserver()) -> pid(). |
53 |
|
get_connection(Server) -> |
54 |
32 |
get_connection(Server, 5). |
55 |
|
|
56 |
|
-spec get_connection(Server :: jid:lserver(), RetriesLeft :: non_neg_integer()) -> |
57 |
|
pid() | no_return(). |
58 |
|
get_connection(Server, 0) -> |
59 |
1 |
?LOG_ERROR(#{what => gd_cannot_acquire_outgoing_connection, server => Server}), |
60 |
1 |
throw({error, {cannot_acquire_outgoing_connection, Server}}); |
61 |
|
get_connection(Server, RetriesLeft) -> |
62 |
47 |
case mod_global_distrib_server_sup:get_connection(Server) of |
63 |
|
{error, not_available} -> |
64 |
|
%% add_server is a synchronous call that, if succeeds, |
65 |
|
%% returns after the outgoing conn layer is ready, |
66 |
|
%% so we may retry immediately |
67 |
16 |
add_server(Server), |
68 |
16 |
get_connection(Server, RetriesLeft - 1); |
69 |
|
{ok, Pid} -> |
70 |
31 |
Pid |
71 |
|
end. |
72 |
|
|
73 |
|
-spec ensure_server_started(Server :: jid:lserver()) -> ok | {error, any()}. |
74 |
|
ensure_server_started(Server) -> |
75 |
569 |
case mod_global_distrib_server_sup:is_available(Server) of |
76 |
67 |
false -> add_server(Server); |
77 |
502 |
true -> ok |
78 |
|
end. |
79 |
|
|
80 |
|
%%-------------------------------------------------------------------- |
81 |
|
%% supervisor callback |
82 |
|
%%-------------------------------------------------------------------- |
83 |
|
|
84 |
|
init(_) -> |
85 |
21 |
SupFlags = #{ strategy => one_for_one, intensity => 5, period => 5 }, |
86 |
21 |
{ok, {SupFlags, []}}. |