]> granicus.if.org Git - ejabberd/commitdiff
Limit result set of disco#items for mod_pubsub
authorEvgeny Khramtsov <ekhramtsov@process-one.net>
Thu, 24 Oct 2019 11:59:47 +0000 (14:59 +0300)
committerEvgeny Khramtsov <ekhramtsov@process-one.net>
Thu, 24 Oct 2019 11:59:47 +0000 (14:59 +0300)
The size of a list of nodes returned for disco#items request
is now controlled by option 'max_nodes_discoitems'. The default
value is 100. The name and the default value of the option is
chosen to be consistent with mod_muc's 'max_rooms_discoitems' option.

src/gen_pubsub_nodetree.erl
src/mod_pubsub.erl
src/mod_pubsub_opt.erl
src/nodetree_tree.erl
src/nodetree_tree_sql.erl
src/nodetree_virtual.erl

index 813ed71cee0f49091d35e75333233cbc19ed5182..e834f8e0b21727cd67e42980e2fc29227e0b4b65 100644 (file)
@@ -61,7 +61,7 @@
     {error, stanza_error()}.
 
 -callback get_nodes(Host :: host(),
-       From :: jid:jid())->
+                   Limit :: non_neg_integer() | infinity)->
     [pubsubNode()].
 
 -callback get_nodes(Host :: host())->
@@ -79,8 +79,8 @@
     [{0, [pubsubNode(),...]}].
 
 -callback get_subnodes(Host :: host(),
-       NodeId :: nodeId(),
-       From :: jid:jid()) ->
+                      NodeId :: nodeId(),
+                      Limit :: non_neg_integer() | infinity) ->
     [pubsubNode()].
 
 -callback get_subnodes_tree(Host :: host(),
index 0063df8d9041b3c37d9e893c4de2a77d4b67a7da..48a19b99f28b624343562427d357e50f173f9f7e 100644 (file)
@@ -498,6 +498,7 @@ disco_sm_items(Acc, _From, _To, _Node, _Lang) -> Acc.
 
 -spec disco_items(ljid(), binary(), jid()) -> [disco_item()].
 disco_items(Host, <<>>, From) ->
+    MaxNodes = mod_pubsub_opt:max_nodes_discoitems(serverhost(Host)),
     Action =
        fun(#pubsub_node{nodeid = {_, Node}, options = Options,
                         type = Type, id = Nidx, owners = O}, Acc) ->
@@ -513,7 +514,7 @@ disco_items(Host, <<>>, From) ->
                end
        end,
     NodeBloc = fun() ->
-                      case tree_call(Host, get_nodes, [Host]) of
+                      case tree_call(Host, get_nodes, [Host, MaxNodes]) of
                           Nodes when is_list(Nodes) ->
                               {result, lists:foldl(Action, [], Nodes)};
                           Error ->
@@ -1007,8 +1008,9 @@ iq_disco_info(ServerHost, Host, SNode, From, Lang) ->
 
 -spec iq_disco_items(host(), binary(), jid(), undefined | rsm_set()) ->
                            {result, disco_items()} | {error, stanza_error()}.
