net_kernel(3erl)

๐Ÿฆ‹ net_kernel(3erl) โ€” Erlang Module Definition โ€” Erlang networking kernel ๐Ÿฆ‹

โœจ NAME โœจ

net_kernel โ€” Erlang networking kernel. Yes bestie, this is the net_kernel(3erl) manual page, and we are gonna get into all of it. ๐Ÿ’–

๐Ÿฆ‹ DESCRIPTION ๐Ÿฆ‹

The net kernel is a system process, registered as net_kernel, and it must be operational for distributed Erlang to work. Its whole purpose is to implement parts of the BIFs spawn/4 and spawn_link/4, and to provide monitoring of the network. Periodt.

An Erlang node is started using command-line flag -name or -sname:

$ erl -sname foobar

It is also possible to call net_kernel:start(foobar, #{}) directly from the normal Erlang shell prompt:

1> net_kernel:start(foobar, #{name_domain => shortnames}).
{ok,<0.64.0>}
(foobar@gringotts)2>

If the node is started with command-line flag -sname, the node name is foobar@Host, where Host is the short name of the host (not the fully qualified domain name). If started with flag -name, the node name is foobar@Host, where Host is the fully qualified domain name. For more information, see erl.

Normally, connections are established automatically when another node is referenced. This functionality can be disabled by setting Kernel configuration parameter dist_auto_connect to never, see kernel(7). In this case, connections must be established explicitly by calling connect_node/1.

Which nodes that are allowed to communicate with each other is handled by the magic cookie system, see section Distributed Erlang in the Erlang Reference Manual.

โš ๏ธ Warning โš ๏ธ

Starting a distributed node without also specifying -proto_dist inet_tls will expose the node to attacks that may give the attacker complete access to the node and in extension the cluster. When using un-secure distributed nodes, make sure that the network is configured to keep potential attackers out. See the Using SSL for Erlang Distribution User's Guide for details on how to setup a secure distributed node.

๐ŸŒŸ EXPORTS ๐ŸŒŸ

๐Ÿ’… allow(Nodes) -> ok | error

Types:

Nodes = [node()]

Permits access to the specified set of nodes.

Before the first call to allow/1, any node with the correct cookie can be connected. When allow/1 is called, a list of allowed nodes is established. Any access attempts made from (or to) nodes not in that list will be rejected.

Subsequent calls to allow/1 will add the specified nodes to the list of allowed nodes. It is not possible to remove nodes from the list.

Returns error if any element in Nodes is not an atom. No cap.

๐Ÿ”Œ connect_node(Node) -> boolean() | ignored

Types:

Node = node()

Establishes a connection to Node. Returns true if a connection was established or was already established or if Node is the local node itself. Returns false if the connection attempt failed, and ignored if the local node is not alive.

โฑ๏ธ get_net_ticktime() -> Res

Types:

Res = NetTicktime | {ongoing_change_to, NetTicktime} | ignored
NetTicktime = integer() >= 1

Returns currently used net tick time in seconds. For more information see the net_ticktime kernel(7) parameter.

Defined return values (Res):

  • NetTicktime โ€” net_ticktime is NetTicktime seconds.
  • {ongoing_change_to, NetTicktime} โ€” net_kernel is currently changing net_ticktime to NetTicktime seconds.
  • ignored โ€” The local node is not alive.

๐Ÿ” getopts(Node, Options) -> {ok, OptionValues} | {error, Reason} | ignored

Types:

Node = node()
Options = [inet:socket_getopt()]
OptionValues = [inet:socket_setopt()]
Reason = inet:posix() | noconnection

Get one or more options for the distribution socket connected to Node.

If Node is a connected node the return value is the same as from inet:getopts(Sock, Options) where Sock is the distribution socket for Node.

Returns ignored if the local node is not alive or {error, noconnection} if Node is not connected.

๐Ÿ”ฎ get_state() -> distribution state map ๐Ÿ’ซ

Signature, exactly:

get_state() ->
             #{started => no | static | dynamic,
               name => atom(),
               name_type => static | dynamic,
               name_domain => shortnames | longnames}

Get the current state of the distribution for the local node.

Returns a map with (at least) the following key-value pairs:

  • started => Started โ€” Valid values for Started:
    • no โ€” The distribution is not started. In this state none of the other keys below are present in the map.
    • static โ€” The distribution was started with command line arguments -name or -sname.
    • dynamic โ€” The distribution was started with net_kernel:start/1 and can be stopped with net_kernel:stop/0.
  • name => Name โ€” The name of the node. Same as returned by erlang:node/0 except when name_type is dynamic in which case Name may be undefined (instead of nonode@nohost).
  • name_type => NameType โ€” Valid values for NameType:
    • static โ€” The node has a static node name set by the node itself.
    • dynamic โ€” The distribution was started in dynamic node name mode, and will get its node name assigned from the first node it connects to. If key name has value undefined that has not happened yet.
  • name_domain => NameDomain โ€” Valid values for NameDomain:
    • shortnames โ€” The distribution was started to use node names with a short host portion (not fully qualified).
    • longnames โ€” The distribution was started to use node names with a long fully qualified host portion.

๐Ÿ‘€ monitor_nodes(Flag) -> ok | Error

๐Ÿ‘€ monitor_nodes(Flag, Options) -> ok | Error

Types:

Flag = boolean()
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()}

