Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use persistent terms to store backend_module in ejabberd_sm #3379

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion big_tests/tests/distributed_helper.erl
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
-export_type([rpc_spec/0]).

is_sm_distributed() ->
Backend = rpc(mim(), ejabberd_sm_backend, backend, []),
Backend = rpc(mim(), ejabberd_sm, sm_backend, []),
is_sm_backend_distributed(Backend).

is_sm_backend_distributed(ejabberd_sm_mnesia) -> true;
Expand Down
2 changes: 1 addition & 1 deletion big_tests/tests/sm_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -1502,7 +1502,7 @@ get_us_from_spec(UserSpec) ->

clear_session_table() ->
Node = ct:get_config({hosts, mim, node}),
SessionBackend = rpc(mim(), ejabberd_sm_backend, backend, []),
SessionBackend = rpc(mim(), ejabberd_sm, sm_backend, []),
rpc(mim(), SessionBackend, cleanup, [Node]).

clear_sm_session_table() ->
Expand Down
1 change: 0 additions & 1 deletion rebar.config
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
eldap_filter_yecc, 'XmppAddr', mongoose_xmpp_errors,
%% *_backend
mongoose_rdbms_backend,
ejabberd_sm_backend,
mod_bosh_backend,
mod_global_distrib_mapping_backend,
mod_keystore_backend,
Expand Down
101 changes: 54 additions & 47 deletions src/ejabberd_sm.erl
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
get_full_session_list/0,
register_iq_handler/3,
unregister_iq_handler/2,
force_update_presence/1,
force_update_presence/2,
user_resources/2,
get_session_pid/1,
get_session/1,
Expand All @@ -65,7 +65,8 @@
is_offline/1,
get_user_present_pids/2,
sync/0,
run_session_cleanup_hook/1
run_session_cleanup_hook/1,
sm_backend/0
]).

%% Hook handlers
Expand All @@ -79,10 +80,10 @@
-export([do_filter/3]).
-export([do_route/4]).

-ignore_xref([{ejabberd_sm_backend, backend, 0},
bounce_offline_message/4, check_in_subscription/5, disconnect_removed_user/3,
do_filter/3, do_route/4, force_update_presence/1, get_unique_sessions_number/0,
get_user_present_pids/2, node_cleanup/2, start_link/0, user_resources/2]).
-ignore_xref([bounce_offline_message/4, check_in_subscription/5, disconnect_removed_user/3,
do_filter/3, do_route/4, force_update_presence/2, get_unique_sessions_number/0,
get_user_present_pids/2, node_cleanup/2, start_link/0, user_resources/2,
sm_backend/0]).

-include("mongoose.hrl").
-include("jlib.hrl").
Expand All @@ -105,6 +106,7 @@
}.
-type info() :: #{info_key() => any()}.

-type backend_type() :: mnesia | redis.
-type backend() :: ejabberd_sm_mnesia | ejabberd_sm_redis.
-type close_reason() :: resumed | normal | replaced.
-type info_key() :: atom().
Expand Down Expand Up @@ -220,8 +222,9 @@ open_session(HostType, SID, JID, Info) ->
Info :: info(),
ReplacedPids :: [pid()].
open_session(HostType, SID, JID, Priority, Info) ->
BackendModule = sm_backend(),
set_session(SID, JID, Priority, Info),
ReplacedPIDs = check_for_sessions_to_replace(JID),
ReplacedPIDs = check_for_sessions_to_replace(HostType, BackendModule, JID),
mongoose_hooks:sm_register_connection_hook(HostType, SID, JID, Info),
ReplacedPIDs.

Expand All @@ -233,13 +236,14 @@ open_session(HostType, SID, JID, Priority, Info) ->
Acc1 :: mongoose_acc:t().
close_session(Acc, SID, JID, Reason) ->
#jid{luser = LUser, lserver = LServer, lresource = LResource} = JID,
Info = case ejabberd_gen_sm:get_sessions(sm_backend(), LUser, LServer, LResource) of
BackendModule = sm_backend(),
Info = case ejabberd_gen_sm:get_sessions(BackendModule, LUser, LServer, LResource) of
[Session] ->
Session#session.info;
_ ->
[]
end,
ejabberd_gen_sm:delete_session(sm_backend(), SID, LUser, LServer, LResource),
ejabberd_gen_sm:delete_session(BackendModule, SID, LUser, LServer, LResource),
mongoose_hooks:sm_remove_connection_hook(Acc, SID, JID, Info, Reason).

