diff options
author | Rickard Green <rickard@erlang.org> | 2022-04-09 03:27:19 +0200 |
---|---|---|
committer | Rickard Green <rickard@erlang.org> | 2022-05-03 19:02:16 +0200 |
commit | 7c212cefe9fdc4a3a788859d1298729f6fb8e5b9 (patch) | |
tree | a81d77ce3ef693dbd222215c4c5d0a4a877bd018 /lib | |
parent | 2c1d5d4ffd9a6ccf72ca5ae4ae8700966116b9f3 (diff) | |
download | erlang-7c212cefe9fdc4a3a788859d1298729f6fb8e5b9.tar.gz |
[erts,kernel] Connection ID information
Diffstat (limited to 'lib')
-rw-r--r-- | lib/kernel/doc/src/net_kernel.xml | 219 | ||||
-rw-r--r-- | lib/kernel/src/kernel.app.src | 2 | ||||
-rw-r--r-- | lib/kernel/src/net_kernel.erl | 77 | ||||
-rw-r--r-- | lib/kernel/test/erl_distribution_SUITE.erl | 99 | ||||
-rw-r--r-- | lib/stdlib/src/erl_internal.erl | 1 | ||||
-rw-r--r-- | lib/stdlib/src/stdlib.app.src | 2 |
6 files changed, 326 insertions, 74 deletions
diff --git a/lib/kernel/doc/src/net_kernel.xml b/lib/kernel/doc/src/net_kernel.xml index 419d3cad84..096af5cb55 100644 --- a/lib/kernel/doc/src/net_kernel.xml +++ b/lib/kernel/doc/src/net_kernel.xml @@ -161,82 +161,121 @@ $ <input>erl -sname foobar</input></pre> are stopped. Two option lists are considered the same if they contain the same set of options.</p> - <p>As from Kernel version 2.11.4, and ERTS version - 5.5.4, the following is guaranteed:</p> - <list type="bulleted"> - <item><p><c>nodeup</c> messages are delivered before delivery - of any message from the remote node passed through the - newly established connection.</p></item> - <item><p><c>nodedown</c> messages are not delivered until all - messages from the remote node that have been passed - through the connection have been delivered.</p></item> - </list> - <p>Notice that this is <em>not</em> guaranteed for Kernel - versions before 2.11.4.</p> - <p>As from Kernel version 2.11.4, subscriptions can also be - made before the <c>net_kernel</c> server is started, that is, - <c>net_kernel:monitor_nodes/[1,2]</c> does not return - <c>ignored</c>.</p> - <p>As from Kernel version 2.13, and ERTS version - 5.7, the following is guaranteed:</p> - <list type="bulleted"> - <item><p><c>nodeup</c> messages are delivered after the + <p>Delivery guarantees of <c>nodeup</c>/<c>nodedown</c> messages:</p> + <list> + <item><p> + <c>nodeup</c> messages are delivered before delivery + of any signals from the remote node through the newly + established connection. + </p></item> + <item> + <p><c>nodedown</c> messages are delivered after all + the signals from the remote node over the connection + have been delivered. + </p></item> + <item><p> + <c>nodeup</c> messages are delivered after the corresponding node appears in results from - <c>erlang:nodes/X</c>.</p></item> - <item><p><c>nodedown</c> messages are delivered after the + <c>erlang:nodes()</c>. + </p></item> + <item> + <p><c>nodedown</c> messages are delivered after the corresponding node has disappeared in results from - <c>erlang:nodes/X</c>.</p></item> + <c>erlang:nodes()</c>. + </p></item> + <item><p> + As of OTP @OTP-16362@, a <c>nodedown</c> message for a + connection being taken down will be delivered before a + <c>nodeup</c> message due to a new connection to the + same node. Prior to OTP @OTP-16362@, this was not + guaranteed to be the case. + </p></item> </list> - <p>Notice that this is <em>not</em> guaranteed for Kernel - versions before 2.13.</p> <p>The format of the node status change messages depends on <c><anno>Options</anno></c>. If <c><anno>Options</anno></c> is - <c>[]</c>, which is the default, the format is as follows:</p> - <code type="none"> + the empty list or if <c>net_kernel:monitor_nodes/1</c> is called, + the format is as follows:</p> + <code type="erl"> {nodeup, Node} | {nodedown, Node} Node = node()</code> - <p>If <c><anno>Options</anno></c> is not <c>[]</c>, the format is - as follows:</p> - <code type="none"> -{nodeup, Node, InfoList} | {nodedown, Node, InfoList} + <p> + When <c><anno>Options</anno></c> is the empty map or empty + list, the caller will only subscribe for status change messages + for visible nodes. That is, only nodes that appear in the + result of + <seealso marker="erts:erlang#nodes/0"><c>erlang:nodes/0</c></seealso>. + </p> + <p> + If <c><anno>Options</anno></c> equals anything other than the + empty list, the format of the status change messages is as follows: + </p> + <code type="erl"> +{nodeup, Node, Info} | {nodedown, Node, Info} Node = node() - InfoList = [{Tag, Val}]</code> - <p><c>InfoList</c> is a list of tuples. Its contents depends on - <c><anno>Options</anno></c>, see below.</p> - <p>Also, when <c>OptionList == []</c>, only visible nodes, that - is, nodes that appear in the result of - <seealso marker="erts:erlang#nodes/0"><c>erlang:nodes/0</c></seealso>, - are monitored.</p> - <p><c><anno>Option</anno></c> can be any of the following:</p> + Info = #{Tag => Val} | [{Tag, Val}]</code> + <p> + <c>Info</c> is either a map or a list of 2-tuples. Its content + depends on <c><anno>Options</anno></c>. If <c><anno>Options</anno></c> + is a map, <c>Info</c> will also be a map. If <c><anno>Options</anno></c> + is a list, <c>Info</c> will also be a list. + </p> + <p> + When <c><anno>Options</anno></c> is a map, currently + the following associations are allowed: + </p> <taglist> - <tag><c>{node_type, NodeType}</c></tag> + <tag><c>connection_id => boolean()</c></tag> + <item> + <p> + If the value of the association equals <c>true</c>, a + <c>connection_id => ConnectionId</c> association will be + included in the <c>Info</c> map where <c>ConnectionId</c> + is the connection identifier of the connection coming up + or going down. For more info about this connection + identifier see the documentation of + <seealso marker="erts:erlang#connection_id">erlang:nodes/2</seealso>. + </p> + </item> + <tag><c>node_type => <anno>NodeType</anno></c></tag> <item> <p>Valid values for <c>NodeType</c>:</p> <taglist> <tag><c>visible</c></tag> <item><p>Subscribe to node status change messages for visible - nodes only. The tuple <c>{node_type, visible}</c> is - included in <c>InfoList</c>.</p></item> + nodes only. The association <c>node_type => visible</c> will + be included in the <c>Info</c> map.</p></item> <tag><c>hidden</c></tag> <item><p>Subscribe to node status change messages for hidden - nodes only. The tuple <c>{node_type, hidden}</c> is - included in <c>InfoList</c>.</p></item> + nodes only. The association <c>node_type => hidden</c> will + be included in the <c>Info</c> map.</p></item> <tag><c>all</c></tag> <item><p>Subscribe to node status change messages for both - visible and hidden nodes. The tuple - <c>{node_type, visible | hidden}</c> is included in - <c>InfoList</c>.</p></item> + visible and hidden nodes. The association + <c>node_type => visible | hidden</c> will be included in + the <c>Info</c> map.</p></item> </taglist> + <p> + If no <c>node_type => <anno>NodeType</anno></c> association + is included in the <c><anno>Options</anno></c> map, the + caller will subscribe for status change messages for visible + nodes only, but <i>no</i> <c>node_type => visible</c> + association will be included in the <c>Info</c> map. + </p> </item> - <tag><c>nodedown_reason</c></tag> + <tag><c>nodedown_reason => boolean()</c></tag> <item> - <p>The tuple <c>{nodedown_reason, Reason}</c> is included in - <c>InfoList</c> in <c>nodedown</c> messages.</p> + <p> + If the value of the association equals <c>true</c>, a + <c>nodedown_reason => Reason</c> association will be + included in the <c>Info</c> map for <c>nodedown</c> + messages. + </p> + <marker id="nodedown_reasons"/> <p> <c>Reason</c> can, depending on which - distribution module or process that is used be any term, + distribution module or process that is used, be any term, but for the standard TCP distribution module it is - any of the following: + one of the following: </p> <taglist> <tag><c>connection_setup_failed</c></tag> @@ -263,6 +302,82 @@ $ <input>erl -sname foobar</input></pre> </taglist> </item> </taglist> + <p> + When <c><anno>Options</anno></c> is a list, currently + <c><anno>ListOption</anno></c> can be one of the following: + </p> + <taglist> + <tag><c>connection_id</c></tag> + <item> + <p> + A <c>{connection_id, ConnectionId}</c> tuple will be + included in <c>Info</c> where <c>ConnectionId</c> is the + connection identifier of the connection coming up or + going down. For more info about this connection identifier + see the documentation of + <seealso marker="erts:erlang#connection_id">erlang:nodes/2</seealso>. + </p> + </item> + <tag><c>{node_type, <anno>NodeType</anno>}</c></tag> + <item> + <p>Valid values for <c><anno>NodeType</anno></c>:</p> + <taglist> + <tag><c>visible</c></tag> + <item><p>Subscribe to node status change messages for visible + nodes only. The tuple <c>{node_type, visible}</c> will be + included in the <c>Info</c> list.</p></item> + <tag><c>hidden</c></tag> + <item><p>Subscribe to node status change messages for hidden + nodes only. The tuple <c>{node_type, hidden}</c> will be + included in the <c>Info</c> list.</p></item> + <tag><c>all</c></tag> + <item><p>Subscribe to node status change messages for both + visible and hidden nodes. The tuple + <c>{node_type, visible | hidden}</c> will be included in + the <c>Info</c> list.</p></item> + </taglist> + <p> + If no <c>{node_type, <anno>NodeType</anno>}</c> option + has been given. The caller will subscribe for status + change messages for visible nodes only, but <i>no</i> + <c>{node_type, visible}</c> tuple will be included in the + <c>Info</c> list. + </p> + </item> + <tag><c>nodedown_reason</c></tag> + <item> + <p> + The tuple <c>{nodedown_reason, Reason}</c> will be included + in the <c>Info</c> list for <c>nodedown</c> messages. + </p> + <p> + See the documentation of the + <seealso marker="#nodedown_reasons"><c>nodedown_reason + => boolean()</c></seealso> association above for information + about possible <c>Reason</c> values. + </p> + </item> + </taglist> + <p>Example:</p> + <code type="erl"> +(a@localhost)1> net_kernel:monitor_nodes(true, #{connection_id=>true, node_type=>all, nodedown_reason=>true}). +ok +(a@localhost)2> flush(). +Shell got {nodeup,b@localhost, + #{connection_id => 3067552,node_type => visible}} +Shell got {nodeup,c@localhost, + #{connection_id => 13892107,node_type => hidden}} +Shell got {nodedown,b@localhost, + #{connection_id => 3067552,node_type => visible, + nodedown_reason => connection_closed}} +Shell got {nodedown,c@localhost, + #{connection_id => 13892107,node_type => hidden, + nodedown_reason => net_tick_timeout}} +Shell got {nodeup,b@localhost, + #{connection_id => 3067553,node_type => visible}} +ok +(a@localhost)3> + </code> </desc> </func> diff --git a/lib/kernel/src/kernel.app.src b/lib/kernel/src/kernel.app.src index 234d71f745..1e49d081fc 100644 --- a/lib/kernel/src/kernel.app.src +++ b/lib/kernel/src/kernel.app.src @@ -150,6 +150,6 @@ {prevent_overlapping_partitions, false} ]}, {mod, {kernel, []}}, - {runtime_dependencies, ["erts-@OTP-17843@", "stdlib-3.5", "sasl-3.0"]} + {runtime_dependencies, ["erts-@OTP-17843:OTP-17934@", "stdlib-3.5", "sasl-3.0"]} ] }. diff --git a/lib/kernel/src/net_kernel.erl b/lib/kernel/src/net_kernel.erl index b857000a80..e4e2ca6e49 100644 --- a/lib/kernel/src/net_kernel.erl +++ b/lib/kernel/src/net_kernel.erl @@ -256,15 +256,41 @@ monitor_nodes(Flag) -> -spec monitor_nodes(Flag, Options) -> ok | Error when Flag :: boolean(), - Options :: [Option], - Option :: {node_type, NodeType} - | nodedown_reason, + Options :: OptionsList | OptionsMap, + OptionsList :: [ListOption], + ListOption :: connection_id + | {node_type, NodeType} + | nodedown_reason, + OptionsMap :: #{connection_id => boolean(), + node_type => NodeType, + nodedown_reason => boolean()}, NodeType :: visible | hidden | all, Error :: error | {error, term()}. monitor_nodes(Flag, Opts) -> - case catch process_flag({monitor_nodes, Opts}, Flag) of - N when is_integer(N) -> ok; - _ -> mk_monitor_nodes_error(Flag, Opts) + try + MapOpts = if is_map(Opts) -> + error = maps:find(list, Opts), + Opts; + is_list(Opts) -> + lists:foldl(fun (nodedown_reason, Acc) -> + Acc#{nodedown_reason => true}; + (connection_id, Acc) -> + Acc#{connection_id => true}; + ({node_type, Val}, Acc) -> + case maps:find(node_type, Acc) of + error -> ok; + {ok, Val} -> ok + end, + Acc#{node_type => Val} + end, + #{list => true}, + Opts) + end, + true = is_integer(process_flag({monitor_nodes, MapOpts}, Flag)), + ok + catch + _:_ -> + mk_monitor_nodes_error(Flag, Opts) end. %% ... @@ -1194,8 +1220,45 @@ check_options(Opts) when is_list(Opts) -> _ -> {error, {unknown_options, RestOpts2}} end; +check_options(Opts) when is_map(Opts) -> + BadMap0 = case maps:find(connection_id, Opts) of + error -> + Opts; + {ok, CIdBool} when is_boolean(CIdBool) -> + maps:remove(connection_id, Opts); + {ok, BadCIdVal} -> + throw({error, + {bad_option_value, + #{connection_id => BadCIdVal}}}) + end, + BadMap1 = case maps:find(nodedown_reason, BadMap0) of + error -> + BadMap0; + {ok, NRBool} when is_boolean(NRBool) -> + maps:remove(nodedown_reason, BadMap0); + {ok, BadNRVal} -> + throw({error, + {bad_option_value, + #{nodedown_reason => BadNRVal}}}) + end, + BadMap2 = case maps:find(node_type, BadMap1) of + error -> + BadMap1; + {ok, NTVal} when NTVal == visible; NTVal == hidden; NTVal == all -> + maps:remove(node_type, BadMap1); + {ok, BadNTVal} -> + throw({error, + {bad_option_value, + #{node_type => BadNTVal}}}) + end, + if map_size(BadMap2) == 0 -> + {error, internal_error}; + true -> + throw({error, {unknown_options, BadMap2}}) + end; check_options(Opts) -> - {error, {options_not_a_list, Opts}}. + {error, {invalid_options, Opts}}. + mk_monitor_nodes_error(Flag, _Opts) when Flag =/= true, Flag =/= false -> error; diff --git a/lib/kernel/test/erl_distribution_SUITE.erl b/lib/kernel/test/erl_distribution_SUITE.erl index e4fe27c619..7d7108e44b 100644 --- a/lib/kernel/test/erl_distribution_SUITE.erl +++ b/lib/kernel/test/erl_distribution_SUITE.erl @@ -1108,7 +1108,9 @@ monitor_nodes_misc(DCfg, _Config) -> MonNodeState = monitor_node_state(), ok = net_kernel:monitor_nodes(true), ok = net_kernel:monitor_nodes(true, [{node_type, all}, nodedown_reason]), - ok = net_kernel:monitor_nodes(true, [nodedown_reason, {node_type, all}]), + ok = net_kernel:monitor_nodes(true, [nodedown_reason, {node_type, all}, connection_id]), + ok = net_kernel:monitor_nodes(true, #{node_type => all, nodedown_reason => true}), + ok = net_kernel:monitor_nodes(true, #{node_type => all, nodedown_reason => true, connection_id => true}), Names = get_numbered_nodenames(3, node), [NN1, NN2, NN3] = Names, @@ -1117,27 +1119,90 @@ monitor_nodes_misc(DCfg, _Config) -> receive {nodeup, N1} -> ok end, + receive {nodeup, N1, #{node_type := visible}} -> ok end, + receive {nodeup, N2, #{node_type := hidden}} -> ok end, receive {nodeup, N1, [{node_type, visible}]} -> ok end, - receive {nodeup, N1, [{node_type, visible}]} -> ok end, - receive {nodeup, N2, [{node_type, hidden}]} -> ok end, receive {nodeup, N2, [{node_type, hidden}]} -> ok end, + NodesInfo = erlang:nodes(connected, #{connection_id => true}), + + {N1, #{connection_id := N1CId}} = lists:keyfind(N1, 1, NodesInfo), + {N2, #{connection_id := N2CId}} = lists:keyfind(N2, 1, NodesInfo), + + ct:pal("N1: ~p ~p~n", [N1, N1CId]), + ct:pal("N2: ~p ~p~n", [N2, N2CId]), + + receive {nodeup, N1, #{node_type := visible, connection_id := N1CId}} -> ok end, + receive {nodeup, N2, #{node_type := hidden, connection_id := N2CId}} -> ok end, + + N1UpInfoSorted = lists:sort([{node_type, visible},{connection_id, N1CId}]), + N2UpInfoSorted = lists:sort([{node_type, hidden},{connection_id, N2CId}]), + + receive {nodeup, N1, UpN1Info} -> N1UpInfoSorted = lists:sort(UpN1Info) end, + receive {nodeup, N2, UpN2Info} -> N2UpInfoSorted = lists:sort(UpN2Info) end, + stop_node(N1), stop_node(N2), - VisbleDownInfo = lists:sort([{node_type, visible}, - {nodedown_reason, connection_closed}]), - HiddenDownInfo = lists:sort([{node_type, hidden}, - {nodedown_reason, connection_closed}]), - receive {nodedown, N1} -> ok end, - receive {nodedown, N1, Info1A} -> VisbleDownInfo = lists:sort(Info1A) end, - receive {nodedown, N1, Info1B} -> VisbleDownInfo = lists:sort(Info1B) end, - receive {nodedown, N2, Info2A} -> HiddenDownInfo = lists:sort(Info2A) end, - receive {nodedown, N2, Info2B} -> HiddenDownInfo = lists:sort(Info2B) end, + receive {nodedown, N1, #{node_type := visible, + nodedown_reason := connection_closed}} -> ok end, + receive {nodedown, N1, #{node_type := visible, + nodedown_reason := connection_closed, + connection_id := N1CId}} -> ok end, + receive {nodedown, N2, #{node_type := hidden, + nodedown_reason := connection_closed}} -> ok end, + receive {nodedown, N2, #{node_type := hidden, + nodedown_reason := connection_closed, + connection_id := N2CId}} -> ok end, + + N1ADownInfoSorted = lists:sort([{node_type, visible}, + {nodedown_reason, connection_closed}]), + N1BDownInfoSorted = lists:sort([{node_type, visible}, + {nodedown_reason, connection_closed}, + {connection_id, N1CId}]), + N2ADownInfoSorted = lists:sort([{node_type, hidden}, + {nodedown_reason, connection_closed}]), + N2BDownInfoSorted = lists:sort([{node_type, hidden}, + {nodedown_reason, connection_closed}, + {connection_id, N2CId}]), + + receive + {nodedown, N1, N1Info1} -> + case lists:sort(N1Info1) of + N1ADownInfoSorted -> + receive + {nodedown, N1, N1Info2} -> + N1BDownInfoSorted = lists:sort(N1Info2) + end; + N1BDownInfoSorted -> + receive + {nodedown, N1, N1Info2} -> + N1ADownInfoSorted = lists:sort(N1Info2) + end + end + end, + receive + {nodedown, N2, N2Info1} -> + case lists:sort(N2Info1) of + N2ADownInfoSorted -> + receive + {nodedown, N2, N2Info2} -> + N2BDownInfoSorted = lists:sort(N2Info2) + end; + N2BDownInfoSorted -> + receive + {nodedown, N2, N2Info2} -> + N2ADownInfoSorted = lists:sort(N2Info2) + end + end + end, ok = net_kernel:monitor_nodes(false, [{node_type, all}, nodedown_reason]), + ok = net_kernel:monitor_nodes(false, [nodedown_reason, {node_type, all}, connection_id]), + ok = net_kernel:monitor_nodes(false, #{node_type => all, nodedown_reason => true}), + ok = net_kernel:monitor_nodes(false, #{node_type => all, nodedown_reason => true, connection_id => true}), {ok, N3} = start_node(DCfg, NN3), receive {nodeup, N3} -> ok end, @@ -1273,7 +1338,11 @@ monitor_nodes_errors(Config) when is_list(Config) -> [gurka]}} = net_kernel:monitor_nodes(true, [gurka]), {error, - {options_not_a_list, + {unknown_options, + #{gurka := true}}} = net_kernel:monitor_nodes(true, + #{gurka => true}), + {error, + {invalid_options, gurka}} = net_kernel:monitor_nodes(true, gurka), {error, @@ -1295,6 +1364,10 @@ monitor_nodes_errors(Config) when is_list(Config) -> {node_type, blaha}}} = net_kernel:monitor_nodes(true, [{node_type, blaha}]), + {error, + {bad_option_value, + #{node_type := blaha}}} + = net_kernel:monitor_nodes(true, #{node_type => blaha}), MonNodeState = monitor_node_state(), ok. diff --git a/lib/stdlib/src/erl_internal.erl b/lib/stdlib/src/erl_internal.erl index 939abaff00..bf170fd7b4 100644 --- a/lib/stdlib/src/erl_internal.erl +++ b/lib/stdlib/src/erl_internal.erl @@ -351,6 +351,7 @@ bif(node, 0) -> true; bif(node, 1) -> true; bif(nodes, 0) -> true; bif(nodes, 1) -> true; +bif(nodes, 2) -> true; bif(now, 0) -> true; bif(open_port, 2) -> true; bif(pid_to_list, 1) -> true; diff --git a/lib/stdlib/src/stdlib.app.src b/lib/stdlib/src/stdlib.app.src index f1a1afaf72..a48dbf0fc5 100644 --- a/lib/stdlib/src/stdlib.app.src +++ b/lib/stdlib/src/stdlib.app.src @@ -108,6 +108,6 @@ dets]}, {applications, [kernel]}, {env, []}, - {runtime_dependencies, ["sasl-3.0","kernel-6.0","erts-10.7.1","crypto-3.3", + {runtime_dependencies, ["sasl-3.0","kernel-6.0","erts-@OTP-17934@","crypto-3.3", "compiler-5.0"]} ]}. |