The calling process subscribes or unsubscribes to node status change messages. A nodeup message is delivered to all subscribing processes when a new node is connected, and a nodedown message is delivered when a node is disconnected.

If Flag is true, a new subscription is started. If Flag is false, all previous subscriptions started with the same Options are stopped. Two option lists are considered the same if they contain the same set of options.

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().
  • nodedown messages are delivered after the corresponding node has disappeared in results from erlang:nodes().
  • As of OTP 23.0, 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 23.0, this was not guaranteed to be the case.

The format of the node status change messages depends on Options. If Options is the empty list or if net_kernel:monitor_nodes/1 is called, the format is as follows:

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

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()
  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:

  • 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 association node_type => visible will be included in the Info map.
    • hidden โ€” Subscribe to node status change messages for hidden 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 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 => boolean() โ€” 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, but for the standard TCP distribution module it is one of the following:

  • connection_id โ€” a {connection_id, ConnectionId} tuple gets slipped into Info, where ConnectionId is the connection identifier of the connection coming up or going down ๐Ÿ™ˆ for more on that identifier, go peep the docs for erlang:nodes/2.
  • {node_type, NodeType} โ€” valid values for NodeType are:
    • visible โ€” you only get node status change messages for visible nodes. The tuple {node_type, visible} shows up in the Info list ๐Ÿ’…
    • hidden โ€” you only get node status change messages for hidden nodes. The tuple {node_type, hidden} shows up in the Info list ๐ŸŒธ
    • all โ€” you get node status change messages for both visible and hidden nodes. The tuple {node_type, visible | hidden} shows up in the Info list โœจ

If no {node_type, NodeType} option is given, the caller subscribes to status change messages for visible nodes only โ€” but no {node_type, visible} tuple gets included in the Info list. iykyk ๐Ÿ˜Œ

  • nodedown_reason โ€” the tuple {nodedown_reason, Reason} gets included in the Info list for nodedown messages. For the possible Reason values, see the docs on the nodedown_reason => boolean() association above ๐Ÿ‘†

Example, and yes it's giving beautiful:

(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>

โœจ set_net_ticktime(NetTicktime) -> Res โœจ

โœจ set_net_ticktime(NetTicktime, TransitionPeriod) -> Res โœจ

Types:

NetTicktime = integer() >= 1
TransitionPeriod = integer() >= 0
Res =
    unchanged | change_initiated |
    {ongoing_change_to, NewNetTicktime}
NewNetTicktime = integer() >= 1

Sets net_ticktime (see kernel(7)) to NetTicktime seconds. TransitionPeriod defaults to 60 ๐ŸŽ€

Some definitions, bestie, so we're all on the same page:

  • Minimum transition traffic interval (MTTI) โ€” minimum(NetTicktime, PreviousNetTicktime)*1000 div 4 milliseconds.
  • Transition period โ€” the time of the least number of consecutive MTTIs to cover TransitionPeriod seconds following the call to set_net_ticktime/2 (that is, ((TransitionPeriod*1000 - 1) div MTTI + 1)*MTTI milliseconds).

If NetTicktime < PreviousNetTicktime, the net_ticktime change happens at the end of the transition period; otherwise it happens at the beginning ๐Ÿ”ฅ During the transition period, net_kernel makes sure there's outgoing traffic on all connections at least every MTTI milliseconds.

Note: the net_ticktime changes must be initiated on all nodes in the network (with the same NetTicktime) before the end of any transition period on any node โ€” otherwise connections can erroneously be disconnected ๐Ÿ˜ญ like, actually please don't do that.

Returns one of these:

  • unchanged โ€” net_ticktime already has the value of NetTicktime and is left unchanged ๐Ÿ’…
  • change_initiated โ€” net_kernel initiated the change of net_ticktime to NetTicktime seconds.
  • {ongoing_change_to, NewNetTicktime} โ€” the request is ignored because net_kernel is busy changing net_ticktime to NewNetTicktime seconds ๐Ÿ™ˆ

โœจ setopts(Node, Options) -> ok | {error, Reason} | ignored โœจ

Types:

Node = node() | new
Options = [inet:socket_setopt()]
Reason = inet:posix() | noconnection

Sets one or more options for distribution sockets. Node can be either one node name, or the atom new to affect the distribution sockets of all future connected nodes ๐ŸŒˆ

The return value is the same as from inet:setopts/2, or {error, noconnection} if Node isn't a connected node or new ๐Ÿ’€

If Node is new, the Options will then also be added to the kernel configuration parameters inet_dist_listen_options and inet_dist_connect_options.

Returns ignored if the local node is not alive ๐Ÿซถ

โœจ start(Name, Options) -> {ok, pid()} | {error, Reason} โœจ

Types:

Options =
    #{name_domain => NameDomain,
      net_ticktime => NetTickTime,
      net_tickintensity => NetTickIntensity,
      dist_listen => boolean(),
      hidden => boolean()}