-spec store_info(jid:jid(), info_key(), any()) ->
Expand Down Expand Up @@ -495,8 +499,7 @@ init([]) ->
undefined -> {mnesia, []};
Value -> Value
end,
{Mod, Code} = dynamic_compile:from_string(sm_backend(Backend)),
code:load_binary(Mod, "ejabberd_sm_backend.erl", Code),
store_backend(Backend),

ets:new(sm_iqtable, [named_table, protected, {read_concurrency, true}]),

Expand Down Expand Up @@ -915,37 +918,45 @@ is_offline(#jid{luser = LUser, lserver = LServer}) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% @doc On new session, check if some existing connections need to be replace
-spec check_for_sessions_to_replace(JID) -> ReplacedPids when
-spec check_for_sessions_to_replace(HostType, BackendModule, JID) -> ReplacedPids when
HostType :: mongooseim:host_type(),
BackendModule :: backend(),
JID :: jid:jid(),
ReplacedPids :: [pid()].
check_for_sessions_to_replace(JID) ->
check_for_sessions_to_replace(HostType, BackendModule, JID) ->
#jid{luser = LUser, lserver = LServer, lresource = LResource} = JID,
%% TODO: Depending on how this is executed, there could be an unneeded
%% replacement for max_sessions. We need to check this at some point.
ReplacedRedundantSessions = check_existing_resources(LUser, LServer, LResource),
AllReplacedSessionPids = check_max_sessions(LUser, LServer, ReplacedRedundantSessions),
ReplacedRedundantSessions = check_existing_resources(HostType, BackendModule, LUser, LServer, LResource),
AllReplacedSessionPids = check_max_sessions(HostType, BackendModule, LUser, LServer, ReplacedRedundantSessions),
[Pid ! replaced || Pid <- AllReplacedSessionPids],
AllReplacedSessionPids.

-spec check_existing_resources(LUser, LServer, LResource) -> ReplacedSessionsPIDs when
LUser :: 'error' | jid:luser() | tuple(),
LServer :: 'error' | jid:lserver() | tuple(),
LResource :: 'error' | jid:lresource() | [byte()] | tuple(),
-spec check_existing_resources(HostType, BackendModule, LUser, LServer, LResource) ->
ReplacedSessionsPIDs when
HostType :: mongooseim:host_type(),
BackendModule :: backend(),
LUser :: jid:luser(),
LServer :: jid:lserver(),
LResource :: jid:lresource(),
ReplacedSessionsPIDs :: ordsets:ordset(pid()).
check_existing_resources(LUser, LServer, LResource) ->
check_existing_resources(_HostType, BackendModule, LUser, LServer, LResource) ->
%% A connection exist with the same resource. We replace it:
Sessions = ejabberd_gen_sm:get_sessions(sm_backend(), LUser, LServer, LResource),
Sessions = ejabberd_gen_sm:get_sessions(BackendModule, LUser, LServer, LResource),
case [S#session.sid || S <- Sessions] of
[] -> [];
SIDs ->
MaxSID = lists:max(SIDs),
ordsets:from_list([Pid || {_, Pid} = S <- SIDs, S /= MaxSID])
end.


-spec check_max_sessions(LUser :: jid:user(), LServer :: jid:server(),
ReplacedPIDs :: [pid()]) -> AllReplacedPIDs :: ordsets:ordset(pid()).
check_max_sessions(LUser, LServer, ReplacedPIDs) ->
-spec check_max_sessions(HostType :: mongooseim:host_type(),
BackendModule :: backend(),
LUser :: jid:luser(),
LServer :: jid:lserver(),
ReplacedPIDs :: [pid()]) ->
AllReplacedPIDs :: ordsets:ordset(pid()).
check_max_sessions(HostType, BackendModule, LUser, LServer, ReplacedPIDs) ->
%% If the max number of sessions for a given is reached, we replace the
%% first one
SIDs = lists:filtermap(
Expand All @@ -956,8 +967,8 @@ check_max_sessions(LUser, LServer, ReplacedPIDs) ->
false -> {true, SID}
end
end,
ejabberd_gen_sm:get_sessions(sm_backend(), LUser, LServer)),
MaxSessions = get_max_user_sessions(LUser, LServer),
ejabberd_gen_sm:get_sessions(BackendModule, LUser, LServer)),
MaxSessions = get_max_user_sessions(HostType, LUser, LServer),
case length(SIDs) =< MaxSessions of
true -> ordsets:to_list(ReplacedPIDs);
false ->
Expand All @@ -969,12 +980,14 @@ check_max_sessions(LUser, LServer, ReplacedPIDs) ->
%% @doc Get the user_max_session setting
%% This option defines the max number of time a given users are allowed to
%% log in. Defaults to infinity
-spec get_max_user_sessions(LUser, Host) -> infinity | pos_integer() when
LUser :: jid:user(),
Host :: jid:server().
get_max_user_sessions(LUser, Host) ->
case acl:match_rule(
Host, max_user_sessions, jid:make_noprep(LUser, Host, <<>>)) of
-spec get_max_user_sessions(HostType, LUser, LServer) -> Result when
HostType :: mongooseim:host_type(),
LUser :: jid:luser(),
LServer :: jid:lserver(),
Result :: infinity | pos_integer().
get_max_user_sessions(HostType, LUser, LServer) ->
JID = jid:make_noprep(LUser, LServer, <<>>),
case acl:match_rule_for_host_type(HostType, LServer, max_user_sessions, JID) of
Max when is_integer(Max) -> Max;
infinity -> infinity;
_ -> ?MAX_USER_SESSIONS
Expand Down Expand Up @@ -1009,8 +1022,8 @@ process_iq(_, From, To, Acc, Packet) ->
ejabberd_router:route(To, From, Acc1, Err).


-spec force_update_presence({jid:user(), jid:server()}) -> 'ok'.
force_update_presence({LUser, LServer}) ->
-spec force_update_presence(mongooseim:host_type(), {jid:luser(), jid:lserver()}) -> 'ok'.
force_update_presence(_HostType, {LUser, LServer}) ->
Ss = ejabberd_gen_sm:get_sessions(sm_backend(), LUser, LServer),
lists:foreach(fun(#session{sid = {_, Pid}}) ->
Pid ! {force_update_presence, LUser}
Expand Down Expand Up @@ -1050,17 +1063,6 @@ user_resources(UserStr, ServerStr) ->
Resources = get_user_resources(JID),
lists:sort(Resources).

-spec sm_backend(backend()) -> string().
sm_backend(Backend) ->
lists:flatten(
["-module(ejabberd_sm_backend).
-export([backend/0]).
-spec backend() -> atom().
backend() ->
ejabberd_sm_",
atom_to_list(Backend),
".\n"]).

-spec get_cached_unique_count() -> non_neg_integer().
get_cached_unique_count() ->
case mongoose_metrics:get_metric_value(global, ?UNIQUE_COUNT_CACHE) of
Expand All @@ -1072,4 +1074,9 @@ get_cached_unique_count() ->

-spec sm_backend() -> backend().
sm_backend() ->
ejabberd_sm_backend:backend().
persistent_term:get(sm_backend_module, ejabberd_sm_mnesia).

-spec store_backend(backend_type()) -> ok.
store_backend(Backend) ->
BackendModule = list_to_atom("ejabberd_sm_" ++ atom_to_list(Backend)),
persistent_term:put(sm_backend_module, BackendModule).
2 changes: 1 addition & 1 deletion test/ejabberd_sm_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ set_test_case_meck(MaxUserSessions) ->
meck:new(ejabberd_config, []),
meck:expect(ejabberd_config, get_local_option, fun(_) -> undefined end),
meck:new(acl, []),
meck:expect(acl, match_rule, fun(_, _, _) -> MaxUserSessions end),
meck:expect(acl, match_rule_for_host_type, fun(_, _, _, _) -> MaxUserSessions end),
meck:new(gen_hook, []),
meck:expect(gen_hook, run_fold, fun(_, _, Acc, _) -> {ok, Acc} end),
meck:new(mongoose_domain_api, []),
Expand Down