summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorRickard Green <rickard@erlang.org>2022-04-09 03:27:19 +0200
committerRickard Green <rickard@erlang.org>2022-05-03 19:02:16 +0200
commit7c212cefe9fdc4a3a788859d1298729f6fb8e5b9 (patch)
treea81d77ce3ef693dbd222215c4c5d0a4a877bd018 /lib
parent2c1d5d4ffd9a6ccf72ca5ae4ae8700966116b9f3 (diff)
downloaderlang-7c212cefe9fdc4a3a788859d1298729f6fb8e5b9.tar.gz
[erts,kernel] Connection ID information
Diffstat (limited to 'lib')
-rw-r--r--lib/kernel/doc/src/net_kernel.xml219
-rw-r--r--lib/kernel/src/kernel.app.src2
-rw-r--r--lib/kernel/src/net_kernel.erl77
-rw-r--r--lib/kernel/test/erl_distribution_SUITE.erl99
-rw-r--r--lib/stdlib/src/erl_internal.erl1
-rw-r--r--lib/stdlib/src/stdlib.app.src2
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"]}
]}.