Example callback module for the locks_leader behaviour.
Behaviours: locks_leader
.
Authors: Ulf Wiger (ulf.wiger@feuerlabs.com
), Thomas Arts (thomas.arts@quviq.com
).
This particular callback module implements a global dictionary,
and is the back-end for the gdict
module.
broadcast() = term()
Whatever the leader decides to broadcast to the candidates.
commonReply() = {ok, state()} | {ok, broadcast(), state()} | {stop, reason(), state()}
Common set of valid replies from most callback functions.
dictionary() = tuple()
Same as from dict:new(); used in this module as State.
info() = term()
Opaque state of the gen_leader behaviour.
reason() = term()
Error information.
state() = dictionary()
Internal server state; In the general case, it can be any term.
code_change/4 | Similar to code_change/3 in a gen_server callback module, with
the exception of the added argument. |
elected/3 | Called by the leader when it is elected leader, and each time a candidate recognizes the leader. |
from_leader/3 | Called by each candidate in response to a message from the leader. |
handle_DOWN/3 | Called by the leader when it detects loss of a candidate. |
handle_call/4 | Equivalent to Mod:handle_call/3 in a gen_server. |
handle_cast/3 | Equivalent to Mod:handle_call/3 in a gen_server, except
(NOTE) for the possible return values. |
handle_info/3 | Equivalent to Mod:handle_info/3 in a gen_server,
except (NOTE) for the possible return values. |
handle_leader_call/4 | Called by the leader in response to a leader_call(). |
handle_leader_cast/3 | Called by the leader in response to a leader_cast(). |
init/1 | Equivalent to the init/1 function in a gen_server. |
surrendered/3 | Called by each candidate when it recognizes another instance as leader. |
terminate/2 | Equivalent to terminate/2 in a gen_server callback
module. |
code_change(FromVsn::string(), OldState::term(), I::info(), Extra::term()) -> {ok, NState}
NState = state()
Similar to code_change/3
in a gen_server callback module, with
the exception of the added argument.
elected(State::state(), I::info(), Cand::pid() | undefined) -> {ok, Broadcast, NState} | {reply, Msg, NState} | {ok, AmLeaderMsg, FromLeaderMsg, NState} | {error, term()}
Broadcast = broadcast()
NState = state()
Called by the leader when it is elected leader, and each time a candidate recognizes the leader.
This function is only called in the leader instance, and Broadcast
will be sent to all candidates (when the leader is first elected),
or to the new candidate that has appeared.
Broadcast
might be the same as NState
, but doesn't have to be.
This is up to the application.
If Cand == undefined
, it is possible to obtain a list of all new
candidates that we haven't synced with (in the normal case, this will be
all known candidates, but if our instance is re-elected after a netsplit,
the 'new' candidates will be the ones that haven't yet recognized us as
leaders). This gives us a chance to talk to them before crafting our
broadcast message.
We can also choose a different message for the new candidates and for
the ones that already see us as master. This would be accomplished by
returning {ok, AmLeaderMsg, FromLeaderMsg, NewState}
, where
AmLeaderMsg
is sent to the new candidates (and processed in
surrendered/3
, and FromLeaderMsg
is sent to the old
(and processed in from_leader/3
).
If Cand == Pid
, a new candidate has connected. If this affects our state
such that all candidates need to be informed, we can return {ok, Msg, NSt}
.
If, on the other hand, we only need to get the one candidate up to speed,
we can return {reply, Msg, NSt}
, and only the candidate will get the
message. In either case, the candidate (Cand
) will receive the message
in surrendered/3
. In the former case, the other candidates will
receive the message in from_leader/3
.
Example:
elected(#st{dict = Dict} = St, _I, undefined) ->
{ok, Dict, St};
elected(#st{dict = Dict} = St, _I, Pid) when is_pid(Pid) ->
%% reply only to Pid
{reply, Dict, St}.
NState = state()
Called by each candidate in response to a message from the leader.
In this particular module, the leader passes an update function to be applied to the candidate's state.
Broadcast = broadcast()
NState = state()
Called by the leader when it detects loss of a candidate.
If the function returns a Broadcast
object, this will be sent to all
candidates, and they will receive it in the function from_leader/3
.
handle_call(Request::term(), From::callerRef(), State::state(), I::info()) -> {reply, Reply, NState} | {noreply, NState} | {stop, Reason, Reply, NState} | commonReply()
Equivalent to Mod:handle_call/3
in a gen_server.
Note the difference in allowed return values. {ok,NState}
and
{noreply,NState}
are synonymous.
{noreply,NState}
is allowed as a return value from handle_call/3
,
since it could arguably add some clarity, but mainly because people are
used to it from gen_server.
handle_cast(Msg::term(), State::state(), I::info()) -> {noreply, NState} | commonReply()
Equivalent to Mod:handle_call/3
in a gen_server, except
(NOTE) for the possible return values.
handle_info(Msg::term(), State::state(), I::info()) -> {noreply, NState} | commonReply()
Equivalent to Mod:handle_info/3
in a gen_server,
except (NOTE) for the possible return values.
This function will be called in response to any incoming message not recognized as a call, cast, leader_call, leader_cast, from_leader message, internal leader negotiation message or system message.
handle_leader_call(Msg::term(), From::callerRef(), State::state(), I::info()) -> {reply, Reply, NState} | {reply, Reply, Broadcast, NState} | {noreply, state()} | {stop, Reason, Reply, NState} | commonReply()
Broadcast = broadcast()
NState = state()
Called by the leader in response to a leader_call().
If the return value includes a Broadcast
object, it will be sent to all
candidates, and they will receive it in the function from_leader/3
.
Example:
handle_leader_call({store,F}, From, #st{dict = Dict} = S, E) ->
NewDict = F(Dict),
{reply, ok, {store, F}, S#st{dict = NewDict}};
handle_leader_call({leader_lookup,F}, From, #st{dict = Dict} = S, E) ->
Reply = F(Dict),
{reply, Reply, S}.
In this particular example, leader_lookup
is not actually supported
from the gdict module, but would be useful during
complex operations, involving a series of updates and lookups. Using
leader_lookup
, all dictionary operations are serialized through the
leader; normally, lookups are served locally and updates by the leader,
which can lead to race conditions.
handle_leader_cast(Msg::term(), State::term(), I::info()) -> commonReply()
Called by the leader in response to a leader_cast().
init(Arg::term()) -> {ok, State}
State = state()
Equivalent to the init/1 function in a gen_server.
surrendered(State::state(), Synch::broadcast(), I::info()) -> {ok, NState}
NState = state()
Called by each candidate when it recognizes another instance as leader.
Strictly speaking, this function is called when the candidate acknowledges a leader and receives a Synch message in return.
Example:
surrendered(_OurDict, LeaderDict, _I) ->
{ok, LeaderDict}.
terminate(Reason::term(), State::state()) -> Void
Equivalent to terminate/2
in a gen_server callback
module.