<commit_before>% Copyright (c) 2010, NorthScale, Inc.
% All rights reserved.

-module(ns_node_disco).

-behaviour(gen_server).
-behavior(ns_log_categorizing).

-define(PING_FREQ, 60000).
-define(NODE_CHANGE_DELAY, 5000).

-define(COOKIE_INHERITED, 1).
-define(COOKIE_SYNCHRONIZED, 2).
-define(COOKIE_GEN, 3).
-define(NODE_UP, 4).
-define(NODE_DOWN, 5).

%% API
-export([start_link/0,
         nodes_wanted/0,
         nodes_wanted_updated/0,
         nodes_actual/0,
         nodes_actual_proper/0,
         nodes_actual_other/0,
         cookie_init/0, cookie_gen/0,
         cookie_get/0, cookie_set/1, cookie_sync/0]).

-export([ns_log_cat/1, ns_log_code_string/1]).

%% gen_server

-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
         terminate/2, code_change/3]).

-record(state, {nodes, timer}).

-include_lib("eunit/include/eunit.hrl").

% Node Discovery and monitoring.
%
start_link() ->
    gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).

% Returns all nodes that we see.
%
% TODO: Track flapping nodes and attenuate, or figure out
%       how to configure OTP to do it, if not already.

nodes_actual() ->
    lists:sort(nodes([this, visible])).

nodes_actual_proper() ->
    gen_server:call(?MODULE, nodes_actual_proper).

% Returns nodes_actual_proper(), but with self node() filtered out.
% This is not the same as nodes([visible]), because this function may
% return a subset of nodes([visible]), similar to nodes_actual_proper().

nodes_actual_other() ->
    lists:subtract(nodes_actual_proper(), [node()]).

nodes_wanted() ->
    gen_server:call(?MODULE, nodes_wanted).

% API's used as callbacks that are invoked when ns_config
% keys have changed.

nodes_wanted_updated() ->
    gen_server:call(?MODULE, nodes_wanted_updated).

%% gen_server implementation

init([]) ->
    error_logger:info_msg("Initting ns_node_disco with ~p~n",
                          [do_nodes_wanted()]),
    % Proactively run one round of reconfiguration update.
    do_nodes_wanted_updated(do_nodes_wanted()),
    % Register for nodeup/down messages as handle_info callbacks.
    ok = net_kernel:monitor_nodes(true),
    {ok, Timer} = timer:send_interval(?PING_FREQ, ping_all),
    % Track the last list of actual ndoes.
    {ok, #state{nodes = false, timer = Timer}}.

terminate(_Reason, State) ->
    timer:cancel(State#state.timer),
    ok.
code_change(_OldVsn, State, _) -> {ok, State}.
handle_cast(_Msg, State)       -> {noreply, State}.

% Read from ns_config nodes_wanted, and add ourselves to the
% nodes_wanted list, if not already there.

handle_call(nodes_actual_proper, _From, State) ->
    {reply, do_nodes_actual_proper(), State};

handle_call(nodes_wanted, _From, State) ->
    {reply, do_nodes_wanted(), State};

handle_call(nodes_wanted_updated, _From, State) ->
    {reply, do_nodes_wanted_updated(do_nodes_wanted()), State};

handle_call(Msg, _From, State) ->
    error_logger:info_msg("Unhandled ~p call: ~p~n", [?MODULE, Msg]),
    {reply, error, State}.

handle_info({nodeup, Node}, State) ->
    ns_log:log(?MODULE, ?NODE_UP, "Node ~p saw that node ~p came up.",
               [node(), Node]),
    % We might be tempted to proactively push/pull/sync
    % our configs with the "new" Node.  Instead, it's
    % cleaner to asynchronous do a gen_event:notify()
    % Delay the notification for five seconds to give the
    % config a chance to settle before notifying clients.
    {ok, _Tref} = timer:send_after(?NODE_CHANGE_DELAY, notify_clients),
    %% Have every known node ping this new node.
    rpc:multicall(net_adm, ping, [Node]),
    {noreply, State};

handle_info({nodedown, Node}, State) ->
    ns_log:log(?MODULE, ?NODE_DOWN, "Node ~p saw that node ~p went down.",
               [node(), Node]),
    {ok, _Tref} = timer:send_after(?NODE_CHANGE_DELAY, notify_clients),
    {noreply, State};

handle_info(notify_clients, State) ->
    State2 = do_notify(State),
    {noreply, State2};

handle_info(ping_all, State) ->
    ping_all(),
    {noreply, State};

handle_info(_Msg, State) -> {noreply, State}.

% -----------------------------------------------------------

% Read from ns_config nodes_wanted.

do_nodes_wanted_raw() ->
    case ns_config:search(ns_config:get(), nodes_wanted) of
        {value, L} -> lists:usort(L);
        false      -> []
    end.

do_nodes_wanted() ->
    W1 = do_nodes_wanted_raw(),
    W2 = lists:usort([node() | W1]),
    case W2 =/= W1 of
        true  -> ns_config:set(nodes_wanted, W2);
        false -> ok
    end,
    W2.

do_nodes_wanted_updated(NodeListIn) ->
    {ok, Cookie} = cookie_sync(),
    NodeList = lists:usort(NodeListIn),
    error_logger:info_msg("ns_node_disco: nodes_wanted updated: ~p, with cookie: ~p~n",
                          [NodeList, erlang:get_cookie()]),
    erlang:set_cookie(node(), Cookie),<commit_msg>Only modify nodes_wanted inside of ns_cluster (bug 809)<commit_after>% Copyright (c) 2010, NorthScale, Inc.
% All rights reserved.

-module(ns_node_disco).

-behaviour(gen_server).
-behavior(ns_log_categorizing).

-define(PING_FREQ, 60000).
-define(NODE_CHANGE_DELAY, 5000).

-define(COOKIE_INHERITED, 1).
-define(COOKIE_SYNCHRONIZED, 2).
-define(COOKIE_GEN, 3).
-define(NODE_UP, 4).
-define(NODE_DOWN, 5).

%% API
-export([start_link/0,
         nodes_wanted/0,
         nodes_wanted_updated/0,
         nodes_actual/0,
         nodes_actual_proper/0,
         nodes_actual_other/0,
         cookie_init/0, cookie_gen/0,
         cookie_get/0, cookie_set/1, cookie_sync/0]).

-export([ns_log_cat/1, ns_log_code_string/1]).

%% gen_server

-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
         terminate/2, code_change/3]).

