mule/runtime/peer_global
The distributed-Erlang lock peer — a port of Oban.Peers.Global.
Leadership is a :global lock on the instance name: set_lock with zero
retries either takes it or reports the holder elsewhere, re-checked every
interval (a :global lock releases when its holding process dies, so a
crashed leader frees the cluster without any TTL). Requires a functional
Distributed Erlang cluster — pair it with the PG notifier; on a single
unclustered node it is simply always leader, like always_leader with
extra steps.
Structure mirrors runtime/peer_database: the actor writes its
leadership into the peer cell on every tick (the gates’ pure read), the
first election runs synchronously in the initialiser, and a supervisor
shutdown hands off by dropping the lock and notifying the Leader channel.
Unlike the database peer there is no lease to renew, so the leader checks
at the same cadence as followers (Peers.Global has no leader boost), and
elections cannot error (no exception telemetry arm). Followers learn
get_leader from the winner’s Lock broadcast (Peers.Global’s
%{lock: conf.node}) instead of a database read — before the first
broadcast arrives it is unknown.
Types
pub type Message {
Elect
IsLeader(reply: process.Subject(Bool))
GetLeader(reply: process.Subject(option.Option(String)))
LeaderHeard(notifier.Notification)
Shutdown
}
Constructors
-
Elect -
IsLeader(reply: process.Subject(Bool)) -
GetLeader(reply: process.Subject(option.Option(String))) -
LeaderHeard(notifier.Notification) -
Shutdown
pub type State {
State(
name: String,
node: String,
notifier: notifier.Notifier,
interval: duration.Duration,
is_leader: Bool,
leader: option.Option(String),
self_subject: process.Subject(Message),
)
}
Constructors
-
State( name: String, node: String, notifier: notifier.Notifier, interval: duration.Duration, is_leader: Bool, leader: option.Option(String), self_subject: process.Subject(Message), )
Values
pub fn child(
name name: String,
node node: String,
notifier notifier: notifier.Notifier,
interval interval: duration.Duration,
peer_name peer_name: process.Name(Message),
) -> supervision.ChildSpecification(Nil)
Supervise the peer actor as a single worker, registered under peer_name
so reader can reach it for get_leader.
pub fn reader(
name name: String,
peer_name peer_name: process.Name(Message),
) -> peer.Peer
Build the Peer read surface: leadership is the pure cell read (the gate
hot path), and get_leader calls into the named peer actor for the last
broadcast leader (never a database). None when the peer is down or no
Lock has been heard yet.
pub fn start(
name name: String,
node node: String,
notifier notifier: notifier.Notifier,
interval interval: duration.Duration,
) -> Result(
actor.Started(process.Subject(Message)),
actor.StartError,
)
Start the peer actor directly (the child spec wraps this). Public so a
test can hold the actor’s Subject and address its process.