1 |
|
%%%============================================================================== |
2 |
|
%% Copyright 2018 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 |
|
|
17 |
|
|
18 |
|
-module(mod_global_distrib_hosts_refresher). |
19 |
|
-author("dominik.stanaszek@erlang-solutions.com"). |
20 |
|
|
21 |
|
-behaviour(gen_server). |
22 |
|
-behaviour(gen_mod). |
23 |
|
-behaviour(mongoose_module_metrics). |
24 |
|
|
25 |
|
-include("mongoose.hrl"). |
26 |
|
|
27 |
|
%% API |
28 |
|
-export([start_link/1]). |
29 |
|
|
30 |
|
%% gen_server API |
31 |
|
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). |
32 |
|
|
33 |
|
%% gen_mod API |
34 |
|
-export([deps/2, start/2, stop/1]). |
35 |
|
|
36 |
|
%% Test & debug API |
37 |
|
-export([pause/0, unpause/0]). |
38 |
|
|
39 |
|
-ignore_xref([pause/0, start_link/1, unpause/0]). |
40 |
|
|
41 |
|
-record(state, {local_host :: binary(), |
42 |
|
refresh_interval :: pos_integer(), |
43 |
|
tref :: reference() | undefined}). |
44 |
|
|
45 |
|
%%-------------------------------------------------------------------- |
46 |
|
%% API |
47 |
|
%%-------------------------------------------------------------------- |
48 |
|
|
49 |
|
-spec start_link(gen_mod:module_opts()) -> pid() | term(). |
50 |
|
start_link(Opts) -> |
51 |
21 |
gen_server:start_link({local, ?MODULE}, ?MODULE, Opts, []). |
52 |
|
|
53 |
|
-spec pause() -> ok. |
54 |
|
pause() -> |
55 |
86 |
gen_server:call(?MODULE, pause). |
56 |
|
|
57 |
|
-spec unpause() -> ok. |
58 |
|
unpause() -> |
59 |
86 |
gen_server:call(?MODULE, unpause). |
60 |
|
|
61 |
|
%%-------------------------------------------------------------------- |
62 |
|
%% gen_mod callbacks |
63 |
|
%%-------------------------------------------------------------------- |
64 |
|
|
65 |
|
-spec start(mongooseim:host_type(), gen_mod:module_opts()) -> any(). |
66 |
|
start(_HostType, Opts) -> |
67 |
21 |
start_outgoing_conns_sup(), |
68 |
21 |
Child = #{ |
69 |
|
id => ?MODULE, |
70 |
|
start => {?MODULE, start_link, [Opts]}, |
71 |
|
restart => transient, |
72 |
|
shutdown => 5000, |
73 |
|
modules => [?MODULE] |
74 |
|
}, |
75 |
21 |
{ok, _} = supervisor:start_child(mod_global_distrib_outgoing_conns_sup, Child), |
76 |
21 |
ok. |
77 |
|
|
78 |
|
-spec stop(mongooseim:host_type()) -> any(). |
79 |
|
stop(_HostType) -> |
80 |
21 |
stop_outgoing_conns_sup(), |
81 |
21 |
ok. |
82 |
|
|
83 |
|
-spec deps(mongooseim:host_type(), gen_mod:module_opts()) -> gen_mod_deps:deps(). |
84 |
|
deps(_HostType, Opts) -> |
85 |
106 |
[{mod_global_distrib_utils, Opts, hard}, |
86 |
|
{mod_global_distrib_mapping, Opts, hard}]. |
87 |
|
|
88 |
|
%%-------------------------------------------------------------------- |
89 |
|
%% gen_server callbacks |
90 |
|
%%-------------------------------------------------------------------- |
91 |
|
|
92 |
|
init(#{local_host := LocalHost, hosts_refresh_interval := RefreshInterval}) -> |
93 |
21 |
?LOG_DEBUG(#{what => gd_refresher_started, refresh_interval => RefreshInterval}), |
94 |
21 |
NState = schedule_refresh(#state{local_host = LocalHost, |
95 |
|
refresh_interval = RefreshInterval}), |
96 |
21 |
{ok, NState}. |
97 |
|
|
98 |
|
handle_call(pause, _From, State = #state{tref = undefined}) -> |
99 |
:-( |
?LOG_ERROR(#{what => gd_refresher_already_paused}), |
100 |
:-( |
{reply, ok, State}; |
101 |
|
handle_call(pause, _From, State) -> |
102 |
86 |
erlang:cancel_timer(State#state.tref), |
103 |
86 |
{reply, ok, State#state{ tref = undefined }}; |
104 |
|
handle_call(unpause, _From, State = #state{tref = undefined}) -> |
105 |
86 |
{reply, ok, schedule_refresh(State)}; |
106 |
|
handle_call(unpause, _From, State = #state{tref = TRef}) -> |
107 |
:-( |
?LOG_ERROR(#{what => gd_refresher_already_running, timer_ref => TRef, |
108 |
:-( |
text => <<"GD Refresher received unpause, when already unpaused. Ignore.">>}), |
109 |
:-( |
{reply, ok, State}; |
110 |
|
handle_call(Request, From, State) -> |
111 |
:-( |
?UNEXPECTED_CALL(Request, From), |
112 |
:-( |
{reply, {error, unknown_request}, State}. |
113 |
|
|
114 |
|
handle_cast(Request, State) -> |
115 |
:-( |
?UNEXPECTED_CAST(Request), |
116 |
:-( |
{noreply, State}. |
117 |
|
|
118 |
|
handle_info({timeout, TRef, refresh}, #state{local_host = LocalHost, tref = TRef} = State) -> |
119 |
579 |
refresh(LocalHost), |
120 |
579 |
NState = schedule_refresh(State), |
121 |
579 |
{noreply, NState, hibernate}; |
122 |
|
handle_info({timeout, _, refresh}, State) -> |
123 |
|
%% We got refresh signal from outdated timer |
124 |
:-( |
{noreply, State, hibernate}; |
125 |
|
handle_info(Msg, State) -> |
126 |
:-( |
?UNEXPECTED_INFO(Msg), |
127 |
:-( |
{noreply, State, hibernate}. |
128 |
|
|
129 |
|
terminate(Reason, _State) -> |
130 |
:-( |
?LOG_INFO(#{what => gd_refresher_stopped, reason => Reason, |
131 |
:-( |
text => <<"mod_global_distrib_refresher has terminated">>}). |
132 |
|
|
133 |
:-( |
code_change(_, State, _) -> {ok, State}. |
134 |
|
|
135 |
|
%%-------------------------------------------------------------------- |
136 |
|
%% Helper functions |
137 |
|
%%-------------------------------------------------------------------- |
138 |
|
|
139 |
|
refresh(LocalHost) -> |
140 |
579 |
Hosts = mod_global_distrib_mapping:hosts(), |
141 |
579 |
?LOG_DEBUG(#{what => gd_refresher_fetched_hosts, |
142 |
579 |
hosts => Hosts, local_host => LocalHost}), |
143 |
579 |
lists:foreach(fun mod_global_distrib_outgoing_conns_sup:ensure_server_started/1, |
144 |
|
lists:delete(LocalHost, Hosts)). |
145 |
|
|
146 |
|
schedule_refresh(#state{ refresh_interval = Interval } = State) -> |
147 |
686 |
TRef = erlang:start_timer(Interval, self(), refresh), |
148 |
686 |
State#state{ tref = TRef }. |
149 |
|
|
150 |
|
start_outgoing_conns_sup() -> |
151 |
21 |
ConnsSup = mod_global_distrib_outgoing_conns_sup, |
152 |
21 |
ChildSpec = #{ |
153 |
|
id => ConnsSup, |
154 |
|
start => {ConnsSup, start_link, []}, |
155 |
|
restart => permanent, |
156 |
|
shutdown => 5000, |
157 |
|
type => supervisor, |
158 |
|
modules => [ConnsSup] |
159 |
|
}, |
160 |
21 |
ejabberd_sup:start_child(ChildSpec). |
161 |
|
|
162 |
|
stop_outgoing_conns_sup() -> |
163 |
21 |
ConnsSup = mod_global_distrib_outgoing_conns_sup, |
164 |
21 |
ejabberd_sup:stop_child(ConnsSup). |