-record(state, {nodes, timer}).

-include_lib("eunit/include/eunit.hrl").

% Node Discovery and monitoring.
%
start_link() ->
    gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).

% Returns all nodes that we see.
%
% TODO: Track flapping nodes and attenuate, or figure out
%       how to configure OTP to do it, if not already.

nodes_actual() ->
    lists:sort(nodes([this, visible])).

nodes_actual_proper() ->
    gen_server:call(?MODULE, nodes_actual_proper).

% Returns nodes_actual_proper(), but with self node() filtered out.
% This is not the same as nodes([visible]), because this function may
% return a subset of nodes([visible]), similar to nodes_actual_proper().

nodes_actual_other() ->
    lists:subtract(nodes_actual_proper(), [node()]).

nodes_wanted() ->
    gen_server:call(?MODULE, nodes_wanted).

% API's used as callbacks that are invoked when ns_config
% keys have changed.

nodes_wanted_updated() ->
    gen_server:call(?MODULE, nodes_wanted_updated).

%% gen_server implementation

init([]) ->
    error_logger:info_msg("Initting ns_node_disco with ~p~n", [nodes()]),
    % Proactively run one round of reconfiguration update.
    do_nodes_wanted_updated(do_nodes_wanted()),
    % Register for nodeup/down messages as handle_info callbacks.
    ok = net_kernel:monitor_nodes(true),
    {ok, Timer} = timer:send_interval(?PING_FREQ, ping_all),
    % Track the last list of actual ndoes.
    {ok, #state{nodes = false, timer = Timer}}.

terminate(_Reason, State) ->
    timer:cancel(State#state.timer),
    ok.
code_change(_OldVsn, State, _) -> {ok, State}.
handle_cast(_Msg, State)       -> {noreply, State}.

% Read from ns_config nodes_wanted, and add ourselves to the
% nodes_wanted list, if not already there.

handle_call(nodes_actual_proper, _From, State) ->
    {reply, do_nodes_actual_proper(), State};

handle_call(nodes_wanted, _From, State) ->
    {reply, do_nodes_wanted(), State};

handle_call(nodes_wanted_updated, _From, State) ->
    {reply, do_nodes_wanted_updated(do_nodes_wanted()), State};

handle_call(Msg, _From, State) ->
    error_logger:info_msg("Unhandled ~p call: ~p~n", [?MODULE, Msg]),
    {reply, error, State}.

handle_info({nodeup, Node}, State) ->
    ns_log:log(?MODULE, ?NODE_UP, "Node ~p saw that node ~p came up.",
               [node(), Node]),
    % We might be tempted to proactively push/pull/sync
    % our configs with the "new" Node.  Instead, it's
    % cleaner to asynchronous do a gen_event:notify()
    % Delay the notification for five seconds to give the
    % config a chance to settle before notifying clients.
    {ok, _Tref} = timer:send_after(?NODE_CHANGE_DELAY, notify_clients),
    %% Have every known node ping this new node.
    rpc:multicall(net_adm, ping, [Node]),
    {noreply, State};

handle_info({nodedown, Node}, State) ->
    ns_log:log(?MODULE, ?NODE_DOWN, "Node ~p saw that node ~p went down.",
               [node(), Node]),
    {ok, _Tref} = timer:send_after(?NODE_CHANGE_DELAY, notify_clients),
    {noreply, State};

handle_info(notify_clients, State) ->
    State2 = do_notify(State),
    {noreply, State2};

handle_info(ping_all, State) ->
    ping_all(),
    {noreply, State};

handle_info(_Msg, State) -> {noreply, State}.

% -----------------------------------------------------------

% Read from ns_config nodes_wanted.

do_nodes_wanted() ->
    case ns_config:search(ns_config:get(), nodes_wanted) of
        {value, L} -> lists:usort(L);
        false      -> []
    end.

do_nodes_wanted_updated(NodeListIn) ->
    {ok, Cookie} = cookie_sync(),
    NodeList = lists:usort(NodeListIn),
    error_logger:info_msg("ns_node_disco: nodes_wanted updated: ~p, with cookie: ~p~n",
                          [NodeList, erlang:get_cookie()]),
    erlang:set_cookie(node(), Cookie),
{'fraction_non_alphanumeric': 0.1477801268498943, 'fraction_numerical': 0.009302325581395349, 'mean_word_length': 3.5904900533721493, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 52, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '731431', 'n_tokens_mistral': 3346, 'n_tokens_neox': 3161, 'n_words': 769}