diff --git a/src/mod_pubsub/pubsub_clean.erl b/src/mod_pubsub/pubsub_clean.erl new file mode 100644 index 000000000..6e32ccb35 --- /dev/null +++ b/src/mod_pubsub/pubsub_clean.erl @@ -0,0 +1,41 @@ +-module(pubsub_clean). + +-define(TIMEOUT, 1000*600). % 1 minute + +-export([start/0, loop/0, subscribed/1, offline/1]). + +start() -> + Pid = spawn(?MODULE, loop, []), + register(pubsub_clean, Pid), + Pid. + +loop() -> + receive + purge -> purge() + after ?TIMEOUT -> purge() + end, + loop(). + +purge() -> + {Sessions, Subscriptions} = {mnesia:table_info(session,size),mnesia:table_info(pubsub_state,size)}, + if Subscriptions > Sessions + 500 -> + lists:foreach(fun(K) -> + [N]=mnesia:dirty_read({pubsub_node, K}), + I=element(3,N), + lists:foreach(fun(JID) -> + mnesia:dirty_delete({pubsub_state, {JID, I}}) + end, offline(subscribed(I))) + end, mnesia:dirty_all_keys(pubsub_node)); + true -> + ok + end. + +subscribed(NodeId) -> + lists:map(fun(S) -> + element(1,element(2,S)) + end, mnesia:dirty_match_object({pubsub_state, {'_',NodeId},'_',none,subscribed})). + +offline(Jids) -> + lists:filter(fun({U,S,""}) -> ejabberd_sm:get_user_resources(U,S) == []; + ({U,S,R}) -> not lists:member(R,ejabberd_sm:get_user_resources(U,S)) + end, Jids). diff --git a/src/mod_pubsub/pubsub_debug.erl b/src/mod_pubsub/pubsub_debug.erl new file mode 100644 index 000000000..2fe1a5b42 --- /dev/null +++ b/src/mod_pubsub/pubsub_debug.erl @@ -0,0 +1,112 @@ +-module(pubsub_debug). +-author('christophe.romain@process-one.net'). + +-include("pubsub.hrl"). + +-compile(export_all). + +nodeid(Host, Node) -> + case mnesia:dirty_read({pubsub_node, {Host, Node}}) of + [N] -> nodeid(N); + _ -> 0 + end. +nodeid(N) -> N#pubsub_node.id. +nodeids() -> [nodeid(Host, Node) || {Host, Node} <- mnesia:dirty_all_keys(pubsub_node)]. +nodeids_by_type(Type) -> [nodeid(N) || N <- mnesia:dirty_match_object(#pubsub_node{type=Type, _='_'})]. +nodeids_by_option(Key, Value) -> [nodeid(N) || N <- mnesia:dirty_match_object(#pubsub_node{_='_'}), lists:member({Key, Value}, N#pubsub_node.options)]. +nodeids_by_owner(JID) -> [nodeid(N) || N <- mnesia:dirty_match_object(#pubsub_node{_='_'}), lists:member(JID, N#pubsub_node.owners)]. +nodes_by_id(I) -> mnesia:dirty_match_object(#pubsub_node{id=I, _='_'}). + +state(JID, NodeId) -> + case mnesia:dirty_read({pubsub_state, {JID, NodeId}}) of + [S] -> S; + _ -> undefined + end. +states(NodeId) -> mnesia:dirty_match_object(#pubsub_state{stateid={'_', NodeId}, _='_'}). +stateid(S) -> element(1, S#pubsub_state.stateid). +stateids(NodeId) -> [stateid(S) || S <- states(NodeId)]. +states_by_jid(JID) -> mnesia:dirty_match_object(#pubsub_state{stateid={JID, '_'}, _='_'}). + +item(ItemId, NodeId) -> + case mnesia:dirty_read({pubsub_item, {ItemId, NodeId}}) of + [I] -> I; + _ -> undefined + end. +items(NodeId) -> mnesia:dirty_match_object(#pubsub_item{itemid={'_', NodeId}, _='_'}). +itemid(I) -> element(1, I#pubsub_item.itemid). +itemids(NodeId) -> [itemid(I) || I <- items(NodeId)]. +items_by_id(ItemId) -> mnesia:dirty_match_object(#pubsub_item{itemid={ItemId, '_'}, _='_'}). + +affiliated(NodeId) -> [stateid(S) || S <- states(NodeId), S#pubsub_state.affiliation=/=none]. +subscribed(NodeId) -> [stateid(S) || S <- states(NodeId), S#pubsub_state.subscriptions=/=[]]. +%subscribed(NodeId) -> [stateid(S) || S <- states(NodeId), S#pubsub_state.subscription=/=none]. %% old record +owners(NodeId) -> [stateid(S) || S <- mnesia:dirty_match_object(#pubsub_state{stateid={'_', NodeId}, affiliation=owner, _='_'})]. + +orphan_items(NodeId) -> + itemids(NodeId) -- lists:foldl(fun(S, A) -> A++S#pubsub_state.items end, [], mnesia:dirty_match_object(#pubsub_state{stateid={'_', NodeId}, _='_'})). +newer_items(NodeId, Seconds) -> + Now = calendar:universal_time(), + Oldest = calendar:seconds_to_daystime(Seconds), + [itemid(I) || I <- items(NodeId), calendar:time_difference(calendar:now_to_universal_time(element(1, I#pubsub_item.modification)), Now) < Oldest]. +older_items(NodeId, Seconds) -> + Now = calendar:universal_time(), + Oldest = calendar:seconds_to_daystime(Seconds), + [itemid(I) || I <- items(NodeId), calendar:time_difference(calendar:now_to_universal_time(element(1, I#pubsub_item.modification)), Now) > Oldest]. + +orphan_nodes() -> [I || I <- nodeids(), owners(I)==[]]. +duplicated_nodes() -> L = nodeids(), lists:usort(L -- lists:seq(1, lists:max(L))). +node_options(NodeId) -> + [N] = mnesia:dirty_match_object(#pubsub_node{id=NodeId, _='_'}), + N#pubsub_node.options. +update_node_options(Key, Value, NodeId) -> + [N] = mnesia:dirty_match_object(#pubsub_node{id=NodeId, _='_'}), + NewOptions = lists:keyreplace(Key, 1, N#pubsub_node.options, {Key, Value}), + mnesia:dirty_write(N#pubsub_node{options = NewOptions}). + +check() -> + mnesia:transaction(fun() -> + case mnesia:read({pubsub_index, node}) of + [Idx] -> + Free = Idx#pubsub_index.free, + Last = Idx#pubsub_index.last, + Allocated = lists:seq(1, Last) -- Free, + NodeIds = mnesia:foldl(fun(N,A) -> [nodeid(N)|A] end, [], pubsub_node), + StateIds = lists:usort(mnesia:foldl(fun(S,A) -> [element(2, S#pubsub_state.stateid)|A] end, [], pubsub_state)), + ItemIds = lists:usort(mnesia:foldl(fun(I,A) -> [element(2, I#pubsub_item.itemid)|A] end, [], pubsub_item)), + BadNodeIds = NodeIds -- Allocated, + BadStateIds = StateIds -- NodeIds, + BadItemIds = ItemIds -- NodeIds, + Lost = Allocated -- NodeIds, + [{bad_nodes, [N#pubsub_node.nodeid || N <- lists:flatten([mnesia:match_object(#pubsub_node{id=I, _='_'}) || I <- BadNodeIds])]}, + {bad_states, lists:foldl(fun(N,A) -> A++[{I,N} || I <- stateids(N)] end, [], BadStateIds)}, + {bad_items, lists:foldl(fun(N,A) -> A++[{I,N} || I <- itemids(N)] end, [], BadItemIds)}, + {lost_idx, Lost}, + {orphaned, [I || I <- NodeIds, owners(I)==[]]}, + {duplicated, lists:usort(NodeIds -- lists:seq(1, lists:max(NodeIds)))}]; + _ -> + no_index + end + end). + +rebuild_index() -> + mnesia:transaction(fun() -> + NodeIds = mnesia:foldl(fun(N,A) -> [nodeid(N)|A] end, [], pubsub_node), + Last = lists:max(NodeIds), + Free = lists:seq(1, Last) -- NodeIds, + mnesia:write(#pubsub_index{index = node, last = Last, free = Free}) + end). + +pep_subscriptions(LUser, LServer, LResource) -> + case ejabberd_sm:get_session_pid({LUser, LServer, LResource}) of + C2SPid when is_pid(C2SPid) -> + case catch ejabberd_c2s:get_subscribed(C2SPid) of + Contacts when is_list(Contacts) -> + lists:map(fun({U, S, _}) -> + io_lib:format("~s@~s", [U, S]) + end, Contacts); + _ -> + [] + end; + _ -> + [] + end.