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, { |
42 |
|
refresh_interval :: pos_integer(), |
43 |
|
tref :: reference() | undefined |
44 |
|
}). |
45 |
|
|
46 |
|
%%-------------------------------------------------------------------- |
47 |
|
%% API |
48 |
|
%%-------------------------------------------------------------------- |
49 |
|
|
50 |
|
-spec start_link(Milliseconds :: non_neg_integer()) -> pid() | term(). |
51 |
|
start_link(RefreshInterval) -> |
52 |
21 |
gen_server:start_link({local, ?MODULE}, ?MODULE, [RefreshInterval], []). |
53 |
|
|
54 |
|
-spec pause() -> ok. |
55 |
|
pause() -> |
56 |
86 |
gen_server:call(?MODULE, pause). |
57 |
|
|
58 |
|
-spec unpause() -> ok. |
59 |
|
unpause() -> |
60 |
86 |
gen_server:call(?MODULE, unpause). |
61 |
|
|
62 |
|
%%-------------------------------------------------------------------- |
63 |
|
%% gen_mod callbacks |
64 |
|
%%-------------------------------------------------------------------- |
65 |
|
|
66 |
|
-spec start(Host :: jid:lserver(), Opts :: proplists:proplist()) -> any(). |
67 |
|
start(Host, Opts0) -> |
68 |
21 |
Opts = [{hosts_refresh_interval, default_refresh_interval()} | Opts0], |
69 |
21 |
mod_global_distrib_utils:start(?MODULE, Host, Opts, fun start/0). |
70 |
|
|
71 |
|
-spec stop(Host :: jid:lserver()) -> any(). |
72 |
|
stop(Host) -> |
73 |
21 |
mod_global_distrib_utils:stop(?MODULE, Host, fun stop/0). |
74 |
|
|
75 |
|
-spec deps(Host :: jid:server(), Opts :: proplists:proplist()) -> gen_mod:deps_list(). |
76 |
|
deps(Host, Opts) -> |
77 |
107 |
mod_global_distrib_utils:deps(?MODULE, Host, Opts, fun deps/1). |
78 |
|
|
79 |
|
%%-------------------------------------------------------------------- |
80 |
|
%% gen_server callbacks |
81 |
|
%%-------------------------------------------------------------------- |
82 |
|
|
83 |
|
init([RefreshInterval]) -> |
84 |
21 |
?LOG_DEBUG(#{what => gd_refresher_started, refresh_interval => RefreshInterval}), |
85 |
21 |
NState = schedule_refresh(#state{ refresh_interval = RefreshInterval }), |
86 |
21 |
{ok, NState}. |
87 |
|
|
88 |
|
handle_call(pause, _From, State = #state{tref = undefined}) -> |
89 |
:-( |
?LOG_ERROR(#{what => gd_refresher_already_paused}), |
90 |
:-( |
{reply, ok, State}; |
91 |
|
handle_call(pause, _From, State) -> |
92 |
86 |
erlang:cancel_timer(State#state.tref), |
93 |
86 |
{reply, ok, State#state{ tref = undefined }}; |
94 |
|
handle_call(unpause, _From, State = #state{tref = undefined}) -> |
95 |
86 |
{reply, ok, schedule_refresh(State)}; |
96 |
|
handle_call(unpause, _From, State = #state{tref = TRef}) -> |
97 |
:-( |
?LOG_ERROR(#{what => gd_refresher_already_running, timer_ref => TRef, |
98 |
:-( |
text => <<"GD Refresher received unpause, when already unpaused. Ignore.">>}), |
99 |
:-( |
{reply, ok, State}; |
100 |
|
handle_call(Request, From, State) -> |
101 |
:-( |
?UNEXPECTED_CALL(Request, From), |
102 |
:-( |
{reply, {error, unknown_request}, State}. |
103 |
|
|
104 |
|
handle_cast(Request, State) -> |
105 |
:-( |
?UNEXPECTED_CAST(Request), |
106 |
:-( |
{noreply, State}. |
107 |
|
|
108 |
|
handle_info({timeout, TRef, refresh}, #state{ tref = TRef } = State) -> |
109 |
1011 |
refresh(), |
110 |
1011 |
NState = schedule_refresh(State), |
111 |
1011 |
{noreply, NState, hibernate}; |
112 |
|
handle_info({timeout, _, refresh}, State) -> |
113 |
|
%% We got refresh signal from outdated timer |
114 |
:-( |
{noreply, State, hibernate}; |
115 |
|
handle_info(Msg, State) -> |
116 |
:-( |
?UNEXPECTED_INFO(Msg), |
117 |
:-( |
{noreply, State, hibernate}. |
118 |
|
|
119 |
|
terminate(Reason, _State) -> |
120 |
:-( |
?LOG_INFO(#{what => gd_refresher_stopped, reason => Reason, |
121 |
:-( |
text => <<"mod_global_distrib_refresher has terminated">>}). |
122 |
|
|
123 |
:-( |
code_change(_, State, _) -> {ok, State}. |
124 |
|
|
125 |
|
%%-------------------------------------------------------------------- |
126 |
|
%% Helper functions |
127 |
|
%%-------------------------------------------------------------------- |
128 |
|
|
129 |
|
|
130 |
|
-spec deps(Opts :: proplists:proplist()) -> gen_mod:deps_list(). |
131 |
|
deps(Opts) -> |
132 |
107 |
[{mod_global_distrib_mapping, Opts, hard}]. |
133 |
|
|
134 |
|
-spec start() -> any(). |
135 |
|
start() -> |
136 |
21 |
start_outgoing_conns_sup(), |
137 |
21 |
Interval = mod_global_distrib_utils:opt(?MODULE, hosts_refresh_interval), |
138 |
21 |
Child = #{ |
139 |
|
id => ?MODULE, |
140 |
|
start => {?MODULE, start_link, [Interval]}, |
141 |
|
restart => transient, |
142 |
|
shutdown => 5000, |
143 |
|
modules => [?MODULE] |
144 |
|
}, |
145 |
21 |
{ok, _} = supervisor:start_child(mod_global_distrib_outgoing_conns_sup, Child), |
146 |
21 |
ok. |
147 |
|
|
148 |
|
-spec stop() -> any(). |
149 |
|
stop() -> |
150 |
21 |
stop_outgoing_conns_sup(), |
151 |
21 |
ok. |
152 |
|
|
153 |
|
refresh() -> |
154 |
1011 |
Hosts = mod_global_distrib_mapping:hosts(), |
155 |
1011 |
LocalHost = local_host(), |
156 |
1011 |
?LOG_DEBUG(#{what => gd_refresher_fetched_hosts, |
157 |
1011 |
hosts => Hosts, local_host => LocalHost}), |
158 |
1011 |
lists:foreach(fun mod_global_distrib_outgoing_conns_sup:ensure_server_started/1, |
159 |
|
lists:delete(LocalHost, Hosts)). |
160 |
|
|
161 |
|
schedule_refresh(#state{ refresh_interval = Interval } = State) -> |
162 |
1118 |
TRef = erlang:start_timer(Interval, self(), refresh), |
163 |
1118 |
State#state{ tref = TRef }. |
164 |
|
|
165 |
|
default_refresh_interval() -> |
166 |
21 |
3000. |
167 |
|
|
168 |
|
local_host() -> |
169 |
1011 |
mod_global_distrib_utils:opt(?MODULE, local_host). |
170 |
|
|
171 |
|
start_outgoing_conns_sup() -> |
172 |
21 |
ConnsSup = mod_global_distrib_outgoing_conns_sup, |
173 |
21 |
ChildSpec = #{ |
174 |
|
id => ConnsSup, |
175 |
|
start => {ConnsSup, start_link, []}, |
176 |
|
restart => permanent, |
177 |
|
shutdown => 5000, |
178 |
|
type => supervisor, |
179 |
|
modules => [ConnsSup] |
180 |
|
}, |
181 |
21 |
ejabberd_sup:start_child(ChildSpec). |
182 |
|
|
183 |
|
|
184 |
|
stop_outgoing_conns_sup() -> |
185 |
21 |
ConnsSup = mod_global_distrib_outgoing_conns_sup, |
186 |
21 |
ejabberd_sup:stop_child(ConnsSup). |