Name = atom()
NameDomain = shortnames | longnames
NetTickTime = integer() >= 1
NetTickIntensity = 4..1000
Reason = {already_started, pid()} | term()

Turns a non-distributed node into a distributed node by starting net_kernel and other necessary processes ๐Ÿ‘‘ main character energy fr.

If Name is set to undefined, the distribution will be started to request a dynamic node name from the first node it connects to. See Dynamic Node Name. Setting Name to undefined implies options dist_listen => false and hidden => true โœจ

Currently supported options:

  • name_domain => NameDomain โ€” determines the host name part of the node name. If NameDomain equals longnames, fully qualified domain names will be used, which also is the default. If NameDomain equals shortnames, only the short name of the host will be used ๐ŸŒธ
  • net_ticktime => NetTickTime โ€” net tick time to use, in seconds. Defaults to the value of the net_ticktime kernel(7) parameter. For more info about net tick time, see the kernel parameter. But โ€” note that if the value of the kernel parameter is invalid, it will silently be replaced by a valid value; however, if an invalid NetTickTime value is passed as option value to this function, the call will fail ๐Ÿ’€
  • net_tickintensity => NetTickIntensity โ€” net tick intensity to use. Defaults to the value of the net_tickintensity kernel(7) parameter. For more info about net tick intensity, see the kernel parameter. But โ€” same deal: if the value of the kernel parameter is invalid, it will silently be replaced by a valid value, but if an invalid NetTickIntensity value is passed as option value to this function, the call will fail ๐Ÿ˜ญ
  • dist_listen => boolean() โ€” enable or disable listening for incoming connections. Defaults to the value of the -dist_listen erl command line argument. Note that dist_listen => false implies hidden => true. If undefined has been passed as Name, the dist_listen option will be overridden with dist_listen => false ๐Ÿฆ‹
  • hidden => boolean() โ€” enable or disable hidden node. Defaults to true if the -hidden erl command line argument has been passed; otherwise false. If undefined has been passed as Name, or the option dist_listen equals false, the hidden option will be overridden with hidden => true ๐Ÿ’…

โœจ start(Options) -> {ok, pid()} | {error, Reason} โœจ

Types:

Options = [Name | NameDomain | TickTime, ...]
Name = atom()
NameDomain = shortnames | longnames
TickTime = integer() >= 1
Reason = {already_started, pid()} | term()
Warning: start/1 is deprecated. Use start/2 instead ๐Ÿ™ˆ (she's iconic but she's retired, okay?)

Turns a non-distributed node into a distributed node by starting net_kernel and other necessary processes.

The Options list can only be exactly one of the following lists (order is imporant โ€” that's their typo, not mine, but the ordering is important bestie):

  • [Name] โ€” the same as net_kernel:start([Name, longnames, 15000]).
  • [Name, NameDomain] โ€” the same as net_kernel:start([Name, NameDomain, 15000]).
  • [Name, NameDomain, TickTime] โ€” the same as net_kernel:start(Name, #{name_domain => NameDomain, net_ticktime => ((TickTime*4-1) div 1000) + 1, net_tickintensity => 4}). Note that TickTime is not the same as net tick time expressed in milliseconds. TickTime is the time between ticks when net tick intensity equals 4 ๐ŸŽ€

โœจ stop() -> ok | {error, Reason} โœจ

Types:

Reason = not_allowed | not_found

Turns a distributed node into a non-distributed node. For other nodes in the network, this is the same as the node going down ๐Ÿ’€ Only possible when the net kernel was started using start/2 โ€” otherwise {error, not_allowed} is returned. Returns {error, not_found} if the local node is not alive ๐Ÿซถ


okay bestie that's literally the whole page ๐ŸŽ€ no cap, you just read all of net_kernel โ€” every function, every return value, every lil' caveat. go forth and be the main character of your own distributed Erlang cluster ๐Ÿ’…โœจ

quick vibe check before you sprint off: re-read the bits about what happens when the net kernel isn't up yet, because that's the classic "why is my shell screaming at me" moment ๐Ÿ˜ญ

you got this fr fr ๐Ÿฆ‹๐ŸŒธ๐Ÿ’–


this page is net_kernel(3erl) from Ericsson AB, and it ships with kernel 8.5.4.2 โ€” that's your source and your version, bestie ๐Ÿ‘‘ ๐ŸŽ€

๐Ÿ“ฆ where this page comes from