Local name cache and consistent read/write proxy for dgen_registry.
Each node participating in a named registry runs one member process. The member has two roles depending on whether it is the current leader.
Storage
Both leader and follower keep a names :: #{LogicalName => pid()} map in
their gen_server state. There is no ETS table and no durable storage for
name→pid mappings. Pids are node-local and process-lifetime-scoped; they
have no meaning after a restart and must never be written to the backend.
Consistent reads and writes go through the leader.
Snapshot reads (whereis_snapshot) are served from the local member's map
without contacting the leader.
Follower role
Keeps its local names map in sync by receiving {name_registered, …},
{name_unregistered, …}, and {apply_names_snapshot, …} casts from the
leader (one-way replication). All follower messages come from the leader
process, so Erlang's per-pair FIFO guarantee ensures followers always see a
snapshot before any {name_registered} broadcast that post-dates it.
Partition recovery
The member subscribes to nodeup/nodedown events via
net_kernel:monitor_nodes/1. On {nodeup, Node}, the member re-announces
itself to the elector ({join, Self}). This handles the case where an
Erlang-level network partition caused both sides to remove each other from
the member set via {member_down} while the DB remained healthy: once the
partition heals and distribution reconnects, both sides re-join and the
elector reconstitutes the cluster without requiring a restart.
Forwards {register, …} calls and {unregister, …} casts to the leader.
For register, the follower also updates its own names map on receiving
yes from the leader, so a subsequent whereis_snapshot on this node
reflects the change without waiting for the replication cast. On no the
local map is left unchanged.
Leader role
Assumed when the elector calls {elector_assume_and_distribute, …}. On
assuming leadership the member uses the supplied snapshot (or its own names
map if the snapshot is self_snapshot), sets up erlang:monitor/2 for
every entry, and distributes {apply_names_snapshot} casts to all followers
from its own process (same sender as future {name_registered} broadcasts —
see elector moduledoc for the FIFO ordering guarantee). Any stale Pid
entries are removed when their DOWN signals arrive.
The leader is the sole writer for the name table. It:
- Handles
{register, LogicalName, Pid}calls: checks the in-memory map, updates it, monitors the Pid, and replicates{name_registered, …}. - Handles
{whereis, LogicalName}calls: consistent read from local map. - Handles
{unregister, LogicalName}casts: updates the map, demonitors, and replicates{name_unregistered, …}. - Monitors every registered Pid. When one dies, removes from the map
and replicates
{name_unregistered, …}to followers.
On relinquishing leadership the member demonitors all registered Pids and
clears the leader-only state. The names map is kept intact (it still
serves snapshot reads).
Failure model
Name-to-pid mappings are intentionally not stored in the backend. As a result:
Leader crash: the new leader starts with
self_snapshot— its own in-memory follower replica. Any registrations the dead leader committed to its map but had not yet broadcast to followers are silently lost. A caller that receivedyesfromregister_name/2may find the name absent after a leader failover. Re-registration after detecting the loss is the caller's responsibility.Full cluster restart: all registered names are lost. Applications must re-register on startup.
Summary
Functions
Starts the member process registered as Name.
Functions
-spec start_link(Name :: atom(), Args :: map()) -> gen_server:start_ret().
Starts the member process registered as Name.