1 |
|
%%============================================================================== |
2 |
|
%% Copyright 2017 Erlang Solutions Ltd. |
3 |
|
%% |
4 |
|
%% Licensed under the Apache License, Version 2.0 (the "License"); |
5 |
|
%% you may not use this file except in compliance with the License. |
6 |
|
%% You may obtain a copy of the License at |
7 |
|
%% |
8 |
|
%% http://www.apache.org/licenses/LICENSE-2.0 |
9 |
|
%% |
10 |
|
%% Unless required by applicable law or agreed to in writing, software |
11 |
|
%% distributed under the License is distributed on an "AS IS" BASIS, |
12 |
|
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 |
|
%% See the License for the specific language governing permissions and |
14 |
|
%% limitations under the License. |
15 |
|
%%============================================================================== |
16 |
|
-module(mod_global_distrib_server_sup). |
17 |
|
-author('piotr.nosek@erlang-solutions.com'). |
18 |
|
|
19 |
|
-behaviour(supervisor). |
20 |
|
|
21 |
|
-include("mongoose.hrl"). |
22 |
|
|
23 |
|
-export([start_link/1, init/1]). |
24 |
|
-export([get_connection/1, is_available/1]). |
25 |
|
-export([start_pool/4, stop_pool/2]). |
26 |
|
|
27 |
|
-ignore_xref([start_link/1]). |
28 |
|
|
29 |
|
%%-------------------------------------------------------------------- |
30 |
|
%% API |
31 |
|
%%-------------------------------------------------------------------- |
32 |
|
|
33 |
|
-spec start_link(Server :: jid:lserver()) -> {ok, pid()} | {error, any()}. |
34 |
|
start_link(Server) -> |
35 |
79 |
SupName = mod_global_distrib_utils:server_to_sup_name(Server), |
36 |
79 |
supervisor:start_link({local, SupName}, ?MODULE, [Server]). |
37 |
|
|
38 |
|
-spec get_connection(Server :: jid:lserver()) -> {ok, pid()} | {error, not_available}. |
39 |
|
get_connection(Server) -> |
40 |
48 |
try mod_global_distrib_server_mgr:get_connection(Server) |
41 |
|
%% Possible issues: |
42 |
|
%% - {'EXIT', {noproc, _}} |
43 |
|
%% - {case_clause,{'EXIT',{no_connections... |
44 |
|
catch Class:Reason:Stacktrace -> |
45 |
17 |
?LOG_ERROR(#{what => gd_get_connection_failed, server => Server, |
46 |
:-( |
class => Class, reason => Reason, stacktrace => Stacktrace}), |
47 |
|
%% May be caused by missing server_sup or missing connection manager |
48 |
|
%% The former occurs when a process tries to send a message to Server |
49 |
|
%% for the first time. |
50 |
|
%% The latter occurs when some other process already started server_sup, |
51 |
|
%% which hasn't started manager yet. |
52 |
|
%% In both cases the caller should attempt to start server_sup, |
53 |
|
%% so the main outgoing_conns_sup becomes a synchronisation point |
54 |
|
%% because it's impossible to learn that the server_sup is `already_started` |
55 |
|
%% without it finishing the init first (thus finishing the init of mgr as well). |
56 |
|
%% |
57 |
|
%% TODO: Write a test for it, once we establish a good way to reproduce |
58 |
|
%% race conditions in tests! |
59 |
17 |
{error, not_available} |
60 |
|
end. |
61 |
|
|
62 |
|
-spec is_available(Server :: jid:lserver()) -> boolean(). |
63 |
|
is_available(Server) -> |
64 |
584 |
pong == mod_global_distrib_server_mgr:ping_proc(Server). |
65 |
|
|
66 |
|
-spec start_pool(Supervisor :: pid(), |
67 |
|
Endpoint :: mod_global_distrib_utils:endpoint(), |
68 |
|
Server :: jid:lserver(), |
69 |
|
ConnOpts :: map()) -> |
70 |
|
{ok, atom(), pid()} | {error, any()}. |
71 |
|
start_pool(Supervisor, Endpoint, Server, #{connections_per_endpoint := PoolSize}) -> |
72 |
109 |
PoolRef = endpoint_to_atom(Endpoint), |
73 |
109 |
PoolParams = [ |
74 |
|
PoolRef, |
75 |
|
{mod_global_distrib_connection, start_link, [Endpoint, Server]}, |
76 |
|
[{pool_size, PoolSize}] |
77 |
|
], |
78 |
109 |
PoolSpec = #{ |
79 |
|
id => Endpoint, |
80 |
|
start => {cpool, new_pool_sup, PoolParams}, |
81 |
|
restart => temporary, |
82 |
|
shutdown => 5000, |
83 |
|
type => supervisor, |
84 |
|
modules => dynamic |
85 |
|
}, |
86 |
109 |
{ok, PoolPid} = supervisor:start_child(Supervisor, PoolSpec), |
87 |
104 |
{ok, PoolRef, PoolPid}. |
88 |
|
|
89 |
|
-spec stop_pool(Supervisor :: pid(), Endpoint :: mod_global_distrib_utils:endpoint()) -> |
90 |
|
ok | {error, any()}. |
91 |
|
stop_pool(Supervisor, Endpoint) -> |
92 |
1 |
ok = supervisor:terminate_child(Supervisor, Endpoint), |
93 |
1 |
supervisor:delete_child(Supervisor, Endpoint). |
94 |
|
|
95 |
|
%%-------------------------------------------------------------------- |
96 |
|
%% supervisor callback |
97 |
|
%%-------------------------------------------------------------------- |
98 |
|
|
99 |
|
init([Server]) -> |
100 |
79 |
SupFlags = #{ strategy => rest_for_one, intensity => 5, period => 5 }, |
101 |
79 |
MgrName = mod_global_distrib_utils:server_to_mgr_name(Server), |
102 |
79 |
ServerMgrSpec = #{ |
103 |
|
id => MgrName, |
104 |
|
start => {mod_global_distrib_server_mgr, start_link, [Server, self()]}, |
105 |
|
restart => transient, |
106 |
|
shutdown => 5000, |
107 |
|
type => worker, |
108 |
|
modules => dynamic |
109 |
|
}, |
110 |
79 |
{ok, {SupFlags, [ServerMgrSpec]}}. |
111 |
|
|
112 |
|
%%-------------------------------------------------------------------- |
113 |
|
%% Helpers |
114 |
|
%%-------------------------------------------------------------------- |
115 |
|
|
116 |
|
-spec endpoint_to_atom(mod_global_distrib_utils:endpoint()) -> atom(). |
117 |
|
endpoint_to_atom({IP, Port}) -> |
118 |
109 |
list_to_atom(inet:ntoa(IP) ++ "_" ++ integer_to_list(Port)). |