Skip to content

Commit

Permalink
Merge pull request #3493 from esl/cache_muclight_affs
Browse files Browse the repository at this point in the history
Cache muclight affs
  • Loading branch information
chrzaszcz authored Jan 12, 2022
2 parents 6c32622 + 65a12d9 commit 3bf8282
Show file tree
Hide file tree
Showing 7 changed files with 225 additions and 37 deletions.
4 changes: 4 additions & 0 deletions big_tests/tests/muc_light_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,10 @@ schema_opts(CaseName) ->
common_muc_light_opts() ->
[{host, subhost_pattern(muc_light_helper:muc_host_pattern())},
{backend, mongoose_helper:mnesia_or_rdbms_backend()},
{cache_affs, [{module, internal},
{strategy, fifo},
{time_to_live, 2},
{number_of_segments, 3}]},
{rooms_in_rosters, true}].

mam_muc_config(CaseName) when CaseName =:= disco_features_with_mam;
Expand Down
5 changes: 3 additions & 2 deletions src/mod_cache_users.erl
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ remove_domain(Acc, HostType, Domain) ->
HostType :: mongooseim:host_type(),
Jid :: jid:jid(),
RequestType :: ejabberd_auth:exist_type()) ->
boolean().
boolean() | {stop, true}.
does_cached_user_exist(false, HostType, Jid, stored) ->
case mongoose_user_cache:is_member(HostType, ?MODULE, Jid) of
true -> {stop, true};
Expand All @@ -94,6 +94,7 @@ does_cached_user_exist(Status, _, _, _) ->
RequestType :: ejabberd_auth:exist_type()) ->
boolean().
maybe_put_user_into_cache(true, HostType, Jid, stored) ->
mongoose_user_cache:merge_entry(HostType, ?MODULE, Jid, #{});
mongoose_user_cache:merge_entry(HostType, ?MODULE, Jid, #{}),
true;
maybe_put_user_into_cache(Status, _, _, _) ->
Status.
25 changes: 22 additions & 3 deletions src/mongoose_hooks.erl
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,9 @@
-export([is_muc_room_owner/4,
can_access_identity/3,
can_access_room/4,
acc_room_affiliations/2]).
acc_room_affiliations/2,
room_new_affiliations/4,
room_exists/2]).

