Packages

Elects master node from Erlang/Elixir cluster that is agreed by all nodes.

Current section

Files

Jump to
elector src strategy elector_strategy_behaviour.erl
Raw

src/strategy/elector_strategy_behaviour.erl

%%%-----------------------------------------------------------------------------
%% @doc Defines the base behaviour for election strategies.
%% @end
%%%-----------------------------------------------------------------------------
-module(elector_strategy_behaviour).
%%------------------------------------------------------------------------------
%% Callbacks
%%------------------------------------------------------------------------------
-callback elect(CandidateNodes :: candidate_nodes()) -> Leader :: leader().
%%------------------------------------------------------------------------------
%% Exported API
%%------------------------------------------------------------------------------
-export([elect/0, candidate_nodes/0, is_local_candidate/0]).
%%------------------------------------------------------------------------------
%% Type definitions
%%------------------------------------------------------------------------------
-type leader() :: node().
-type candidate_nodes() :: [node()].
%%------------------------------------------------------------------------------
%% Exported functions
%%------------------------------------------------------------------------------
%% @doc Starts the election process by triggering the strategy
%% modules elect() function.
%% The election implementation should only contain the logic for
%% selecting the leader node and returning the leader node name.
%% The elect/0 function is triggered on all nodes automatically
%% by the elector application. This means the strategy implementation
%% does not have to worry about starting the election on all nodes.
%% @end
-spec elect() -> Leader :: leader().
elect() ->
CandidateNodes = candidate_nodes(),
erlang:apply(
elector_config_handler:strategy_module(), elect, [CandidateNodes]
).
%% @doc Returns the candidate nodes for the next election.
%%
%% Queries each connected node live (in parallel) for its current candidate
%% status. The async candidate cache races with `candidate_status'
%% propagation during cluster join — a node electing against an unpopulated
%% cache self-elects and the wrong leader can stick — so we ask the source
%% of truth on every election instead.
%% @end
-spec candidate_nodes() -> CandidateNodes :: candidate_nodes().
candidate_nodes() ->
Nodes = [node() | nodes()],
Results = erpc:multicall(Nodes, ?MODULE, is_local_candidate, [], 500),
[Node || {Node, {ok, true}} <- lists:zip(Nodes, Results)].
%% @doc Returns whether the local node is currently a candidate. Exported so
%% remote nodes can query it via erpc from `candidate_nodes/0'.
%% @end
-spec is_local_candidate() -> boolean().
is_local_candidate() ->
case erlang:whereis(elector_candidate) of
undefined -> false;
Pid ->
try gen_server:call(Pid, is_candidate_node, 300) of
{ok, true} -> true;
_ -> false
catch
_:_ -> false
end
end.