-iq_disco_items(Host, <<>>, From, _RSM) ->
-    case tree_action(Host, get_subnodes, [Host, <<>>, From]) of
+iq_disco_items(Host, <<>>, _From, _RSM) ->
+    MaxNodes = mod_pubsub_opt:max_nodes_discoitems(serverhost(Host)),
+    case tree_action(Host, get_subnodes, [Host, <<>>, MaxNodes]) of
        {error, #stanza_error{}} = Err ->
            Err;
        Nodes when is_list(Nodes) ->
@@ -1039,6 +1041,7 @@ iq_disco_items(Host, Item, From, RSM) ->
        [_Node, _ItemId] ->
            {result, #disco_items{}};
        [Node] ->
+           MaxNodes = mod_pubsub_opt:max_nodes_discoitems(serverhost(Host)),
            Action = fun(#pubsub_node{id = Nidx, type = Type, options = Options, owners = O}) ->
                             Owners = node_owners_call(Host, Type, Nidx, O),
                             {NodeItems, RsmOut} = case get_allowed_items_call(
@@ -1046,7 +1049,7 @@ iq_disco_items(Host, Item, From, RSM) ->
                                                       {result, R} -> R;
                                                       _ -> {[], undefined}
                                                   end,
-                            case tree_call(Host, get_subnodes, [Host, Node, From]) of
+                            case tree_call(Host, get_subnodes, [Host, Node, MaxNodes]) of
                                 SubNodes when is_list(SubNodes) ->
                                     Nodes = lists:map(
                                               fun(#pubsub_node{nodeid = {_, SubNode}, options = SubOptions}) ->
@@ -3154,7 +3157,7 @@ send_last_pep(From, To) ->
     Host = host(ServerHost),
     Publisher = jid:tolower(From),
     Owner = jid:remove_resource(Publisher),
-    case tree_action(Host, get_nodes, [Owner, From]) of
+    case tree_action(Host, get_nodes, [Owner, infinity]) of
        Nodes when is_list(Nodes) ->
            lists:foreach(
              fun(#pubsub_node{nodeid = {_, Node}, type = Type, id = Nidx, options = Options}) ->
@@ -4123,6 +4126,8 @@ mod_opt_type(last_item_cache) ->
     econf:bool();
 mod_opt_type(max_items_node) ->
     econf:non_neg_int();
+mod_opt_type(max_nodes_discoitems) ->
+    econf:non_neg_int(infinity);
 mod_opt_type(max_subscriptions_node) ->
     econf:non_neg_int();
 mod_opt_type(force_node_config) ->
@@ -4168,6 +4173,7 @@ mod_options(Host) ->
      {ignore_pep_from_offline, true},
      {last_item_cache, false},
      {max_items_node, ?MAXITEMS},
+     {max_nodes_discoitems, 100},
      {nodetree, ?STDTREE},
      {pep_mapping, []},
      {plugins, [?STDNODE]},
index 68ed7f96097cac0fad41970652229a80e1931d32..8db5532f64c80418a4eb87319802dcf6d9ea6d63 100644 (file)
@@ -12,6 +12,7 @@
 -export([ignore_pep_from_offline/1]).
 -export([last_item_cache/1]).
 -export([max_items_node/1]).
+-export([max_nodes_discoitems/1]).
 -export([max_subscriptions_node/1]).
 -export([name/1]).
 -export([nodetree/1]).
@@ -73,6 +74,12 @@ max_items_node(Opts) when is_map(Opts) ->
 max_items_node(Host) ->
     gen_mod:get_module_opt(Host, mod_pubsub, max_items_node).
 
+-spec max_nodes_discoitems(gen_mod:opts() | global | binary()) -> 'infinity' | non_neg_integer().
+max_nodes_discoitems(Opts) when is_map(Opts) ->
+    gen_mod:get_opt(max_nodes_discoitems, Opts);
+max_nodes_discoitems(Host) ->
+    gen_mod:get_module_opt(Host, mod_pubsub, max_nodes_discoitems).
+
 -spec max_subscriptions_node(gen_mod:opts() | global | binary()) -> 'undefined' | non_neg_integer().
 max_subscriptions_node(Opts) when is_map(Opts) ->
     gen_mod:get_opt(max_subscriptions_node, Opts);
index 08bc3192c41bd6047626be084977984ab936ac1d..c94ba197b92b81f26ecbd1070ae2189415ed3577 100644 (file)
@@ -38,6 +38,7 @@
 -author('christophe.romain@process-one.net').
 
 -include_lib("stdlib/include/qlc.hrl").
+-include_lib("stdlib/include/ms_transform.hrl").
 
 -include("pubsub.hrl").
 -include("xmpp.hrl").
@@ -81,11 +82,21 @@ get_node(Nidx) ->
        _ -> {error, xmpp:err_item_not_found(?T("Node not found"), ejabberd_option:language())}
     end.
 
-get_nodes(Host, _From) ->
-    get_nodes(Host).
-
 get_nodes(Host) ->
-    mnesia:match_object(#pubsub_node{nodeid = {Host, '_'}, _ = '_'}).
+    get_nodes(Host, infinity).
+
+get_nodes(Host, infinity) ->
+    mnesia:match_object(#pubsub_node{nodeid = {Host, '_'}, _ = '_'});
+get_nodes(Host, Limit) ->
+    case mnesia:select(
+          pubsub_node,
+          ets:fun2ms(
+            fun(#pubsub_node{nodeid = {H, _}} = Node) when H == Host ->
+                    Node
+            end), Limit, read) of
+       '$end_of_table' -> [];
+       {Nodes, _} -> Nodes
+    end.
 
 get_parentnodes(Host, Node, _From) ->
     case catch mnesia:read({pubsub_node, {Host, Node}}) of
@@ -109,25 +120,40 @@ get_parentnodes_tree(Host, Node, Level, Acc) ->
            Acc
     end.
 
-get_subnodes(Host, Node, _From) ->
-    get_subnodes(Host, Node).
-
-get_subnodes(Host, <<>>) ->
-    Q = qlc:q([N
-               || #pubsub_node{nodeid = {NHost, _},
-                       parents = Parents} =
-                   N
-                   <- mnesia:table(pubsub_node),
-                   Host == NHost, Parents == []]),
-    qlc:e(Q);
-get_subnodes(Host, Node) ->
+get_subnodes(Host, <<>>, infinity) ->
+    mnesia:match_object(#pubsub_node{nodeid = {Host, '_'}, parents = [], _ = '_'});
+get_subnodes(Host, <<>>, Limit) ->
+    case mnesia:select(
+          pubsub_node,
+          ets:fun2ms(
+            fun(#pubsub_node{nodeid = {H, _}, parents = []} = Node) when H == Host ->
+                    Node
+            end), Limit, read) of
+       '$end_of_table' -> [];
+       {Nodes, _} -> Nodes
+    end;
+get_subnodes(Host, Node, infinity) ->
     Q = qlc:q([N
                || #pubsub_node{nodeid = {NHost, _},
                        parents = Parents} =
                    N
                    <- mnesia:table(pubsub_node),
                    Host == NHost, lists:member(Node, Parents)]),
-    qlc:e(Q).
+    qlc:e(Q);
+get_subnodes(Host, Node, Limit) ->
+    case mnesia:select(
+          pubsub_node,
+          ets:fun2ms(
+            fun(#pubsub_node{nodeid = {H, _}, parents = Ps} = N)
+                  when H == Host andalso Ps /= [] -> N
+            end), Limit, read) of
+       '$end_of_table' -> [];
+       {Nodes, _} ->
+           lists:filter(
+             fun(#pubsub_node{parents = Parents}) ->
+                     lists:member(Node, Parents)
+             end, Nodes)
+    end.
 
 get_subnodes_tree(Host, Node, _From) ->
     get_subnodes_tree(Host, Node).
index efef656c570ed981c2accf8b59f6c5df90783729..bb9a27a61c7a9d5e44a02e263ac61cab654c04c7 100644 (file)
@@ -140,16 +140,25 @@ get_node(Nidx) ->
            {error, xmpp:err_item_not_found(?T("Node not found"), ejabberd_option:language())}
     end.
 
-get_nodes(Host, _From) ->
-    get_nodes(Host).
-
 get_nodes(Host) ->
+    get_nodes(Host, infinity).
+
+get_nodes(Host, Limit) ->
     H = node_flat_sql:encode_host(Host),
-    case catch
-       ejabberd_sql:sql_query_t(
-         ?SQL("select @(node)s, @(parent)s, @(plugin)s, @(nodeid)d from pubsub_node "
-              "where host=%(H)s"))
-    of
+    Query = fun(mssql, _) when is_integer(Limit), Limit>=0 ->
+                   ejabberd_sql:sql_query_t(
+                     ?SQL("select top %(Limit)d @(node)s, @(parent)s, @(plugin)s, @(nodeid)d "
+                          "from pubsub_node where host=%(H)s"));
+              (_, _) when is_integer(Limit), Limit>=0 ->
+                   ejabberd_sql:sql_query_t(
+                     ?SQL("select @(node)s, @(parent)s, @(plugin)s, @(nodeid)d "
+                          "from pubsub_node where host=%(H)s limit %(Limit)d"));
+              (_, _) ->
+                   ejabberd_sql:sql_query_t(
+                     ?SQL("select @(node)s, @(parent)s, @(plugin)s, @(nodeid)d "
+                          "from pubsub_node where host=%(H)s"))
+           end,
+    case ejabberd_sql:sql_query_t(Query) of
        {selected, RItems} ->
            [raw_to_node(Host, Item) || Item <- RItems];
        _ ->
@@ -178,16 +187,23 @@ get_parentnodes_tree(Host, Node, Level, Acc) ->
            Acc
     end.
 
-get_subnodes(Host, Node, _From) ->
-    get_subnodes(Host, Node).
-
-get_subnodes(Host, Node) ->
+get_subnodes(Host, Node, Limit) ->
     H = node_flat_sql:encode_host(Host),
-    case catch
-       ejabberd_sql:sql_query_t(
-         ?SQL("select @(node)s, @(parent)s, @(plugin)s, @(nodeid)d from pubsub_node "
-              "where host=%(H)s and parent=%(Node)s"))
-    of
+    Query = fun(mssql, _) when is_integer(Limit), Limit>=0 ->
+                   ejabberd_sql:sql_query_t(
+                     ?SQL("select top %(Limit)d @(node)s, @(parent)s, @(plugin)s, @(nodeid)d "
+                          "from pubsub_node where host=%(H)s and parent=%(Node)s"));
+              (_, _) when is_integer(Limit), Limit>=0 ->
+                   ejabberd_sql:sql_query_t(
+                     ?SQL("select @(node)s, @(parent)s, @(plugin)s, @(nodeid)d "
+                          "from pubsub_node where host=%(H)s and parent=%(Node)s "
+                          "limit %(Limit)d"));
+              (_, _) ->
+                   ejabberd_sql:sql_query_t(
+                     ?SQL("select @(node)s, @(parent)s, @(plugin)s, @(nodeid)d "
+                          "from pubsub_node where host=%(H)s and parent=%(Node)s"))
+           end,
+    case ejabberd_sql:sql_query_t(Query) of
        {selected, RItems} ->
            [raw_to_node(Host, Item) || Item <- RItems];
        _ ->
index c27efe44bbde87da05162025262ae879697a5fba..627eca092d62cdd335a3636507f3f38f6c79cf17 100644 (file)
@@ -65,10 +65,10 @@ get_node(Nidx) ->
     {Host, Node} = nodeid(Nidx),
     node_record(Host, Node, Nidx).
 
-get_nodes(Host, _From) ->
-    get_nodes(Host).
+get_nodes(Host) ->
+    get_nodes(Host, infinity).
 
-get_nodes(_Host) ->
+get_nodes(_Host, _Limit) ->
     [].
 
 get_parentnodes(_Host, _Node, _From) ->
@@ -77,10 +77,7 @@ get_parentnodes(_Host, _Node, _From) ->
 get_parentnodes_tree(Host, Node, From) ->
     [{0, [get_node(Host, Node, From)]}].
 
-get_subnodes(Host, Node, _From) ->
-    get_subnodes(Host, Node).
-
-get_subnodes(_Host, _Node) ->
+get_subnodes(_Host, _Node, _From) ->
     [].
 
 get_subnodes_tree(Host, Node, _From) ->