./ct_report/coverage/mod_global_distrib_mapping.COVER.html

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
17 -module(mod_global_distrib_mapping).
18 -author('konrad.zemek@erlang-solutions.com').
19
20 -behaviour(gen_mod).
21 -behaviour(mongoose_module_metrics).
22
23 -include("mongoose.hrl").
24 -include("jlib.hrl").
25 -include("global_distrib_metrics.hrl").
26
27 -define(DOMAIN_TAB, mod_global_distrib_domain_cache_tab).
28 -define(JID_TAB, mod_global_distrib_jid_cache_tab).
29
30 -export([start/2, stop/1, deps/2]).
31 -export([for_domain/1, insert_for_domain/1, insert_for_domain/2, insert_for_domain/3,
32 cache_domain/2, delete_for_domain/1, all_domains/0, public_domains/0]).
33 -export([for_jid/1, insert_for_jid/1, cache_jid/2, delete_for_jid/1, clear_cache/1]).
34 -export([register_subhost/3, unregister_subhost/2, packet_to_component/3,
35 session_opened/5, session_closed/5]).
36 -export([endpoints/1, hosts/0]).
37
38 -define(MOD_GLOBAL_DISTRIB_MAPPING_BACKEND, mod_global_distrib_mapping_backend).
39 -ignore_xref([
40 {?MOD_GLOBAL_DISTRIB_MAPPING_BACKEND, get_domains, 0},
41 {?MOD_GLOBAL_DISTRIB_MAPPING_BACKEND, delete_domain, 1},
42 {?MOD_GLOBAL_DISTRIB_MAPPING_BACKEND, delete_session, 1},
43 {?MOD_GLOBAL_DISTRIB_MAPPING_BACKEND, get_endpoints, 1},
44 {?MOD_GLOBAL_DISTRIB_MAPPING_BACKEND, get_domain, 1},
45 {?MOD_GLOBAL_DISTRIB_MAPPING_BACKEND, get_session, 1},
46 {?MOD_GLOBAL_DISTRIB_MAPPING_BACKEND, get_hosts, 0},
47 {?MOD_GLOBAL_DISTRIB_MAPPING_BACKEND, get_public_domains, 0},
48 {?MOD_GLOBAL_DISTRIB_MAPPING_BACKEND, put_domain, 2},
49 {?MOD_GLOBAL_DISTRIB_MAPPING_BACKEND, put_session, 1},
50 {?MOD_GLOBAL_DISTRIB_MAPPING_BACKEND, start, 1},
51 {?MOD_GLOBAL_DISTRIB_MAPPING_BACKEND, stop, 0},
52 behaviour_info/1, delete_for_domain/1, delete_for_jid/1, insert_for_domain/1,
53 insert_for_domain/2, insert_for_domain/3, insert_for_jid/1, packet_to_component/3,
54 register_subhost/3, session_closed/5, session_opened/5, unregister_subhost/2
55 ]).
56
57 -type endpoint() :: mod_global_distrib_utils:endpoint().
58
59 %%--------------------------------------------------------------------
60 %% Callbacks
61 %%--------------------------------------------------------------------
62
63 -callback start(Opts :: proplists:proplist()) -> any().
64 -callback stop() -> any().
65 -callback put_session(JID :: binary()) -> ok | error.
66 -callback get_session(JID :: binary()) -> {ok, Host :: binary()} | error.
67 -callback delete_session(JID :: binary()) -> ok | error.
68 -callback put_domain(Domain :: binary(), IsHidden :: boolean()) -> ok | error.
69 -callback get_domain(Domain :: binary()) -> {ok, Host :: binary()} | error.
70 -callback delete_domain(Domain :: binary()) -> ok | error.
71 -callback get_domains() -> {ok, [Domain :: binary()]} | error.
72 -callback get_public_domains() -> {ok, [Domain :: binary()]} | error.
73 -callback get_endpoints(Host :: binary()) -> {ok, [endpoint()]}.
74 -callback get_hosts() -> [Host :: jid:lserver()].
75
76 %%--------------------------------------------------------------------
77 %% API
78 %%--------------------------------------------------------------------
79
80 -spec for_domain(Domain :: binary()) -> {ok, Host :: jid:lserver()} | error.
81 for_domain(Domain) when is_binary(Domain) ->
82
:-(
mongoose_metrics:update(global, ?GLOBAL_DISTRIB_MAPPING_FETCHES, 1),
83
:-(
{Time, R} = timer:tc(ets_cache, lookup, [?DOMAIN_TAB, Domain, fun() -> get_domain(Domain) end]),
84
:-(
mongoose_metrics:update(global, ?GLOBAL_DISTRIB_MAPPING_FETCH_TIME, Time),
85
:-(
R.
86
87 -spec insert_for_domain(Domain :: binary()) -> ok.
88 insert_for_domain(Domain) when is_binary(Domain) ->
89
:-(
insert_for_domain(Domain, false).
90
91 -spec insert_for_domain(Domain :: binary(), IsHidden :: boolean()) -> ok.
92 insert_for_domain(Domain, IsHidden) when is_binary(Domain) ->
93
:-(
LocalHost = opt(local_host),
94
:-(
insert_for_domain(Domain, LocalHost, IsHidden).
95
96 -spec insert_for_domain(Domain :: binary(), Host :: binary(), IsHidden :: boolean()) -> ok.
97 insert_for_domain(Domain, Host, IsHidden) when is_binary(Domain), is_binary(Host) ->
98
:-(
do_insert_for_domain(Domain, Host, fun(ToStore) -> put_domain(ToStore, IsHidden) end).
99
100 -spec cache_domain(Domain :: binary(), Host :: binary()) -> ok.
101 cache_domain(Domain, Host) when is_binary(Domain), is_binary(Host) ->
102
:-(
do_insert_for_domain(Domain, Host, fun(_) -> ok end).
103
104 -spec delete_for_domain(Domain :: binary()) -> ok.
105 delete_for_domain(Domain) when is_binary(Domain) ->
106
:-(
delete_domain(Domain),
107
:-(
ets_cache:delete(?DOMAIN_TAB, Domain).
108
109 -spec for_jid(jid:jid() | jid:ljid()) -> {ok, Host :: jid:lserver()} | error.
110
:-(
for_jid(#jid{} = Jid) -> for_jid(jid:to_lower(Jid));
111 for_jid({_, _, _} = Jid) ->
112
:-(
mongoose_metrics:update(global, ?GLOBAL_DISTRIB_MAPPING_FETCHES, 1),
113
:-(
{Time, R} = timer:tc(fun do_lookup_jid/1, [Jid]),
114
:-(
mongoose_metrics:update(global, ?GLOBAL_DISTRIB_MAPPING_FETCH_TIME, Time),
115
:-(
R.
116
117 -spec insert_for_jid(jid:jid() | jid:ljid()) -> ok.
118 insert_for_jid(Jid) ->
119
:-(
LocalHost = opt(local_host),
120
:-(
do_insert_for_jid(Jid, LocalHost, fun put_session/1).
121
122 -spec cache_jid(jid:jid() | jid:ljid(), Host :: jid:lserver()) -> ok.
123 cache_jid(Jid, Host) when is_binary(Host) ->
124
:-(
do_insert_for_jid(Jid, Host, fun(_) -> ok end).
125
126 -spec clear_cache(jid:jid()) -> ok.
127 clear_cache(#jid{} = Jid) ->
128
:-(
GlobalHost = opt(global_host),
129
:-(
case jid:to_lower(Jid) of
130 {_, GlobalHost, _} = LJid ->
131
:-(
[ets_cache:delete(?JID_TAB, J) || J <- normalize_jid(LJid)];
132 {_, SubHost, _} ->
133
:-(
ets_cache:delete(?DOMAIN_TAB, SubHost)
134 end.
135
136 -spec delete_for_jid(jid:jid() | jid:ljid()) -> ok.
137
:-(
delete_for_jid(#jid{} = Jid) -> delete_for_jid(jid:to_lower(Jid));
138 delete_for_jid({_, _, _} = Jid) ->
139
:-(
lists:foreach(
140 fun(BinJid) ->
141
:-(
delete_session(BinJid),
142
:-(
ets_cache:delete(?JID_TAB, BinJid)
143 end,
144 normalize_jid(Jid)).
145
146 -spec all_domains() -> {ok, [jid:lserver()]}.
147 all_domains() ->
148
:-(
mod_global_distrib_mapping_backend:get_domains().
149
150 -spec public_domains() -> {ok, [jid:lserver()]}.
151 public_domains() ->
152
:-(
mod_global_distrib_mapping_backend:get_public_domains().
153
154 -spec endpoints(Host :: jid:lserver()) -> {ok, [endpoint()]}.
155 endpoints(Host) ->
156
:-(
mod_global_distrib_mapping_backend:get_endpoints(Host).
157
158 -spec hosts() -> [Host :: jid:lserver()].
159 hosts() ->
160
:-(
mod_global_distrib_mapping_backend:get_hosts().
161
162 %%--------------------------------------------------------------------
163 %% gen_mod API
164 %%--------------------------------------------------------------------
165
166 -spec start(Host :: jid:lserver(), Opts :: proplists:proplist()) -> any().
167 start(Host, Opts0) ->
168
:-(
AdvEndpoints = get_advertised_endpoints(Opts0),
169
:-(
Opts = [{advertised_endpoints, AdvEndpoints}, {backend, redis}, {redis, [no_opts]}, {cache_missed, true},
170 {domain_lifetime_seconds, 600}, {jid_lifetime_seconds, 5}, {max_jids, 10000} | Opts0],
171
:-(
mod_global_distrib_utils:start(?MODULE, Host, Opts, fun start/0).
172
173 -spec stop(Host :: jid:lserver()) -> any().
174 stop(Host) ->
175
:-(
mod_global_distrib_utils:stop(?MODULE, Host, fun stop/0).
176
177 -spec deps(Host :: jid:server(), Opts :: proplists:proplist()) -> gen_mod:deps_list().
178 deps(Host, Opts) ->
179
:-(
mod_global_distrib_utils:deps(?MODULE, Host, Opts, fun deps/1).
180
181 %%--------------------------------------------------------------------
182 %% Hooks implementation
183 %%--------------------------------------------------------------------
184
185 -spec session_opened(Acc, binary(), ejabberd_sm:sid(), UserJID :: jid:jid(), Info :: list()) ->
186 Acc when Acc :: any().
187 session_opened(Acc, _HostType, _SID, UserJid, _Info) ->
188
:-(
insert_for_jid(UserJid),
189
:-(
Acc.
190
191 -spec session_closed(mongoose_acc:t(),
192 ejabberd_sm:sid(),
193 UserJID :: jid:jid(),
194 Info :: list(),
195 _Status :: any()) ->
196 mongoose_acc:t().
197 session_closed(Acc, _SID, UserJid, _Info, _Reason) ->
198
:-(
delete_for_jid(UserJid),
199
:-(
Acc.
200
201 -spec packet_to_component(Acc :: mongoose_acc:t(),
202 From :: jid:jid(),
203 To :: jid:jid()) -> mongoose_acc:t().
204 packet_to_component(Acc, From, _To) ->
205
:-(
mod_global_distrib_utils:maybe_update_mapping(From, Acc),
206
:-(
Acc.
207
208 -spec register_subhost(any(), SubHost :: binary(), IsHidden :: boolean()) -> ok.
209 register_subhost(_, SubHost, IsHidden) ->
210
:-(
IsSubhostOf =
211 fun(Host) ->
212
:-(
case binary:match(SubHost, Host) of
213
:-(
{Start, Length} -> Start + Length == byte_size(SubHost);
214
:-(
_ -> false
215 end
216 end,
217
218
:-(
GlobalHost = opt(global_host),
219
:-(
case lists:filter(IsSubhostOf, ?MYHOSTS) of
220
:-(
[GlobalHost] -> insert_for_domain(SubHost, IsHidden);
221
:-(
_ -> ok
222 end.
223
224 -spec unregister_subhost(any(), SubHost :: binary()) -> ok.
225 unregister_subhost(_, SubHost) ->
226
:-(
delete_for_domain(SubHost).
227
228 %%--------------------------------------------------------------------
229 %% Helpers
230 %%--------------------------------------------------------------------
231
232 -spec deps(proplists:proplist()) -> gen_mod:deps_list().
233 deps(_Opts) ->
234
:-(
[{mod_global_distrib_receiver, hard}].
235
236 -spec start() -> any().
237 start() ->
238
:-(
Host = opt(global_host),
239
:-(
Backend = opt(backend),
240
:-(
gen_mod:start_backend_module(?MODULE, [{backend, Backend}]),
241
:-(
mod_global_distrib_mapping_backend:start(opt(Backend)),
242
243
:-(
mongoose_metrics:ensure_metric(global, ?GLOBAL_DISTRIB_MAPPING_FETCH_TIME, histogram),
244
:-(
mongoose_metrics:ensure_metric(global, ?GLOBAL_DISTRIB_MAPPING_FETCHES, spiral),
245
:-(
mongoose_metrics:ensure_metric(global, ?GLOBAL_DISTRIB_MAPPING_CACHE_MISSES, spiral),
246
247
:-(
CacheMissed = opt(cache_missed),
248
:-(
DomainLifetime = opt(domain_lifetime_seconds) * 1000,
249
:-(
JidLifetime = opt(jid_lifetime_seconds) * 1000,
250
:-(
MaxJids = opt(max_jids),
251
252
:-(
ejabberd_hooks:add(register_subhost, global, ?MODULE, register_subhost, 90),
253
:-(
ejabberd_hooks:add(unregister_subhost, global, ?MODULE, unregister_subhost, 90),
254
:-(
ejabberd_hooks:add(packet_to_component, global, ?MODULE, packet_to_component, 90),
255
:-(
ejabberd_hooks:add(sm_register_connection_hook, Host, ?MODULE, session_opened, 90),
256
:-(
ejabberd_hooks:add(sm_remove_connection_hook, Host, ?MODULE, session_closed, 90),
257
258
:-(
ets_cache:new(?DOMAIN_TAB, [{cache_missed, CacheMissed}, {life_time, DomainLifetime}]),
259
:-(
ets_cache:new(?JID_TAB, [{cache_missed, CacheMissed}, {life_time, JidLifetime},
260 {max_size, MaxJids}]).
261
262 -spec stop() -> any().
263 stop() ->
264
:-(
Host = opt(global_host),
265
266
:-(
ets_cache:delete(?JID_TAB),
267
:-(
ets_cache:delete(?DOMAIN_TAB),
268
269
:-(
ejabberd_hooks:delete(sm_remove_connection_hook, Host, ?MODULE, session_closed, 90),
270
:-(
ejabberd_hooks:delete(sm_register_connection_hook, Host, ?MODULE, session_opened, 90),
271
:-(
ejabberd_hooks:delete(packet_to_component, global, ?MODULE, packet_to_component, 90),
272
:-(
ejabberd_hooks:delete(unregister_subhost, global, ?MODULE, unregister_subhost, 90),
273
:-(
ejabberd_hooks:delete(register_subhost, global, ?MODULE, register_subhost, 90),
274
275
:-(
mod_global_distrib_mapping_backend:stop().
276
277 -spec normalize_jid(jid:ljid()) -> [binary()].
278 normalize_jid({_, _, _} = FullJid) ->
279
:-(
case jid:to_bare(FullJid) of
280
:-(
FullJid -> [jid:to_binary(FullJid)];
281
:-(
BareJid -> [jid:to_binary(FullJid), jid:to_binary(BareJid)]
282 end.
283
284 -spec opt(Key :: atom()) -> term().
285 opt(Key) ->
286
:-(
mod_global_distrib_utils:opt(?MODULE, Key).
287
288 -spec get_session(Key :: binary()) -> {ok, term()} | error.
289 get_session(Key) ->
290
:-(
mongoose_metrics:update(global, ?GLOBAL_DISTRIB_MAPPING_CACHE_MISSES, 1),
291
:-(
mod_global_distrib_mapping_backend:get_session(Key).
292
293 -spec put_session(Key :: binary()) -> ok.
294 put_session(Key) ->
295
:-(
?LOG_DEBUG(#{what => gd_mapper_put_session, key => Key}),
296
:-(
mod_global_distrib_mapping_backend:put_session(Key).
297
298 -spec delete_session(Key :: binary()) -> ok.
299 delete_session(Key) ->
300
:-(
?LOG_DEBUG(#{what => gd_mapper_delete_session, key => Key}),
301
:-(
mod_global_distrib_mapping_backend:delete_session(Key).
302
303 -spec get_domain(Key :: binary()) -> {ok, term()} | error.
304 get_domain(Key) ->
305
:-(
mongoose_metrics:update(global, ?GLOBAL_DISTRIB_MAPPING_CACHE_MISSES, 1),
306
:-(
mod_global_distrib_mapping_backend:get_domain(Key).
307
308 -spec put_domain(Key :: binary(), IsHidden :: boolean()) -> ok.
309 put_domain(Key, IsHidden) ->
310
:-(
mod_global_distrib_mapping_backend:put_domain(Key, IsHidden).
311
312 -spec delete_domain(Key :: binary()) -> ok.
313 delete_domain(Key) ->
314
:-(
mod_global_distrib_mapping_backend:delete_domain(Key).
315
316 -spec do_insert_for_jid(jid:jid() | jid:ljid(), Host :: jid:lserver(),
317 PutSession :: fun((binary()) -> ok | error)) -> ok.
318 do_insert_for_jid(#jid{} = Jid, Host, PutSession) ->
319
:-(
do_insert_for_jid(jid:to_lower(Jid), Host, PutSession);
320 do_insert_for_jid({_, _, _} = Jid, Host, PutSession) ->
321
:-(
lists:foreach(
322 fun(BinJid) ->
323
:-(
ets_cache:update(?JID_TAB, BinJid, {ok, Host}, fun() -> PutSession(BinJid) end)
324 end,
325 normalize_jid(Jid)).
326
327 -spec do_insert_for_domain(Domain :: binary(), Host :: jid:lserver(),
328 PutDomain :: fun((binary()) -> ok | error)) -> ok.
329 do_insert_for_domain(Domain, Host, PutDomain) ->
330
:-(
ets_cache:update(?DOMAIN_TAB, Domain, {ok, Host}, fun() -> PutDomain(Domain) end).
331
332 -spec do_lookup_jid(jid:ljid()) -> {ok, Host :: jid:lserver()} | error.
333 do_lookup_jid({_, _, _} = Jid) ->
334
:-(
BinJid = jid:to_binary(Jid),
335
:-(
LookupInDB = fun(BJid) -> fun() -> get_session(BJid) end end,
336
:-(
case ets_cache:lookup(?JID_TAB, BinJid, LookupInDB(BinJid)) of
337
:-(
{ok, _} = Result -> Result;
338 Other ->
339
:-(
case jid:to_bare(Jid) of
340
:-(
Jid -> Other;
341
:-(
BareJid -> ets_cache:lookup(?JID_TAB, BinJid, LookupInDB(jid:to_binary(BareJid)))
342 end
343 end.
344
345 -spec get_advertised_endpoints(Opts :: list()) -> [endpoint()].
346 get_advertised_endpoints(Opts) ->
347
:-(
Conns = proplists:get_value(connections, Opts, []),
348
:-(
proplists:get_value(advertised_endpoints, Conns, false).
Line Hits Source