-export([mam_archive_id/2,
mam_archive_size/3,
Expand Down Expand Up @@ -910,9 +912,26 @@ can_access_room(HostType, Acc, Room, User) ->
Room :: jid:jid(),
NewAcc :: mongoose_acc:t().
acc_room_affiliations(Acc, Room) ->
HostType = mongoose_acc:host_type(Acc),
HostType = mod_muc_light_utils:acc_to_host_type(Acc),
run_hook_for_host_type(acc_room_affiliations, HostType, Acc, [Room]).

-spec room_exists(HostType, Room) -> Result when
HostType :: mongooseim:host_type(),
Room :: jid:jid(),
Result :: boolean().
room_exists(HostType, Room) ->
run_hook_for_host_type(room_exists, HostType, false, [HostType, Room]).

-spec room_new_affiliations(Acc, Room, NewAffs, Version) -> NewAcc when
Acc :: mongoose_acc:t(),
Room :: jid:jid(),
NewAffs :: mod_muc_light:aff_users(),
Version :: binary(),
NewAcc :: mongoose_acc:t().
room_new_affiliations(Acc, Room, NewAffs, Version) ->
HostType = mod_muc_light_utils:acc_to_host_type(Acc),
run_hook_for_host_type(room_new_affiliations, HostType, Acc, [Room, NewAffs, Version]).

%% MAM related hooks

%%% @doc The `mam_archive_id' hook is called to determine
Expand Down Expand Up @@ -1334,7 +1353,7 @@ filter_room_packet(HostType, Packet, EventData) ->
Room :: jid:luser(),
Result :: any().
forget_room(HostType, MucHost, Room) ->
run_hook_for_host_type(forget_room, HostType, ok, [HostType, MucHost, Room]).
run_hook_for_host_type(forget_room, HostType, #{}, [HostType, MucHost, Room]).

-spec invitation_sent(HookServer, Host, RoomJID, From, To, Reason) -> Result when
HookServer :: jid:server(),
Expand Down
38 changes: 28 additions & 10 deletions src/muc_light/mod_muc_light.erl
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
delete_room/1]).

%% gen_mod callbacks
-export([start/2, stop/1, config_spec/0, supported_features/0]).
-export([start/2, stop/1, config_spec/0, supported_features/0, deps/2]).

%% config processing callback
-export([process_config_schema/1]).
Expand All @@ -53,6 +53,7 @@
is_muc_room_owner/4,
can_access_room/4,
acc_room_affiliations/2,
room_exists/3,
can_access_identity/4]).
-export([get_room_affiliations_from_acc/1]).

Expand All @@ -69,7 +70,7 @@

-ignore_xref([
add_rooms_to_roster/2, apply_rsm/3, can_access_identity/4, can_access_room/4,
default_schema/0, disco_local_items/1, acc_room_affiliations/2,
default_schema/0, disco_local_items/1, acc_room_affiliations/2, room_exists/3,
force_clear_from_ct/1, is_muc_room_owner/4, prevent_service_unavailable/4,
process_iq_get/5, process_iq_set/4, remove_domain/3, remove_user/3,
server_host_to_muc_host/2
Expand Down Expand Up @@ -108,6 +109,7 @@ config_schema_for_host_type(HostType) ->
gen_mod:get_module_opt(HostType, ?MODULE, config_schema, default_schema()).

force_clear_from_ct(HostType) ->
catch mod_muc_light_cache:force_clear(HostType),
mod_muc_light_db_backend:force_clear(HostType).

%%====================================================================
Expand Down Expand Up @@ -186,6 +188,15 @@ stop(HostType) ->
mod_muc_light_db_backend:stop(HostType),
ok.

-spec deps(mongooseim:host_type(), gen_mod:module_opts()) -> gen_mod:deps_list().
deps(_HostType, Opts) ->
case gen_mod:get_opt(cache_affs, Opts, undefined) of
undefined ->
[];
CacheOpts ->
[{mod_muc_light_cache, CacheOpts, hard}]
end.

%% Init helpers
subdomain_pattern(HostType) ->
gen_mod:get_module_opt(HostType, ?MODULE, host, default_host()).
Expand All @@ -207,6 +218,7 @@ config_spec() ->
#section{
items = #{<<"backend">> => #option{type = atom,
validate = {module, mod_muc_light_db}},
<<"cache_affs">> => mongoose_user_cache:config_spec(),
<<"host">> => #option{type = string,
validate = subdomain_template,
process = fun mongoose_subdomain_utils:make_subdomain_pattern/1},
Expand Down Expand Up @@ -260,6 +272,7 @@ hooks(HostType) ->
[{is_muc_room_owner, HostType, ?MODULE, is_muc_room_owner, 50},
{can_access_room, HostType, ?MODULE, can_access_room, 50},
{acc_room_affiliations, HostType, ?MODULE, acc_room_affiliations, 50},
{room_exists, HostType, ?MODULE, room_exists, 50},
{can_access_identity, HostType, ?MODULE, can_access_identity, 50},
%% Prevent sending service-unavailable on groupchat messages
{offline_groupchat_message_hook, HostType, ?MODULE, prevent_service_unavailable, 90},
Expand Down Expand Up @@ -329,7 +342,7 @@ process_decoded_packet(_HostType, From, To, Acc, El,
process_decoded_packet(HostType, From, To, Acc, El,
{ok, RequestToRoom})
when To#jid.luser =/= <<>> ->
case mod_muc_light_db_backend:room_exists(HostType, jid:to_lus(To)) of
case mongoose_hooks:room_exists(HostType, To) of
true -> mod_muc_light_room:handle_request(From, To, El, RequestToRoom, Acc);
false -> make_err(From, To, El, Acc, {error, item_not_found})
end;
Expand Down Expand Up @@ -388,7 +401,7 @@ remove_user(Acc, User, Server) ->
Acc;
AffectedRooms ->
bcast_removed_user(Acc, UserJid, AffectedRooms, Version),
maybe_forget_rooms(Acc, AffectedRooms),
maybe_forget_rooms(Acc, AffectedRooms, Version),
Acc
end.

Expand Down Expand Up @@ -505,6 +518,10 @@ acc_room_affiliations(Acc1, Room) ->
Acc1
end.

-spec room_exists(boolean(), mongooseim:host_type(), jid:jid()) -> boolean().
room_exists(_, HostType, RoomJid) ->
mod_muc_light_db_backend:room_exists(HostType, jid:to_lus(RoomJid)).

-spec get_room_affiliations_from_acc(mongoose_acc:t()) ->
{ok, aff_users(), binary()} | {error, not_exists}.
get_room_affiliations_from_acc(Acc) ->
Expand All @@ -513,7 +530,7 @@ get_room_affiliations_from_acc(Acc) ->
-spec get_room_affiliations_from_acc(mongoose_acc:t(), jid:jid()) ->
{mongoose_acc:t(), {ok, aff_users(), binary()} | {error, not_exists}}.
get_room_affiliations_from_acc(Acc1, RoomJid) ->
Acc2 = acc_room_affiliations(Acc1, RoomJid),
Acc2 = mongoose_hooks:acc_room_affiliations(Acc1, RoomJid),
{Acc2, mongoose_acc:get(?MODULE, affiliations, {error, not_exists}, Acc2)}.

-spec can_access_identity(Acc :: boolean(), HostType :: mongooseim:host_type(),
Expand Down Expand Up @@ -789,12 +806,13 @@ bcast_removed_user(Acc, UserJID, [{{RoomU, RoomS} = _RoomUS, Error} | RAffected]
bcast_removed_user(Acc, UserJID, RAffected, Version, ID).

-spec maybe_forget_rooms(Acc :: mongoose_acc:t(),
AffectedRooms :: mod_muc_light_db_backend:remove_user_return()) -> ok.
maybe_forget_rooms(_Acc, []) ->
AffectedRooms :: mod_muc_light_db_backend:remove_user_return(),
Version :: binary()) -> ok.
maybe_forget_rooms(_Acc, [], _) ->
ok;
maybe_forget_rooms(Acc, [{RoomUS, {ok, _, NewAffUsers, _, _}} | RAffectedRooms]) ->
mod_muc_light_room:maybe_forget(Acc, RoomUS, NewAffUsers),
maybe_forget_rooms(Acc, RAffectedRooms).
maybe_forget_rooms(Acc, [{RoomUS, {ok, _, NewAffUsers, _, _}} | RAffectedRooms], Version) ->
mod_muc_light_room:maybe_forget(Acc, RoomUS, NewAffUsers, Version),
maybe_forget_rooms(Acc, RAffectedRooms, Version).

make_handler_fun(Acc) ->
fun(From, To, Packet) -> ejabberd_router:route(From, To, Acc, Packet) end.
Expand Down
138 changes: 138 additions & 0 deletions src/muc_light/mod_muc_light_cache.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
-module(mod_muc_light_cache).

-behaviour(gen_mod).
-define(FRONTEND, mod_muc_light).

%% gen_mod callbacks
-export([start/2, stop/1, supported_features/0]).

%% Hook handlers
-export([pre_acc_room_affiliations/2, post_acc_room_affiliations/2,
pre_room_exists/3, post_room_exists/3,
forget_room/4, remove_domain/3, room_new_affiliations/4]).
-ignore_xref([pre_acc_room_affiliations/2, post_acc_room_affiliations/2,
pre_room_exists/3, post_room_exists/3,
forget_room/4, remove_domain/3, room_new_affiliations/4]).

%% For tests
-export([force_clear/1]).

-spec start(mongooseim:host_type(), gen_mod:module_opts()) -> ok.
start(HostType, Opts) ->
start_cache(HostType, Opts),
ejabberd_hooks:add(hooks(HostType)),
ok.

-spec stop(HostType :: mongooseim:host_type()) -> ok.
stop(HostType) ->
ejabberd_hooks:delete(hooks(HostType)),
stop_cache(HostType),
ok.

-spec supported_features() -> [atom()].
supported_features() ->
[dynamic_domains].

-spec hooks(mongooseim:host_type()) -> [ejabberd_hooks:hook()].
hooks(HostType) ->
[
{acc_room_affiliations, HostType, ?MODULE, pre_acc_room_affiliations, 40},
{acc_room_affiliations, HostType, ?MODULE, post_acc_room_affiliations, 60},
{room_exists, HostType, ?MODULE, pre_room_exists, 40},
{room_exists, HostType, ?MODULE, post_room_exists, 60},
{forget_room, HostType, ?MODULE, forget_room, 80},
{remove_domain, HostType, ?MODULE, remove_domain, 20},
{room_new_affiliations, HostType, ?MODULE, room_new_affiliations, 50}
].

-spec pre_acc_room_affiliations(mongoose_acc:t(), jid:jid()) ->
mongoose_acc:t() | {stop, mongoose_acc:t()}.
pre_acc_room_affiliations(Acc, RoomJid) ->
case mongoose_acc:get(?FRONTEND, affiliations, {error, not_exists}, Acc) of
{error, _} ->
HostType = mongoose_acc:host_type(Acc),
case mongoose_user_cache:get_entry(HostType, ?MODULE, RoomJid) of
#{affs := Res} ->
mongoose_acc:set(?FRONTEND, affiliations, Res, Acc);
_ ->
Acc
end;
_Res ->
{stop, Acc}
end.

-spec post_acc_room_affiliations(mongoose_acc:t(), jid:jid()) -> mongoose_acc:t().
post_acc_room_affiliations(Acc, RoomJid) ->
case mongoose_acc:get(?FRONTEND, affiliations, {error, not_exists}, Acc) of
{error, _} ->
Acc;
Res ->
HostType = mongoose_acc:host_type(Acc),
mongoose_user_cache:merge_entry(HostType, ?MODULE, RoomJid, #{affs => Res}),
Acc
end.

-spec pre_room_exists(boolean(), mongooseim:host_type(), jid:jid()) ->
boolean() | {stop, true}.
pre_room_exists(false, HostType, RoomJid) ->
case mongoose_user_cache:is_member(HostType, ?MODULE, RoomJid) of
true -> {stop, true};
false -> false
end;
pre_room_exists(Status, _, _) ->
Status.

-spec post_room_exists(boolean(), mongooseim:host_type(), jid:jid()) ->
boolean().
post_room_exists(true, HostType, RoomJid) ->
mongoose_user_cache:merge_entry(HostType, ?MODULE, RoomJid, #{}),
true;
post_room_exists(Status, _, _) ->
Status.

-spec forget_room(mongoose_hooks:simple_acc(), mongooseim:host_type(), jid:lserver(), binary()) ->
mongoose_hooks:simple_acc().
forget_room(Acc, HostType, RoomS, RoomU) ->
mongoose_user_cache:delete_user(HostType, ?MODULE, jid:make_noprep(RoomU, RoomS, <<>>)),
Acc.

-spec remove_domain(mongoose_hooks:simple_acc(), mongooseim:host_type(), jid:lserver()) ->
mongoose_hooks:simple_acc().
remove_domain(Acc, HostType, Domain) ->
MUCHost = mod_muc_light:server_host_to_muc_host(HostType, Domain),
mongoose_user_cache:delete_domain(HostType, ?MODULE, MUCHost),
Acc.

-spec room_new_affiliations(mongoose_acc:t(), jid:jid(), mod_muc_light:aff_users(), binary()) ->
mongoose_acc:t().
room_new_affiliations(Acc, RoomJid, NewAffs, NewVersion) ->
HostType = mod_muc_light_utils:acc_to_host_type(Acc),
% make sure other nodes forget about stale values
mongoose_user_cache:delete_user(HostType, ?MODULE, RoomJid),
mongoose_user_cache:merge_entry(HostType, ?MODULE, RoomJid, #{affs => {ok, NewAffs, NewVersion}}),
Acc.

-spec force_clear(mongooseim:host_type()) -> ok.
force_clear(HostType) ->
CacheName = gen_mod:get_module_proc(HostType, ?MODULE),
segmented_cache:delete_pattern(CacheName, {'_', '_'}),
ok.

%%====================================================================
%% internal
%%====================================================================
-spec start_cache(mongooseim:host_type(), gen_mod:module_opts()) -> any().
start_cache(HostType, Opts) ->
FinalOpts = maps:to_list(maps:merge(defaults(), maps:from_list(Opts))),
mongoose_user_cache:start_new_cache(HostType, ?MODULE, FinalOpts).

defaults() ->
#{module => internal,
strategy => fifo,
time_to_live => 2,
number_of_segments => 3
}.

-spec stop_cache(mongooseim:host_type()) -> any().
stop_cache(HostType) ->
mongoose_user_cache:stop_cache(HostType, ?MODULE).
16 changes: 10 additions & 6 deletions src/muc_light/mod_muc_light_room.erl
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
-author('[email protected]').

%% API
-export([handle_request/5, maybe_forget/3, process_request/5]).
-export([handle_request/5, maybe_forget/4, process_request/5]).

%% Callbacks
-export([participant_limit_check/2]).
Expand All @@ -48,12 +48,16 @@ handle_request(From, Room, OrigPacket, Request, Acc1) ->
send_response(From, Room, OrigPacket, Response, Acc2).

-spec maybe_forget(Acc :: mongoose_acc:t(),
RoomUS :: jid:simple_bare_jid(), NewAffUsers :: aff_users()) -> any().
maybe_forget(Acc, {RoomU, RoomS} = RoomUS, []) ->
RoomUS :: jid:simple_bare_jid(),
NewAffUsers :: aff_users(),
Version :: binary() ) -> any().
maybe_forget(Acc, {RoomU, RoomS} = RoomUS, [], _Version) ->
HostType = mod_muc_light_utils:acc_to_host_type(Acc),
mongoose_hooks:forget_room(HostType, RoomS, RoomU),
mod_muc_light_db_backend:destroy_room(HostType, RoomUS);
maybe_forget(_Acc, _, _) ->
maybe_forget(Acc, {RoomU, RoomS}, NewAffs, Version) ->
RoomJid = jid:make_noprep(RoomU, RoomS, <<>>),
mongoose_hooks:room_new_affiliations(Acc, RoomJid, NewAffs, Version),
my_room_will_go_on.

%%====================================================================
Expand Down Expand Up @@ -150,7 +154,7 @@ process_request({set, #destroy{} = DestroyReq},
_From, RoomUS, {_, owner}, AffUsers, Acc) ->
HostType = mongoose_acc:host_type(Acc),
ok = mod_muc_light_db_backend:destroy_room(HostType, RoomUS),
maybe_forget(Acc, RoomUS, []),
maybe_forget(Acc, RoomUS, [], <<>>),
{set, DestroyReq, AffUsers};
process_request({set, #destroy{}},
_From, _RoomUS, _Auth, _AffUsers, _Acc) ->
Expand Down Expand Up @@ -229,7 +233,7 @@ process_aff_set(AffReq, RoomUS, {ok, FilteredAffUsers}, Acc) ->
case mod_muc_light_db_backend:modify_aff_users(HostType, RoomUS, FilteredAffUsers,
fun ?MODULE:participant_limit_check/2, NewVersion) of
{ok, OldAffUsers, NewAffUsers, AffUsersChanged, OldVersion} ->
maybe_forget(Acc, RoomUS, NewAffUsers),
maybe_forget(Acc, RoomUS, NewAffUsers, NewVersion),
{set, AffReq#affiliations{
prev_version = OldVersion,
version = NewVersion,
Expand Down
Loading

0 comments on commit 3bf8282

Please sign in to comment.