mirror of
https://github.com/processone/ejabberd.git
synced 2024-11-22 16:20:52 +01:00
mod_caps: reduce memory and remove mnesia lock
SVN Revision: 2050
This commit is contained in:
parent
5ae96e4065
commit
0ed0c45aba
@ -17,6 +17,9 @@
|
|||||||
* src/mod_pubsub/gen_pubsub_node.erl: Likewise
|
* src/mod_pubsub/gen_pubsub_node.erl: Likewise
|
||||||
* src/mod_pubsub/gen_pubsub_nodetree.erl: Likewise
|
* src/mod_pubsub/gen_pubsub_nodetree.erl: Likewise
|
||||||
|
|
||||||
|
* src/mod_caps.erl: Reduce memory consumption and remove mnesia table
|
||||||
|
locking, improving performances
|
||||||
|
|
||||||
2009-04-28 Badlop <badlop@process-one.net>
|
2009-04-28 Badlop <badlop@process-one.net>
|
||||||
|
|
||||||
* src/ejabberd_hooks.erl: Support distributed hooks (EJAB-829)
|
* src/ejabberd_hooks.erl: Support distributed hooks (EJAB-829)
|
||||||
|
@ -22,6 +22,8 @@
|
|||||||
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||||
%%% 02111-1307 USA
|
%%% 02111-1307 USA
|
||||||
%%%
|
%%%
|
||||||
|
%%% 2009, improvements from Process-One to support correct PEP handling
|
||||||
|
%%% through s2s, use less memory, and speedup global caps handling
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
|
|
||||||
-module(mod_caps).
|
-module(mod_caps).
|
||||||
@ -63,7 +65,7 @@
|
|||||||
-define(CAPS_QUERY_TIMEOUT, 60000). % 1mn without answer, consider client never answer
|
-define(CAPS_QUERY_TIMEOUT, 60000). % 1mn without answer, consider client never answer
|
||||||
|
|
||||||
-record(caps, {node, version, exts}).
|
-record(caps, {node, version, exts}).
|
||||||
-record(caps_features, {node_pair, features}).
|
-record(caps_features, {node_pair, features = []}).
|
||||||
-record(user_caps, {jid, caps}).
|
-record(user_caps, {jid, caps}).
|
||||||
-record(user_caps_resources, {uid, resource}).
|
-record(user_caps_resources, {uid, resource}).
|
||||||
-record(state, {host,
|
-record(state, {host,
|
||||||
@ -100,7 +102,7 @@ read_caps([], Result) ->
|
|||||||
|
|
||||||
%% get_caps reads user caps from database
|
%% get_caps reads user caps from database
|
||||||
get_caps(JID) ->
|
get_caps(JID) ->
|
||||||
case catch mnesia:dirty_read({user_caps, list_to_binary(jlib:jid_to_string(JID))}) of
|
case catch mnesia:dirty_read({user_caps, jid_to_binary(JID)}) of
|
||||||
[#user_caps{caps=Caps}] ->
|
[#user_caps{caps=Caps}] ->
|
||||||
Caps;
|
Caps;
|
||||||
_ ->
|
_ ->
|
||||||
@ -110,16 +112,15 @@ get_caps(JID) ->
|
|||||||
%% clear_caps removes user caps from database
|
%% clear_caps removes user caps from database
|
||||||
clear_caps(JID) ->
|
clear_caps(JID) ->
|
||||||
{U, S, R} = jlib:jid_tolower(JID),
|
{U, S, R} = jlib:jid_tolower(JID),
|
||||||
BJID = list_to_binary(jlib:jid_to_string(JID)),
|
BJID = jid_to_binary(JID),
|
||||||
BUID = list_to_binary(jlib:jid_to_string({U, S, []})),
|
BUID = jid_to_binary({U, S, []}),
|
||||||
catch mnesia:dirty_delete({user_caps, BJID}),
|
catch mnesia:dirty_delete({user_caps, BJID}),
|
||||||
catch mnesia:dirty_delete_object(#user_caps_resources{uid = BUID, resource = list_to_binary(R)}),
|
catch mnesia:dirty_delete_object(#user_caps_resources{uid = BUID, resource = list_to_binary(R)}),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
%% give default user resource
|
%% give default user resource
|
||||||
get_user_resources(LUser, LServer) ->
|
get_user_resources(LUser, LServer) ->
|
||||||
BUID = list_to_binary(jlib:jid_to_string({LUser, LServer, []})),
|
case catch mnesia:dirty_read({user_caps_resources, jid_to_binary({LUser, LServer, []})}) of
|
||||||
case catch mnesia:dirty_read({user_caps_resources, BUID}) of
|
|
||||||
{'EXIT', _} ->
|
{'EXIT', _} ->
|
||||||
[];
|
[];
|
||||||
Resources ->
|
Resources ->
|
||||||
@ -196,19 +197,32 @@ receive_packet(_, _, _) ->
|
|||||||
receive_packet(_JID, From, To, Packet) ->
|
receive_packet(_JID, From, To, Packet) ->
|
||||||
receive_packet(From, To, Packet).
|
receive_packet(From, To, Packet).
|
||||||
|
|
||||||
|
jid_to_binary(JID) ->
|
||||||
|
list_to_binary(jlib:jid_to_string(JID)).
|
||||||
|
|
||||||
|
caps_to_binary(#caps{node = Node, version = Version, exts = Exts}) ->
|
||||||
|
BExts = [list_to_binary(Ext) || Ext <- Exts],
|
||||||
|
#caps{node = list_to_binary(Node), version = list_to_binary(Version), exts = BExts}.
|
||||||
|
|
||||||
|
node_to_binary(Node, SubNode) ->
|
||||||
|
{list_to_binary(Node), list_to_binary(SubNode)}.
|
||||||
|
|
||||||
|
features_to_binary(L) -> [list_to_binary(I) || I <- L].
|
||||||
|
binary_to_features(L) -> [binary_to_list(I) || I <- L].
|
||||||
|
|
||||||
%%====================================================================
|
%%====================================================================
|
||||||
%% gen_server callbacks
|
%% gen_server callbacks
|
||||||
%%====================================================================
|
%%====================================================================
|
||||||
|
|
||||||
init([Host, _Opts]) ->
|
init([Host, _Opts]) ->
|
||||||
mnesia:create_table(caps_features,
|
mnesia:create_table(caps_features,
|
||||||
[{ram_copies, [node()]},
|
[{disc_copies, [node()]},
|
||||||
{attributes, record_info(fields, caps_features)}]),
|
{attributes, record_info(fields, caps_features)}]),
|
||||||
mnesia:create_table(user_caps,
|
mnesia:create_table(user_caps,
|
||||||
[{disc_copies, [node()]},
|
[{ram_copies, [node()]},
|
||||||
{attributes, record_info(fields, user_caps)}]),
|
{attributes, record_info(fields, user_caps)}]),
|
||||||
mnesia:create_table(user_caps_resources,
|
mnesia:create_table(user_caps_resources,
|
||||||
[{disc_copies, [node()]},
|
[{ram_copies, [node()]},
|
||||||
{type, bag},
|
{type, bag},
|
||||||
{attributes, record_info(fields, user_caps_resources)}]),
|
{attributes, record_info(fields, user_caps_resources)}]),
|
||||||
mnesia:delete_table(user_caps_default),
|
mnesia:delete_table(user_caps_default),
|
||||||
@ -220,26 +234,21 @@ init([Host, _Opts]) ->
|
|||||||
|
|
||||||
maybe_get_features(#caps{node = Node, version = Version, exts = Exts}) ->
|
maybe_get_features(#caps{node = Node, version = Version, exts = Exts}) ->
|
||||||
SubNodes = [Version | Exts],
|
SubNodes = [Version | Exts],
|
||||||
F = fun() ->
|
|
||||||
%% Make sure that we have all nodes we need to know.
|
%% Make sure that we have all nodes we need to know.
|
||||||
%% If a single one is missing, we wait for more disco
|
%% If a single one is missing, we wait for more disco
|
||||||
%% responses.
|
%% responses.
|
||||||
lists:foldl(fun(SubNode, Acc) ->
|
case lists:foldl(fun(SubNode, Acc) ->
|
||||||
case Acc of
|
case Acc of
|
||||||
fail -> fail;
|
fail -> fail;
|
||||||
_ ->
|
_ ->
|
||||||
case mnesia:read({caps_features, {Node, SubNode}}) of
|
case mnesia:dirty_read({caps_features, {Node, SubNode}}) of
|
||||||
[] -> fail;
|
[] -> fail;
|
||||||
[#caps_features{features = Features}] -> Features ++ Acc
|
[#caps_features{features = Features}] -> Features ++ Acc %% TODO binary
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end, [], SubNodes)
|
end, [], SubNodes) of
|
||||||
end,
|
fail -> wait;
|
||||||
case mnesia:transaction(F) of
|
Features -> {ok, Features}
|
||||||
{atomic, fail} ->
|
|
||||||
wait;
|
|
||||||
{atomic, Features} ->
|
|
||||||
{ok, Features}
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
timestamp() ->
|
timestamp() ->
|
||||||
@ -249,7 +258,7 @@ timestamp() ->
|
|||||||
handle_call({get_features, Caps}, From, State) ->
|
handle_call({get_features, Caps}, From, State) ->
|
||||||
case maybe_get_features(Caps) of
|
case maybe_get_features(Caps) of
|
||||||
{ok, Features} ->
|
{ok, Features} ->
|
||||||
{reply, Features, State};
|
{reply, binary_to_features(Features), State};
|
||||||
wait ->
|
wait ->
|
||||||
gen_server:cast(self(), visit_feature_queries),
|
gen_server:cast(self(), visit_feature_queries),
|
||||||
Timeout = timestamp() + 10,
|
Timeout = timestamp() + 10,
|
||||||
@ -268,30 +277,29 @@ handle_cast({note_caps, From,
|
|||||||
%% XXX: this leads to race conditions where ejabberd will send
|
%% XXX: this leads to race conditions where ejabberd will send
|
||||||
%% lots of caps disco requests.
|
%% lots of caps disco requests.
|
||||||
{U, S, R} = jlib:jid_tolower(From),
|
{U, S, R} = jlib:jid_tolower(From),
|
||||||
BJID = list_to_binary(jlib:jid_to_string(From)),
|
BJID = jid_to_binary(From),
|
||||||
mnesia:dirty_write(#user_caps{jid = BJID, caps = Caps}),
|
mnesia:dirty_write(#user_caps{jid = BJID, caps = caps_to_binary(Caps)}),
|
||||||
case ejabberd_sm:get_user_resources(U, S) of
|
case ejabberd_sm:get_user_resources(U, S) of
|
||||||
[] ->
|
[] ->
|
||||||
% only store resources of caps aware external contacts
|
% only store resources of caps aware external contacts
|
||||||
BUID = list_to_binary(jlib:jid_to_string(jlib:jid_remove_resource(From))),
|
BUID = jid_to_binary({U, S, []}),
|
||||||
mnesia:dirty_write(#user_caps_resources{uid = BUID, resource = list_to_binary(R)});
|
mnesia:dirty_write(#user_caps_resources{uid = BUID, resource = list_to_binary(R)});
|
||||||
_ ->
|
_ ->
|
||||||
ok
|
ok
|
||||||
end,
|
end,
|
||||||
SubNodes = [Version | Exts],
|
|
||||||
%% Now, find which of these are not already in the database.
|
%% Now, find which of these are not already in the database.
|
||||||
Fun = fun() ->
|
SubNodes = [Version | Exts],
|
||||||
lists:foldl(fun(SubNode, Acc) ->
|
case lists:foldl(fun(SubNode, Acc) ->
|
||||||
case mnesia:read({caps_features, {Node, SubNode}}) of
|
case mnesia:dirty_read({caps_features, node_to_binary(Node, SubNode)}) of
|
||||||
[] ->
|
[] ->
|
||||||
[SubNode | Acc];
|
[SubNode | Acc];
|
||||||
_ ->
|
_ ->
|
||||||
Acc
|
Acc
|
||||||
end
|
end
|
||||||
end, [], SubNodes)
|
end, [], SubNodes) of
|
||||||
end,
|
[] ->
|
||||||
case mnesia:transaction(Fun) of
|
{noreply, State};
|
||||||
{atomic, Missing} ->
|
Missing ->
|
||||||
%% For each unknown caps "subnode", we send a disco request.
|
%% For each unknown caps "subnode", we send a disco request.
|
||||||
NewRequests = lists:foldl(
|
NewRequests = lists:foldl(
|
||||||
fun(SubNode, Dict) ->
|
fun(SubNode, Dict) ->
|
||||||
@ -308,12 +316,9 @@ handle_cast({note_caps, From,
|
|||||||
(Host, ID, ?MODULE, handle_disco_response),
|
(Host, ID, ?MODULE, handle_disco_response),
|
||||||
ejabberd_router:route(jlib:make_jid("", Host, ""), From, Stanza),
|
ejabberd_router:route(jlib:make_jid("", Host, ""), From, Stanza),
|
||||||
timer:send_after(?CAPS_QUERY_TIMEOUT, self(), {disco_timeout, ID}),
|
timer:send_after(?CAPS_QUERY_TIMEOUT, self(), {disco_timeout, ID}),
|
||||||
?DICT:store(ID, {Node, SubNode}, Dict)
|
?DICT:store(ID, node_to_binary(Node, SubNode), Dict)
|
||||||
end, Requests, Missing),
|
end, Requests, Missing),
|
||||||
{noreply, State#state{disco_requests = NewRequests}};
|
{noreply, State#state{disco_requests = NewRequests}}
|
||||||
Error ->
|
|
||||||
?ERROR_MSG("Transaction failed: ~p", [Error]),
|
|
||||||
{noreply, State}
|
|
||||||
end;
|
end;
|
||||||
handle_cast({disco_response, From, _To,
|
handle_cast({disco_response, From, _To,
|
||||||
#iq{type = Type, id = ID,
|
#iq{type = Type, id = ID,
|
||||||
@ -322,18 +327,14 @@ handle_cast({disco_response, From, _To,
|
|||||||
case {Type, SubEls} of
|
case {Type, SubEls} of
|
||||||
{result, [{xmlelement, "query", _Attrs, Els}]} ->
|
{result, [{xmlelement, "query", _Attrs, Els}]} ->
|
||||||
case ?DICT:find(ID, Requests) of
|
case ?DICT:find(ID, Requests) of
|
||||||
{ok, {Node, SubNode}} ->
|
{ok, BinaryNode} ->
|
||||||
Features =
|
Features =
|
||||||
lists:flatmap(fun({xmlelement, "feature", FAttrs, _}) ->
|
lists:flatmap(fun({xmlelement, "feature", FAttrs, _}) ->
|
||||||
[xml:get_attr_s("var", FAttrs)];
|
[xml:get_attr_s("var", FAttrs)];
|
||||||
(_) ->
|
(_) ->
|
||||||
[]
|
[]
|
||||||
end, Els),
|
end, Els),
|
||||||
mnesia:transaction(
|
mnesia:dirty_write(#caps_features{node_pair = BinaryNode, features = features_to_binary(Features)}),
|
||||||
fun() ->
|
|
||||||
mnesia:write(#caps_features{node_pair = {Node, SubNode},
|
|
||||||
features = Features})
|
|
||||||
end),
|
|
||||||
gen_server:cast(self(), visit_feature_queries);
|
gen_server:cast(self(), visit_feature_queries);
|
||||||
error ->
|
error ->
|
||||||
?ERROR_MSG("ID '~s' matches no query", [ID])
|
?ERROR_MSG("ID '~s' matches no query", [ID])
|
||||||
@ -341,13 +342,8 @@ handle_cast({disco_response, From, _To,
|
|||||||
{error, _} ->
|
{error, _} ->
|
||||||
%% XXX: if we get error, we cache empty feature not to probe the client continuously
|
%% XXX: if we get error, we cache empty feature not to probe the client continuously
|
||||||
case ?DICT:find(ID, Requests) of
|
case ?DICT:find(ID, Requests) of
|
||||||
{ok, {Node, SubNode}} ->
|
{ok, BinaryNode} ->
|
||||||
Features = [],
|
mnesia:write(#caps_features{node_pair = BinaryNode}),
|
||||||
mnesia:transaction(
|
|
||||||
fun() ->
|
|
||||||
mnesia:write(#caps_features{node_pair = {Node, SubNode},
|
|
||||||
features = Features})
|
|
||||||
end),
|
|
||||||
gen_server:cast(self(), visit_feature_queries);
|
gen_server:cast(self(), visit_feature_queries);
|
||||||
error ->
|
error ->
|
||||||
?ERROR_MSG("ID '~s' matches no query", [ID])
|
?ERROR_MSG("ID '~s' matches no query", [ID])
|
||||||
|
Loading…
Reference in New Issue
Block a user