From 7c212cefe9fdc4a3a788859d1298729f6fb8e5b9 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Sat, 9 Apr 2022 03:27:19 +0200 Subject: [erts,kernel] Connection ID information --- lib/kernel/doc/src/net_kernel.xml | 219 ++++++++++++++++++++++------- lib/kernel/src/kernel.app.src | 2 +- lib/kernel/src/net_kernel.erl | 77 +++++++++- lib/kernel/test/erl_distribution_SUITE.erl | 99 +++++++++++-- lib/stdlib/src/erl_internal.erl | 1 + lib/stdlib/src/stdlib.app.src | 2 +- 6 files changed, 326 insertions(+), 74 deletions(-) (limited to 'lib') 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 @@ $ erl -sname foobar are stopped. Two option lists are considered the same if they contain the same set of options.

-

As from Kernel version 2.11.4, and ERTS version - 5.5.4, the following is guaranteed:

- -

nodeup messages are delivered before delivery - of any message from the remote node passed through the - newly established connection.

-

nodedown messages are not delivered until all - messages from the remote node that have been passed - through the connection have been delivered.

-
-

Notice that this is not guaranteed for Kernel - versions before 2.11.4.

-

As from Kernel version 2.11.4, subscriptions can also be - made before the net_kernel server is started, that is, - net_kernel:monitor_nodes/[1,2] does not return - ignored.

-

As from Kernel version 2.13, and ERTS version - 5.7, the following is guaranteed:

- -

nodeup messages are delivered after the +

Delivery guarantees of nodeup/nodedown messages:

+ +

+ nodeup messages are delivered before delivery + of any signals from the remote node through the newly + established connection. +

+ +

nodedown messages are delivered after all + the signals from the remote node over the connection + have been delivered. +

+

+ nodeup messages are delivered after the corresponding node appears in results from - erlang:nodes/X.

-

nodedown messages are delivered after the + erlang:nodes(). +

+ +

nodedown messages are delivered after the corresponding node has disappeared in results from - erlang:nodes/X.

+ erlang:nodes(). +

+

+ As of OTP @OTP-16362@, a nodedown message for a + connection being taken down will be delivered before a + nodeup message due to a new connection to the + same node. Prior to OTP @OTP-16362@, this was not + guaranteed to be the case. +

-

Notice that this is not guaranteed for Kernel - versions before 2.13.

The format of the node status change messages depends on Options. If Options is - [], which is the default, the format is as follows:

- + the empty list or if net_kernel:monitor_nodes/1 is called, + the format is as follows:

+ {nodeup, Node} | {nodedown, Node} Node = node() -

If Options is not [], the format is - as follows:

- -{nodeup, Node, InfoList} | {nodedown, Node, InfoList} +

+ When Options 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 + erlang:nodes/0. +

+

+ If Options equals anything other than the + empty list, the format of the status change messages is as follows: +

+ +{nodeup, Node, Info} | {nodedown, Node, Info} Node = node() - InfoList = [{Tag, Val}] -

InfoList is a list of tuples. Its contents depends on - Options, see below.

-

Also, when OptionList == [], only visible nodes, that - is, nodes that appear in the result of - erlang:nodes/0, - are monitored.

-

Option can be any of the following:

+ Info = #{Tag => Val} | [{Tag, Val}]
+

+ Info is either a map or a list of 2-tuples. Its content + depends on Options. If Options + is a map, Info will also be a map. If Options + is a list, Info will also be a list. +

+

+ When Options is a map, currently + the following associations are allowed: +

- {node_type, NodeType} + connection_id => boolean() + +

+ If the value of the association equals true, a + connection_id => ConnectionId association will be + included in the Info map where ConnectionId + is the connection identifier of the connection coming up + or going down. For more info about this connection + identifier see the documentation of + erlang:nodes/2. +

+
+ node_type => NodeType

Valid values for NodeType:

visible

Subscribe to node status change messages for visible - nodes only. The tuple {node_type, visible} is - included in InfoList.

+ nodes only. The association node_type => visible will + be included in the Info map.

hidden

Subscribe to node status change messages for hidden - nodes only. The tuple {node_type, hidden} is - included in InfoList.

+ nodes only. The association node_type => hidden will + be included in the Info map.

all

Subscribe to node status change messages for both - visible and hidden nodes. The tuple - {node_type, visible | hidden} is included in - InfoList.

+ visible and hidden nodes. The association + node_type => visible | hidden will be included in + the Info map.

+

+ If no node_type => NodeType association + is included in the Options map, the + caller will subscribe for status change messages for visible + nodes only, but no node_type => visible + association will be included in the Info map. +

- nodedown_reason + nodedown_reason => boolean() -

The tuple {nodedown_reason, Reason} is included in - InfoList in nodedown messages.

+

+ If the value of the association equals true, a + nodedown_reason => Reason association will be + included in the Info map for nodedown + messages. +

+

Reason 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:

connection_setup_failed @@ -263,6 +302,82 @@ $ erl -sname foobar
+

+ When Options is a list, currently + ListOption can be one of the following: +

+ + connection_id + +

+ A {connection_id, ConnectionId} tuple will be + included in Info where ConnectionId is the + connection identifier of the connection coming up or + going down. For more info about this connection identifier + see the documentation of + erlang:nodes/2. +

+
+ {node_type, NodeType} + +

Valid values for NodeType:

+ + visible +

Subscribe to node status change messages for visible + nodes only. The tuple {node_type, visible} will be + included in the Info list.

+ hidden +

Subscribe to node status change messages for hidden + nodes only. The tuple {node_type, hidden} will be + included in the Info list.

+ all +

Subscribe to node status change messages for both + visible and hidden nodes. The tuple + {node_type, visible | hidden} will be included in + the Info list.

+
+

+ If no {node_type, NodeType} option + has been given. The caller will subscribe for status + change messages for visible nodes only, but no + {node_type, visible} tuple will be included in the + Info list. +

+
+ nodedown_reason + +

+ The tuple {nodedown_reason, Reason} will be included + in the Info list for nodedown messages. +

+

+ See the documentation of the + nodedown_reason + => boolean() association above for information + about possible Reason values. +

+
+
+

Example:

+ +(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> + 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"]} ]}. -- cgit v1.2.1