portunus_election behaviour (portunus v0.8.0)
View SourceHelps implement a leader election with application-specific semantics.
A candidate (an election participant) runs on every node. At most one candidate is elected at a time (at any committed Raft index).
When a candidate is elected, the elected/1 callback is called.
Its opposite, stepped_down/1, is invoked when the participant
loses leadership.
-callback elected(Ctx :: election_ctx()) -> {ok, State :: term()}.
-callback stepped_down(State :: term()) -> ok.Ctx is an election_ctx() map carrying name, key, token, and
args, so the elected leader can use the fencing token for operations
on external resources.
Summary
Functions
Whether this participant is the elected owner. An election blocked in a
Ra command (a quorum loss, a slow elected/1) is not the owner, so a
caller that must not block treats a timeout as false.
Stop several elections concurrently against one deadline, killing
stragglers. Each election's terminate runs user stepped_down code plus a
revoke that blocks up to the command timeout under no quorum, so a serial
stop holds the caller for the sum. A killed election's revoke is lost; TTL
expiry covers it.
Ask this node's election, if it is the current owner of its key, to hand
ownership to TargetNode. It pre-checks that TargetNode is a ready
contender, stops the local work, issues the token-fenced transfer, and on
success re-contends as a standby; if the target was not ready it restores the
local work and stays owner. Returns {error, not_owner} when this node is not
the owner, and {error, {no_contender, TargetNode}} when the target is not a
ready contender. {error, no_quorum} means the command timed out and its
outcome is unknown: the work stays stopped while the election settles
ownership itself (restoring it or re-contending), so the caller retries later
rather than treating it as a failed transfer. A retry made before that
settles also returns {error, not_owner}; it does not prove ownership moved.
Types
-type election_ctx() :: #{name := portunus:name(), key := portunus:lock_key(), token := portunus:token(), args := term()}.
-type election_opts() :: #{ttl_ms => pos_integer(), affinity => portunus_affinity:spec()}.
Callbacks
-callback elected(election_ctx()) -> {ok, State :: term()}.
-callback stepped_down(State :: term()) -> ok.
Functions
Whether this participant is the elected owner. An election blocked in a
Ra command (a quorum loss, a slow elected/1) is not the owner, so a
caller that must not block treats a timeout as false.
-spec start_link(portunus:name(), portunus:lock_key(), module(), term()) -> {ok, pid()} | {error, term()}.
-spec start_link(portunus:name(), portunus:lock_key(), module(), term(), election_opts()) -> {ok, pid()} | {error, term()}.
-spec stop(pid()) -> ok.
-spec stop_all([pid()]) -> ok.
Stop several elections concurrently against one deadline, killing
stragglers. Each election's terminate runs user stepped_down code plus a
revoke that blocks up to the command timeout under no quorum, so a serial
stop holds the caller for the sum. A killed election's revoke is lost; TTL
expiry covers it.
-spec stop_all([pid()], pos_integer()) -> ok.
-spec transfer_to(pid(), node()) -> portunus:ok_or_error({no_contender, node()} | not_owner | no_quorum).
Ask this node's election, if it is the current owner of its key, to hand
ownership to TargetNode. It pre-checks that TargetNode is a ready
contender, stops the local work, issues the token-fenced transfer, and on
success re-contends as a standby; if the target was not ready it restores the
local work and stays owner. Returns {error, not_owner} when this node is not
the owner, and {error, {no_contender, TargetNode}} when the target is not a
ready contender. {error, no_quorum} means the command timed out and its
outcome is unknown: the work stays stopped while the election settles
ownership itself (restoring it or re-contending), so the caller retries later
rather than treating it as a failed transfer. A retry made before that
settles also returns {error, not_owner}; it does not prove ownership moved.