Module gen_leader

This module defines the gen_leader behaviour.
Required callback functions: init/1, elected/3, surrendered/3, handle_leader_call/4, handle_leader_cast/3, from_leader/3, handle_call/4, handle_cast/3, handle_DOWN/3, handle_info/2, terminate/2, code_change/4.

Authors: Hans Svensson (hanssv@chalmers.se), Thomas Arts (thomas.arts@ituniv.se), Ulf Wiger (ulf.wiger@ericsson.com), (contributor: Serge Aleynikov (saleyn@gmail.com).

Description

Leader election behavior.

This application implements a leader election behavior modeled after gen_server. This behavior intends to make it reasonably straightforward to implement a fully distributed server with master-slave semantics.

The gen_leader behavior supports nearly everything that gen_server does (some functions, such as multicall() and the internal timeout, have been removed), and adds a few callbacks and API functions to support leader election etc.

Also included is an example program, a global dictionary, based on the modules gen_leader and dict. The callback implementing the global dictionary is called 'test_cb', for no particularly logical reason.

New version: The internal leader election algorithm was faulty and has been replaced with a new version based on a different leader election algorithm. As a consequence of this the query functions alive and down can no longer be provided. The new algorithm also make use of an incarnation parameter, by default written to disk in the function incarnation. This implies that only one gen_leader per node is permitted, if used in a diskless environment, incarnation must be adapted.

Modifications contributed by Serge Aleynikov:

  1. Added configurable startup options (see leader_options() type)
  2. Implemented handle_DOWN/3 callback with propagation of the leader's state via broadcast to all connected candidates.
  3. Fixed population of the #election.down member so that down/1 query can be used in the behavior's implementation
  4. Rewrote implementation of the tau timer to prevent the leader looping on the timer timeout event when all candidates are connected.

Data Types

bcast_type()

bcast_type() = all | sender

caller_ref()

caller_ref() = {pid(), reference()}

elid()

elid() = {priority(), incarn(), lclock()}

incarn()

incarn() = non_neg_integer()

lclock()

lclock() = non_neg_integer()

mon_ref()

mon_ref() = reference()

name()

name() = atom()

option()

option() = 
    {workers, Workers :: [node()]} |
    {vardir, Dir :: string()} |
    {bcast_type, Type :: bcast_type()} |
    {heartbeat, Seconds :: integer()}

options()

options() = [option()]

priority()

priority() = integer()

server_ref()

server_ref() = 
    name() | {name(), node()} | {global, name()} | pid()

start_ret()

start_ret() = {ok, pid()} | {error, term()}

status()

status() = 
    elec1 | elec2 | wait | joining | worker | waiting_worker |
    norm

Function Index

alive/1Returns list of alive nodes.
behaviour_info/1
broadcast/3
call/2Equivalent to gen_server:call/2, but with a slightly different exit reason if something goes wrong.
call/3Equivalent to gen_server:call/3, but with a slightly different exit reason if something goes wrong.
candidates/1Returns a list of known candidates.
cast/2Equivalent to gen_server:cast / 2.
down/1Returns list of down nodes.
leader_call/2Makes a call (similar to gen_server:call/2) to the leader.
leader_call/3Makes a call (similar to gen_server:call/3) to the leader.
leader_cast/2Similar to gen_server:cast/2 but will be forwarded to the leader via the local gen_leader instance.
leader_node/1Returns the current leader node.
reply/2Equivalent to gen_server:reply / 2.
start/6Starts a gen_leader process without linking to the parent.
start_link/6Starts a gen_leader process.
workers/1Returns a list of known workers.

Function Details

alive/1

alive(Election ::
          #election{leader = none | pid(),
                    previous_leader = none | pid(),
                    name = name(),
                    leadernode = node(),
                    candidate_nodes = [node()],
                    worker_nodes = [node()],
                    down = [node()],
                    monitored = [{mon_ref(), node()}],
                    buffered = [{reference(), caller_ref()}],
                    seed_node = none | node(),
                    status = status(),
                    elid = elid(),
                    acks = [node()],
                    work_down = [node()],
                    cand_timer_int = integer(),
                    cand_timer = term(),
                    pendack = node(),
                    incarn = incarn(),
                    nextel = integer(),
                    bcast_type = bcast_type()}) ->
         [node()]

Returns list of alive nodes.

behaviour_info/1

behaviour_info(Other :: atom()) -> undefined | [{atom(), arity()}]

broadcast/3

broadcast(X1, ToNodes, E) -> any()

call/2

call(Name :: server_ref(), Request :: term()) -> term()

Equivalent to gen_server:call/2, but with a slightly different exit reason if something goes wrong. This function calls the gen_leader process exactly as if it were a gen_server (which, for practical purposes, it is.)

call/3

call(Name :: server_ref(),
     Request :: term(),
     Timeout :: integer()) ->
        term()

Equivalent to gen_server:call/3, but with a slightly different exit reason if something goes wrong. This function calls the gen_leader process exactly as if it were a gen_server (which, for practical purposes, it is.)

candidates/1

