diff options
author | Paweł Chmielowski <pchmielowski@process-one.net> | 2021-04-16 10:34:00 +0200 |
---|---|---|
committer | Paweł Chmielowski <pchmielowski@process-one.net> | 2021-04-16 10:34:32 +0200 |
commit | 5b0f0d8352d729d953d84d2119989e3b93ca0139 (patch) | |
tree | a78885d73cebb75655e163117eabbc77e7ee1280 /src/mod_shared_roster.erl | |
parent | Use proper source for cache options in mod_shared_roster (diff) |
Improve database and caching in mod_shared_roster
This makes us keep cache of groups that use wildcards no matter
of cache settings, and tries to not same fetch data multiple times
in roster get operations.
Diffstat (limited to 'src/mod_shared_roster.erl')
-rw-r--r-- | src/mod_shared_roster.erl | 335 |
1 files changed, 170 insertions, 165 deletions
diff --git a/src/mod_shared_roster.erl b/src/mod_shared_roster.erl index 422173b70..8f0f8ee28 100644 --- a/src/mod_shared_roster.erl +++ b/src/mod_shared_roster.erl @@ -150,18 +150,17 @@ depends(_Host, _Opts) -> -spec init_cache(module(), binary(), gen_mod:opts()) -> ok. init_cache(Mod, Host, Opts) -> + ets_cache:new(?SPECIAL_GROUPS_CACHE, [{max_size, 4}]), case use_cache(Mod, Host) of true -> - CacheOpts = cache_opts(Opts), - ets_cache:new(?GROUP_OPTS_CACHE, CacheOpts), + CacheOpts = cache_opts(Opts), + ets_cache:new(?GROUP_OPTS_CACHE, CacheOpts), ets_cache:new(?USER_GROUPS_CACHE, CacheOpts), - ets_cache:new(?GROUP_EXPLICIT_USERS_CACHE, CacheOpts), - ets_cache:new(?SPECIAL_GROUPS_CACHE, CacheOpts); + ets_cache:new(?GROUP_EXPLICIT_USERS_CACHE, CacheOpts); false -> ets_cache:delete(?GROUP_OPTS_CACHE), ets_cache:delete(?USER_GROUPS_CACHE), - ets_cache:delete(?GROUP_EXPLICIT_USERS_CACHE), - ets_cache:delete(?SPECIAL_GROUPS_CACHE) + ets_cache:delete(?GROUP_EXPLICIT_USERS_CACHE) end. -spec cache_opts(gen_mod:opts()) -> [proplists:property()]. @@ -186,45 +185,36 @@ cache_nodes(Mod, Host) -> end. -spec get_user_roster([#roster{}], {binary(), binary()}) -> [#roster{}]. -get_user_roster(Items, US) -> - {U, S} = US, - DisplayedGroups = get_user_displayed_groups(US), - SRUsers = lists:foldl(fun (Group, Acc1) -> - GroupLabel = get_group_label(S, Group), %++ - lists:foldl(fun (User, Acc2) -> - if User == US -> Acc2; - true -> - dict:append(User, - GroupLabel, - Acc2) - end - end, - Acc1, get_group_users(S, Group)) - end, - dict:new(), DisplayedGroups), - {NewItems1, SRUsersRest} = lists:mapfoldl(fun (Item, - SRUsers1) -> - {_, _, {U1, S1, _}} = - Item#roster.usj, - US1 = {U1, S1}, - case dict:find(US1, - SRUsers1) - of - {ok, GroupLabels} -> - {Item#roster{subscription - = - both, - groups = - Item#roster.groups ++ GroupLabels, - ask = - none}, - dict:erase(US1, - SRUsers1)}; - error -> - {Item, SRUsers1} - end - end, - SRUsers, Items), +get_user_roster(Items, {U, S} = US) -> + {DisplayedGroups, Cache} = get_user_displayed_groups(US), + SRUsers = lists:foldl( + fun(Group, Acc1) -> + GroupLabel = get_group_label_cached(S, Group, Cache), + lists:foldl( + fun(User, Acc2) -> + if User == US -> Acc2; + true -> + dict:append(User, GroupLabel, Acc2) + end + end, + Acc1, get_group_users_cached(S, Group, Cache)) + end, + dict:new(), DisplayedGroups), + {NewItems1, SRUsersRest} = lists:mapfoldl( + fun(Item, SRUsers1) -> + {_, _, {U1, S1, _}} = Item#roster.usj, + US1 = {U1, S1}, + case dict:find(US1, SRUsers1) of + {ok, GroupLabels} -> + {Item#roster{subscription = both, + groups = Item#roster.groups ++ GroupLabels, + ask = none}, + dict:erase(US1, SRUsers1)}; + error -> + {Item, SRUsers1} + end + end, + SRUsers, Items), SRItems = [#roster{usj = {U, S, {U1, S1, <<"">>}}, us = US, jid = {U1, S1, <<"">>}, name = get_rosteritem_name(U1, S1), @@ -261,7 +251,7 @@ process_item(RosterItem, Host) -> {UserTo, ServerTo, ResourceTo} = RosterItem#roster.jid, NameTo = RosterItem#roster.name, USTo = {UserTo, ServerTo}, - DisplayedGroups = get_user_displayed_groups(USFrom), + {DisplayedGroups, Cache} = get_user_displayed_groups(USFrom), CommonGroups = lists:filter(fun (Group) -> is_user_in_group(USTo, Group, Host) end, @@ -271,7 +261,7 @@ process_item(RosterItem, Host) -> %% Roster item cannot be removed: We simply reset the original groups: _ when RosterItem#roster.subscription == remove -> GroupLabels = lists:map(fun (Group) -> - get_group_label(Host, Group) + get_group_label_cached(Host, Group, Cache) end, CommonGroups), RosterItem#roster{subscription = both, ask = none, @@ -352,18 +342,16 @@ get_jid_info({Subscription, Ask, Groups}, User, Server, US = {LUser, LServer}, {U1, S1, _} = jid:tolower(JID), US1 = {U1, S1}, - DisplayedGroups = get_user_displayed_groups(US), - SRUsers = lists:foldl(fun (Group, Acc1) -> - GroupLabel = get_group_label(LServer, Group), %++ - lists:foldl(fun (User1, Acc2) -> - dict:append(User1, - GroupLabel, - Acc2) - end, - Acc1, - get_group_users(LServer, Group)) - end, - dict:new(), DisplayedGroups), + {DisplayedGroups, Cache} = get_user_displayed_groups(US), + SRUsers = lists:foldl( + fun(Group, Acc1) -> + GroupLabel = get_group_label_cached(LServer, Group, Cache), %++ + lists:foldl( + fun(User1, Acc2) -> + dict:append(User1, GroupLabel, Acc2) + end, Acc1, get_group_users_cached(LServer, Group, Cache)) + end, + dict:new(), DisplayedGroups), case dict:find(US1, SRUsers) of {ok, GroupLabels} -> NewGroups = if Groups == [] -> GroupLabels; @@ -398,7 +386,7 @@ process_subscription(Direction, User, Server, JID, {U1, S1, _} = jid:tolower(jid:remove_resource(JID)), US1 = {U1, S1}, - DisplayedGroups = get_user_displayed_groups(US), + {DisplayedGroups, _} = get_user_displayed_groups(US), SRUsers = lists:usort(lists:flatmap(fun (Group) -> get_group_users(LServer, Group) end, @@ -425,10 +413,16 @@ create_group(Host, Group) -> create_group(Host, Group, Opts) -> Mod = gen_mod:db_mod(Host, ?MODULE), + case proplists:get_value(all_users, Opts, false) orelse + proplists:get_value(online_users, Opts, false) of + true -> + update_wildcard_cache(Host, Group, Opts); + _ -> + ok + end, case use_cache(Mod, Host) of true -> - ets_cache:insert(?GROUP_OPTS_CACHE, {Host, Group}, Opts, cache_nodes(Mod, Host)), - ets_cache:clear(?SPECIAL_GROUPS_CACHE, cache_nodes(Mod, Host)); + ets_cache:insert(?GROUP_OPTS_CACHE, {Host, Group}, Opts, cache_nodes(Mod, Host)); _ -> ok end, @@ -436,19 +430,33 @@ create_group(Host, Group, Opts) -> delete_group(Host, Group) -> Mod = gen_mod:db_mod(Host, ?MODULE), + update_wildcard_cache(Host, Group, []), case use_cache(Mod, Host) of true -> ets_cache:delete(?GROUP_OPTS_CACHE, {Host, Group}, cache_nodes(Mod, Host)), ets_cache:clear(?USER_GROUPS_CACHE, cache_nodes(Mod, Host)), - ets_cache:clear(?GROUP_EXPLICIT_USERS_CACHE, cache_nodes(Mod, Host)), - ets_cache:clear(?SPECIAL_GROUPS_CACHE, cache_nodes(Mod, Host)); + ets_cache:delete(?GROUP_EXPLICIT_USERS_CACHE, {Host, Group}, cache_nodes(Mod, Host)); _ -> ok end, Mod:delete_group(Host, Group). +get_groups_opts_cached(Host1, Group1, Cache) -> + {Host, Group} = split_grouphost(Host1, Group1), + case Cache of + #{{Group, Host} := Opts} -> + {Opts, Cache}; + _ -> + Opts = get_group_opts_int(Host, Group), + {Opts, Cache#{{Group, Host} => Opts}} + end. + get_group_opts(Host1, Group1) -> {Host, Group} = split_grouphost(Host1, Group1), + get_group_opts_int(Host, Group). + +get_group_opts_int(Host1, Group1) -> + {Host, Group} = split_grouphost(Host1, Group1), Mod = gen_mod:db_mod(Host, ?MODULE), Res = case use_cache(Mod, Host) of true -> @@ -470,11 +478,11 @@ get_group_opts(Host1, Group1) -> set_group_opts(Host, Group, Opts) -> Mod = gen_mod:db_mod(Host, ?MODULE), + update_wildcard_cache(Host, Group, Opts), case use_cache(Mod, Host) of true -> ets_cache:delete(?GROUP_OPTS_CACHE, {Host, Group}, cache_nodes(Mod, Host)), - ets_cache:insert(?GROUP_OPTS_CACHE, {Host, Group}, Opts, cache_nodes(Mod, Host)), - ets_cache:clear(?SPECIAL_GROUPS_CACHE, cache_nodes(Mod, Host)); + ets_cache:insert(?GROUP_OPTS_CACHE, {Host, Group}, Opts, cache_nodes(Mod, Host)); _ -> ok end, @@ -493,13 +501,13 @@ get_user_groups(US) -> false -> Mod:get_user_groups(US, Host) end, - UG ++ get_special_users_groups(Host). + UG ++ get_groups_with_wildcards(Host, both). -is_group_enabled(Host1, Group1) -> - {Host, Group} = split_grouphost(Host1, Group1), - case get_group_opts(Host, Group) of - error -> false; - Opts -> not lists:member(disabled, Opts) +get_group_opt_cached(Host, Group, Opt, Default, Cache) -> + case get_groups_opts_cached(Host, Group, Cache) of + {error, _} -> Default; + {Opts, _} -> + proplists:get_value(Opt, Opts, Default) end. %% @spec (Host::string(), Group::string(), Opt::atom(), Default) -> OptValue | Default @@ -507,16 +515,17 @@ get_group_opt(Host, Group, Opt, Default) -> case get_group_opts(Host, Group) of error -> Default; Opts -> - case lists:keysearch(Opt, 1, Opts) of - {value, {_, Val}} -> Val; - false -> Default - end + proplists:get_value(Opt, Opts, Default) end. get_online_users(Host) -> lists:usort([{U, S} || {U, S, _} <- ejabberd_sm:get_vh_session_list(Host)]). +get_group_users_cached(Host, Group, Cache) -> + {Opts, _} = get_groups_opts_cached(Host, Group, Cache), + get_group_users(Host, Group, Opts). + get_group_users(Host1, Group1) -> {Host, Group} = split_grouphost(Host1, Group1), get_group_users(Host, Group, get_group_opts(Host, Group)). @@ -547,82 +556,69 @@ get_group_explicit_users(Host, Group) -> Mod:get_group_explicit_users(Host, Group) end. -get_group_label(Host1, Group1) -> - {Host, Group} = split_grouphost(Host1, Group1), - get_group_opt(Host, Group, label, Group). - -%% Get list of names of groups that have @all@/@online@/etc in the memberlist -get_special_users_groups(Host) -> - Extract = - fun() -> - lists:filtermap( - fun({Group, Opts}) -> - case proplists:get_value(all_users, Opts, false) orelse - proplists:get_value(online_users, Opts, false) of - true -> {true, Group}; - false -> false - end - end, - groups_with_opts(Host)) - end, +get_group_label_cached(Host, Group, Cache) -> + get_group_opt_cached(Host, Group, label, Group, Cache). + +-spec update_wildcard_cache(binary(), binary(), list()) -> ok. +update_wildcard_cache(Host, Group, NewOpts) -> Mod = gen_mod:db_mod(Host, ?MODULE), - case use_cache(Mod, Host) of - true -> - ets_cache:lookup( - ?SPECIAL_GROUPS_CACHE, {Host, false}, - fun() -> - {cache, Extract()} - end); - false -> - Extract() - end. + Online = get_groups_with_wildcards(Host, online), + Both = get_groups_with_wildcards(Host, both), + IsOnline = proplists:get_value(online_users, NewOpts, false), + IsAll = proplists:get_value(all_users, NewOpts, false), + OnlineUpdated = lists:member(Group, Online) /= IsOnline, + BothUpdated = lists:member(Group, Both) /= (IsOnline orelse IsAll), -%% Get list of names of groups that have @online@ in the memberlist -get_special_users_groups_online(Host) -> - Extract = - fun() -> - lists:filtermap( - fun({Group, Opts}) -> - case proplists:get_value(online_users, Opts, false) of - true -> {true, Group}; - false -> false - end - end, - groups_with_opts(Host)) + if + OnlineUpdated -> + NewOnline = case IsOnline of + true -> [Group | Online]; + _ -> Online -- [Group] + end, + ets_cache:update(?SPECIAL_GROUPS_CACHE, {Host, online}, + NewOnline, fun() -> ok end, cache_nodes(Mod, Host)); + true -> ok end, - Mod = gen_mod:db_mod(Host, ?MODULE), - case use_cache(Mod, Host) of - true -> - ets_cache:lookup( - ?SPECIAL_GROUPS_CACHE, {Host, true}, - fun() -> - {cache, Extract()} - end); - false -> - Extract() - end. + if + BothUpdated -> + NewBoth = case IsOnline orelse IsAll of + true -> [Group | Both]; + _ -> Both -- [Group] + end, + ets_cache:update(?SPECIAL_GROUPS_CACHE, {Host, both}, + NewBoth, fun() -> ok end, cache_nodes(Mod, Host)); + true -> ok + end, + ok. + +-spec get_groups_with_wildcards(binary(), online | both) -> list(binary()). +get_groups_with_wildcards(Host, Type) -> + ets_cache:lookup( + ?SPECIAL_GROUPS_CACHE, {Host, Type}, + fun() -> + Res = lists:filtermap( + fun({Group, Opts}) -> + case proplists:get_value(online_users, Opts, false) orelse + (Type == both andalso proplists:get_value(all_users, Opts, false)) of + true -> {true, Group}; + false -> false + end + end, + groups_with_opts(Host)), + {cache, Res} + end). %% Given two lists of groupnames and their options, %% return the list of displayed groups to the second list displayed_groups(GroupsOpts, SelectedGroupsOpts) -> - DisplayedGroups = lists:usort(lists:flatmap(fun - ({_Group, Opts}) -> - [G - || G - <- proplists:get_value(displayed_groups, - Opts, - []), - not - lists:member(disabled, - Opts)] - end, - SelectedGroupsOpts)), - [G - || G <- DisplayedGroups, - not - lists:member(disabled, - proplists:get_value(G, GroupsOpts, []))]. + DisplayedGroups = lists:usort(lists:flatmap( + fun + ({_Group, Opts}) -> + [G || G <- proplists:get_value(displayed_groups, Opts, []), + not lists:member(disabled, Opts)] + end, SelectedGroupsOpts)), + [G || G <- DisplayedGroups, not lists:member(disabled, proplists:get_value(G, GroupsOpts, []))]. %% Given a list of group names with options, %% for those that have @all@ in memberlist, @@ -645,24 +641,35 @@ get_user_displayed_groups(LUser, LServer, GroupsOpts) -> %% @doc Get the list of groups that are displayed to this user get_user_displayed_groups(US) -> Host = element(2, US), - DisplayedGroups1 = lists:usort(lists:flatmap(fun - (Group) -> - case - is_group_enabled(Host, - Group) - of - true -> - get_group_opt(Host, - Group, - displayed_groups, - []); - false -> [] - end - end, - get_user_groups(US))), - [Group - || Group <- DisplayedGroups1, - is_group_enabled(Host, Group)]. + {Groups, Cache} = + lists:foldl( + fun(Group, {Groups, Cache}) -> + case get_groups_opts_cached(Host, Group, Cache) of + {error, Cache2} -> + {Groups, Cache2}; + {Opts, Cache3} -> + case lists:member(disabled, Opts) of + false -> + {proplists:get_value(displayed_groups, Opts, []) ++ Groups, Cache3}; + _ -> + {Groups, Cache3} + end + end + end, {[], #{}}, get_user_groups(US)), + lists:foldl( + fun(Group, {Groups0, Cache0}) -> + case get_groups_opts_cached(Host, Group, Cache0) of + {error, Cache1} -> + {Groups0, Cache1}; + {Opts, Cache2} -> + case lists:member(disabled, Opts) of + false -> + {[Group|Groups0], Cache2}; + _ -> + {Groups0, Cache2} + end + end + end, {[], Cache}, lists:usort(Groups)). is_user_in_group(US, Group, Host) -> Mod = gen_mod:db_mod(Host, ?MODULE), @@ -865,7 +872,6 @@ unset_presence(LUser, LServer, Resource, Status) -> [LUser, LServer, Resource, Status, length(Resources)]), case length(Resources) of 0 -> - OnlineGroups = get_special_users_groups_online(LServer), lists:foreach( fun(OG) -> DisplayedToGroups = displayed_to_groups(OG, LServer), @@ -873,8 +879,7 @@ unset_presence(LUser, LServer, Resource, Status) -> LServer, remove, DisplayedToGroups), push_displayed_to_user(LUser, LServer, LServer, remove, DisplayedToGroups) - end, - OnlineGroups); + end, get_groups_with_wildcards(LServer, online)); _ -> ok end. |