./ct_report/coverage/mongoose_start_node_id.COVER.html

1 %% Generates a unique ID on the node start.
2 %% Registers the ID on all other nodes.
3 %% Used in ejabberd_local to find to which node to route IQ responses.
4 -module(mongoose_start_node_id).
5 -behaviour(gen_server).
6
7 %% API
8 -export([start_link/0]).
9 -export([node_id/0, node_id_to_name/1]).
10 -export([register_on_remote_node_rpc/3]).
11
12 %% gen_server callbacks
13 -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
14 terminate/2, code_change/3]).
15
16 -ignore_xref([start_link/0, register_on_remote_node_rpc/3]).
17
18 -include("mongoose.hrl").
19 -include("mongoose_logger.hrl").
20
21 -type id() :: binary().
22
23 -record(state, {start_id :: id(), mon_ref_to_start_id :: map()}).
24 -define(KEY, ?MODULE).
25
26 -spec node_id() -> id().
27 node_id() ->
28 3 persistent_term:get(?KEY).
29
30 -spec node_id_to_name(id()) -> {ok, node()} | {error, unknown_id}.
31 node_id_to_name(ID) ->
32 3 case persistent_term:get({?KEY, ID}, undefined) of
33 undefined ->
34 1 {error, unknown_id};
35 Name ->
36 2 {ok, Name}
37 end.
38
39 -spec start_link() -> {ok, pid()}.
40 start_link() ->
41 93 gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
42
43 init(_) ->
44 93 net_kernel:monitor_nodes(true),
45 93 StartId = mongoose_bin:gen_from_crypto(),
46 93 persistent_term:put(mongoose_start_node_id, StartId),
47 93 [register_on_remote_node(RemoteNode, StartId)
48 93 || RemoteNode <- [node()|nodes()]],
49 93 {ok, #state{start_id = StartId, mon_ref_to_start_id = #{}}}.
50
51 handle_call(_Request, _From, State) ->
52
:-(
{reply, ok, State}.
53
54 handle_cast({register_cleaning_task, StartId, RemotePid},
55 State = #state{mon_ref_to_start_id = Mon2StartId}) ->
56 219 MonRef = erlang:monitor(process, RemotePid),
57 219 Mon2StartId2 = maps:put(MonRef, StartId, Mon2StartId),
58 219 {noreply, State#state{mon_ref_to_start_id = Mon2StartId2}};
59 handle_cast(_Msg, State) ->
60
:-(
{noreply, State}.
61
62 handle_info({nodeup, RemoteNode}, State = #state{start_id = StartId}) ->
63 10 register_on_remote_node(RemoteNode, StartId),
64 10 {noreply, State};
65 handle_info({'DOWN', MonRef, process, RemotePid, Reason},
66 State = #state{mon_ref_to_start_id = Mon2StartId}) ->
67 86 case maps:get(MonRef, Mon2StartId, undefined) of
68 undefined ->
69
:-(
?LOG_ERROR(#{what => node_id_unexpected_monitor,
70 reason => Reason,
71 monitor_ref => MonRef,
72 remote_pid => RemotePid,
73
:-(
remote_node => node(RemotePid)});
74 StartId ->
75 86 persistent_term:erase({?KEY, StartId}),
76 86 ?LOG_WARNING(#{what => node_id_node_down,
77 reason => Reason,
78 monitor_ref => MonRef,
79 remote_pid => RemotePid,
80
:-(
remote_node => node(RemotePid)})
81 end,
82 %% We use pid monitors instead of node monitors to avoid cleaning
83 %% start id when a node is restarting and reappearing very quicky.
84 %% I.e. node name could be reused by a newly started node, while Refs - not.
85 %% Pids could be also reused, but collisions are rare.
86 86 {noreply, State#state{mon_ref_to_start_id = maps:remove(MonRef, Mon2StartId)}};
87 handle_info(_Info, State) ->
88 8 {noreply, State}.
89
90 terminate(_Reason, _State) ->
91
:-(
ok.
92 code_change(_OldVsn, State, _Extra) ->
93
:-(
{ok, State}.
94
95 register_on_remote_node(RemoteNode, StartId) ->
96 217 Res = rpc:call(RemoteNode, ?MODULE, register_on_remote_node_rpc,
97 [node(), StartId, self()]),
98 217 case Res of
99 ok ->
100 217 ok;
101 _ ->
102
:-(
?LOG_ERROR(#{what => node_id_register_on_remote_node_failed,
103
:-(
remote_node => RemoteNode, reason => Res})
104 end.
105
106 register_on_remote_node_rpc(RemoteNode, StartId, RemotePid) ->
107 219 persistent_term:put({?KEY, StartId}, RemoteNode),
108 219 gen_server:cast(?MODULE, {register_cleaning_task, StartId, RemotePid}),
109 219 ok.
Line Hits Source