candidates(Election ::
               #election{leader = none | pid(),
                         previous_leader = none | pid(),
                         name = name(),
                         leadernode = node(),
                         candidate_nodes = [node()],
                         worker_nodes = [node()],
                         down = [node()],
                         monitored = [{mon_ref(), node()}],
                         buffered = [{reference(), caller_ref()}],
                         seed_node = none | node(),
                         status = status(),
                         elid = elid(),
                         acks = [node()],
                         work_down = [node()],
                         cand_timer_int = integer(),
                         cand_timer = term(),
                         pendack = node(),
                         incarn = incarn(),
                         nextel = integer(),
                         bcast_type = bcast_type()}) ->
              [node()]

Returns a list of known candidates.

cast/2

cast(Name :: name() | pid(), Request :: term()) -> ok

Equivalent to gen_server:cast / 2.

down/1

down(Election ::
         #election{leader = none | pid(),
                   previous_leader = none | pid(),
                   name = name(),
                   leadernode = node(),
                   candidate_nodes = [node()],
                   worker_nodes = [node()],
                   down = [node()],
                   monitored = [{mon_ref(), node()}],
                   buffered = [{reference(), caller_ref()}],
                   seed_node = none | node(),
                   status = status(),
                   elid = elid(),
                   acks = [node()],
                   work_down = [node()],
                   cand_timer_int = integer(),
                   cand_timer = term(),
                   pendack = node(),
                   incarn = incarn(),
                   nextel = integer(),
                   bcast_type = bcast_type()}) ->
        [node()]

Returns list of down nodes.

leader_call/2

leader_call(Name :: server_ref(), Request :: term()) -> term()

Makes a call (similar to gen_server:call/2) to the leader. The call is forwarded via the local gen_leader instance, if that one isn't actually the leader. The client will exit if the leader dies while the request is outstanding.

This function uses gen:call/3, and is subject to the same default timeout as e.g. gen_server:call/2.

leader_call/3

leader_call(Name :: server_ref(),
            Request :: term(),
            Timeout :: integer()) ->
               term()

Makes a call (similar to gen_server:call/3) to the leader. The call is forwarded via the local gen_leader instance, if that one isn't actually the leader. The client will exit if the leader dies while the request is outstanding.

leader_cast/2

leader_cast(Name :: name() | pid(), Request :: term()) -> ok

Similar to gen_server:cast/2 but will be forwarded to the leader via the local gen_leader instance.

leader_node/1

leader_node(Election ::
                #election{leader = none | pid(),
                          previous_leader = none | pid(),
                          name = name(),
                          leadernode = node(),
                          candidate_nodes = [node()],
                          worker_nodes = [node()],
                          down = [node()],
                          monitored = [{mon_ref(), node()}],
                          buffered =
                              [{reference(), caller_ref()}],
                          seed_node = none | node(),
                          status = status(),
                          elid = elid(),
                          acks = [node()],
                          work_down = [node()],
                          cand_timer_int = integer(),
                          cand_timer = term(),
                          pendack = node(),
                          incarn = incarn(),
                          nextel = integer(),
                          bcast_type = bcast_type()}) ->
               node() | none

Returns the current leader node.

reply/2

reply(From :: caller_ref(), Reply :: term()) -> term()

Equivalent to gen_server:reply / 2.

start/6

start(Name :: atom(),
      CandidateNodes :: [node()],
      OptArgs :: options(),
      Mod :: module(),
      Arg :: term(),
      Options :: list()) ->
         start_ret()

Starts a gen_leader process without linking to the parent.

See also: start_link/6.

start_link/6

start_link(Name :: atom(),
           CandidateNodes :: [node()],
           OptArgs :: options(),
           Mod :: module(),
           Arg :: term(),
           Options :: list()) ->
              start_ret()

Starts a gen_leader process.
NameThe locally registered name of the process
CandidateNodesThe names of nodes capable of assuming a leadership role
OptArgs Optional arguments given to gen_leader.
{workers, Workers}
The names of nodes that will be part of the "cluster", but cannot ever assume a leadership role. Default: [].
{vardir, Dir}
Directory name used to store candidate's incarnation cookie. Default: "."
{bcast_type, Type}
When Type is 'all' each election event (when a new candidate becomes visible to the leader) will be broadcast to all live candidate nodes. Each candidate will get a from_leader/3 callback. When Type is sender, only the newly registered candidate will get the surrendered/3 callback. Default: sender.
{heartbeat, Seconds}
Heartbeat timeout value used to send ping messages to inactive candidate nodes.
ModThe name of the callback module
ArgArgument passed on to Mod:init/1
OptionsSame as gen_server's Options

The list of candidates needs to be known from the start. Workers could potentially be added at runtime, but no functionality to do this is provided by this version.

workers/1

workers(Election ::
            #election{leader = none | pid(),
                      previous_leader = none | pid(),
                      name = name(),
                      leadernode = node(),
                      candidate_nodes = [node()],
                      worker_nodes = [node()],
                      down = [node()],
                      monitored = [{mon_ref(), node()}],
                      buffered = [{reference(), caller_ref()}],
                      seed_node = none | node(),
                      status = status(),
                      elid = elid(),
                      acks = [node()],
                      work_down = [node()],
                      cand_timer_int = integer(),
                      cand_timer = term(),
                      pendack = node(),
                      incarn = incarn(),
                      nextel = integer(),
                      bcast_type = bcast_type()}) ->
           [node()]

Returns a list of known workers.