OTP-compatible process registry backed by the configured storage backend.
Processes started with a {:via, DGen.Registry, {name, logical_name}} tuple
can be addressed by name across an Erlang/Elixir cluster.
Starting a registry
{:ok, _sup} = DGen.Registry.start_link(:my_registry, tenant)start_link/2,3 returns the supervisor's pid — hold onto it (e.g. to
Supervisor.stop/2 it later) rather than looking it up by name afterward;
see "Process identity" below.
Via-tuple usage
via = {:via, DGen.Registry, {:my_registry, :user_service}}
GenServer.start_link(MyServer, [], name: via)
GenServer.call(via, :ping)Process identity
Starting a registry creates zero atoms. name is supplied by the caller and
is application-controlled, not generated by this module. The supervisor
itself is not registered under any name, and the elector has no registered
name either — only the member is, under name directly, with no further
atom derived from it. This matters for applications that start many
independent registries dynamically (e.g. one per tenant): atoms are never
garbage-collected, so this module never derives additional atoms per
registry beyond the one the caller already chose to create.
Consistency model
register_name/2, unregister_name/1, and whereis_name_consistent/1
route through the elected leader. whereis_name/1 (used by the OTP
via-tuple machinery) is served lock-free from the calling process — an ETS
read against the local member's table, no member round-trip, no network hop.
There is a short replication window after registration where a follower
node's snapshot read may still return nil.
Metadata and queries
A registration can carry metadata for its lifetime: register_name/3 /
set_metadata/2 attach an index map (queryable) and an opaque data
payload; get_metadata/1 / get_metadata_consistent/1 read it back
(lock-free / leader-routed, mirroring whereis_name/1 /
whereis_name_consistent/1). query/2 / query_consistent/2 find every
registration whose index satisfies a conjunction of exact equalities.
Summary
Functions
Blocks until ready/1 holds, or 5s elapses. Returns :ok or {:error, :timeout}.
Blocks until the registry on this node is ready to serve registrations, or
timeout ms elapses. Returns :ok or {:error, :timeout}. Use after starting
or joining a cluster to wait out leader election and the join handoff.
Removes all of the registry's durable backend state (membership, leader/version fence,
and subscriptions). Returns :ok.
Returns the current elector pid for registry_name, or nil (:undefined)
if the member (and so the elector) cannot currently be found.
Returns the current leader epoch. Increments each time a new leader is elected.
Returns the current leader member id, or :undefined if none is elected.
Returns the list of all current member ids [{node, member_atom}] in the registry.
Snapshot read of a registration's metadata — served lock-free from the
calling process, mirroring whereis_name/1.
Consistent metadata read routed through the leader (authoritative, never stale).
Returns the registered name of the member process for the given registry name.
Returns the ETS table name holding the registry's local name => pid replica.
Finds every registration in registry_name whose indexed metadata satisfies
all of constraints (a conjunction of exact equalities — AND-equal).
Like query/2, but routed through the leader for an authoritative (never-stale) result.
Whether the registry on this node is ready to serve registrations right now
(a leader is known and this member has synced). Point-in-time; see await_ready/2.
Registers pid under {registry_name, logical_name}.
Registers pid under {registry_name, logical_name} with metadata, in one
fenced step.
Sends msg to the process registered as name, returning the pid.
Replaces the metadata of the existing registration {registry_name, logical_name}.
Starts the registry name with default options.
Starts the registry name with per-registry options opts.
Creates (or updates) a durable presence subscription sub_id: keeps the processes
matching notify up to date with the set of processes matching watch.
Returns the registry's durable presence subscriptions as
%{sub_id => {watch, notify}} (shared cluster-wide and durable).
Removes the registration for {registry_name, logical_name}. Returns :ok.
Removes the durable presence subscription sub_id (from subscribe/4). Returns :ok
(idempotent; applied asynchronously).
Removes all durable presence subscriptions from the registry. Returns :ok (applied
asynchronously).
Snapshot read — served lock-free from the calling process (an ETS lookup against the local member's table, no member round-trip).
Consistent read routed through the leader.
Functions
Blocks until ready/1 holds, or 5s elapses. Returns :ok or {:error, :timeout}.
Blocks until the registry on this node is ready to serve registrations, or
timeout ms elapses. Returns :ok or {:error, :timeout}. Use after starting
or joining a cluster to wait out leader election and the join handoff.
Removes all of the registry's durable backend state (membership, leader/version fence,
and subscriptions). Returns :ok.
Stop the registry supervisor first, then call this with the tenant you started it with — a running elector would re-create the keys from its in-memory state. Clears only this registry's keys, not any other registry sharing the tenant.
Returns the current elector pid for registry_name, or nil (:undefined)
if the member (and so the elector) cannot currently be found.
The elector has no registered name; it is discovered via the shared
supervisor (all-local, no network hop). Exported for introspection — used
internally by get_leader/1, get_epoch/1, and get_members/1.
Returns the current leader epoch. Increments each time a new leader is elected.
Returns the current leader member id, or :undefined if none is elected.
Returns the list of all current member ids [{node, member_atom}] in the registry.
Snapshot read of a registration's metadata — served lock-free from the
calling process, mirroring whereis_name/1.
Returns {:ok, %{pid: pid, index: map, data: term}} for a registered name,
or nil (:undefined) if not registered.
Consistent metadata read routed through the leader (authoritative, never stale).
Returns the same shape as get_metadata/1.
Returns the registered name of the member process for the given registry name.
Identity today (member_name(name) == name) — the member is registered
under name directly.
Returns the ETS table name holding the registry's local name => pid replica.
Identity today (names_table(name) == name). Exported for introspection;
ordinary callers should use whereis_name/1 / get_metadata/1 rather than
reading the table directly.
Finds every registration in registry_name whose indexed metadata satisfies
all of constraints (a conjunction of exact equalities — AND-equal).
Snapshot read, local to the calling member; batch-consistent (never observes
a half-applied group commit). Returns a list of %{name:, pid:, index:, data:} matches, or {:error, :empty_query} if constraints is empty.
Like query/2, but routed through the leader for an authoritative (never-stale) result.
Whether the registry on this node is ready to serve registrations right now
(a leader is known and this member has synced). Point-in-time; see await_ready/2.
Registers pid under {registry_name, logical_name}.
Routes through the local member, which forwards to the leader if needed.
Returns :yes on success, :no if the name is already taken or no leader
is currently elected.
Registers pid under {registry_name, logical_name} with metadata, in one
fenced step.
meta_spec is %{index: map(), data: term()} (both optional; default %{}
and nil). Like register_name/2, returns :yes on success or :no if the
name is already taken or no leader is currently elected.
Sends msg to the process registered as name, returning the pid.
Raises {:badarg, {name, msg}} if the name is not registered.
Called internally by OTP for {:via, …} routing.
Replaces the metadata of the existing registration {registry_name, logical_name}.
meta_spec is %{index: map(), data: term()} (both optional; an omitted
field becomes %{} / nil — this is a replace, not a merge). Returns :ok,
{:error, :not_registered} if the name is not currently bound, or
{:error, :no_leader} if no leader is elected (or leadership changed
mid-flight — retry).
Starts the registry name with default options.
Returns {:ok, sup_pid} — the supervisor is not registered under any name;
see "Process identity" above.
Starts the registry name with per-registry options opts.
See docs/dgen_registry_design.md (§8 Configuration) for the available
tuning knobs (register_replicas, strict_replication, etc.).
Creates (or updates) a durable presence subscription sub_id: keeps the processes
matching notify up to date with the set of processes matching watch.
watch and notify are queries ({:all, %{attr => value}}, or a bare map read as
{:all, map}). sub_id is any application term (an idempotent upsert). Whenever a
committed batch changes which processes satisfy watch, every process currently
matching notify receives one message {:dgen_presence, sub_id, events} where
events is a non-empty list of {:joined, name, pid} / {:left, name, pid}. A
process is sent an initial snapshot of the current watch set (as :joined events)
whenever it enters the notify set — at subscribe time or when it registers into
notify later — and deltas thereafter, so the subscription can be created before any
viewer exists.
The subscription is durable — stored in the elector's backend state, so it
outlives the cluster: tie its lifetime to a database entity and presence comes back
intact after a scale-to-zero. Sent as a durable cast (applied asynchronously), so
:ok means "accepted"; subscriptions/1 reflects it shortly after. Returns :ok, or
{:error, :empty_query} if either query is empty.
Returns the registry's durable presence subscriptions as
%{sub_id => {watch, notify}} (shared cluster-wide and durable).
Removes the registration for {registry_name, logical_name}. Returns :ok.
A tracked call mirroring the register path: the local member's snapshot is
updated immediately, and the removal is forwarded to the leader. :ok
normally means the leader committed it; with an unreachable leader the
removal is stashed and re-driven on the next leadership/rejoin event, so an
explicit unregister is never silently lost (design doc, Non-goal 5).
Removes the durable presence subscription sub_id (from subscribe/4). Returns :ok
(idempotent; applied asynchronously).
Removes all durable presence subscriptions from the registry. Returns :ok (applied
asynchronously).
Snapshot read — served lock-free from the calling process (an ETS lookup against the local member's table, no member round-trip).
Never blocks on the leader or the backend. May be slightly stale on
follower nodes in the brief window between a remote registration and the
replication cast arriving. Returns nil (:undefined) if not found.
Consistent read routed through the leader.
Returns the authoritative pid for {registry_name, logical_name}, or
nil (:undefined) if not registered. More expensive than whereis_name/1
but never stale.