dgen_registry (DGen v0.3.0)

Copy Markdown View Source

OTP-compatible process registry implementing the {via, dgen_registry, {RegistryName, LogicalName}} contract.

Standard OTP processes (gen_server, gen_statem, gen_event, etc.) can be registered and addressed by name across an Erlang cluster.

This module implements the four-function contract required for OTP via tuples:

Name terms

Every via name is a two-tuple {RegistryName, LogicalName} where RegistryName identifies which registry to use (the one started with start_link/2), and LogicalName is any term meaningful to the application (atom, binary, tuple, …).

Example

{ok, _} = dgen_registry:start_link(my_registry, Tenant),

%% Start a gen_server registered in the dgen registry
gen_server:start_link({via, dgen_registry, {my_registry, user_service}},
                      my_server, [], []),

%% Call it from anywhere on the cluster
gen_server:call({via, dgen_registry, {my_registry, user_service}}, ping).

Consistency model

All writes (register_name, unregister_name) and consistent reads (whereis_name_consistent) go through the elected leader member process. The leader is the single writer for the name table.

whereis_name/1 (used by the OTP via-tuple machinery) is a snapshot read served from the local member's in-memory map — no network hop, no backend round-trip. This map is kept in sync by one-way replication casts from the leader. There is therefore a short window after registration where a remote node's whereis_name/1 may still return undefined; this is the same eventual-consistency trade-off as gproc global mode.

Leadership

The elected leader is the member process on the Erlang node that most recently committed a backend transaction for the elector queue — i.e., whoever wins the backend consensus. When leadership changes, the elector sets a distributed lock so all elector consumers pause while replication paths are reconfigured. The new leader broadcasts a name snapshot to all followers on assumption.

Auto-unregistration

The leader monitors every registered Pid. When a monitored process exits, the leader removes the entry and propagates {name_unregistered} to all follower members.

Summary

Functions

Returns the registered name of the elector process for the given registry name.

Returns the current leader epoch. Increments each time a new leader is elected.

Returns the current leader member id, or undefined if no leader is elected.

Returns the list of all current member ids in the registry.

Returns the registered name of the member process for the given registry name.

Registers Pid under {RegistryName, LogicalName}.

Sends Msg to the process registered as Name, returning the Pid.

Starts the registry supervisor registered as Name.

Starts the registry supervisor registered as SupName, using Name as the registry name.

Removes the registration for {RegistryName, LogicalName}.

Snapshot read — served from the local member's in-memory map.

Consistent read routed through the leader.

Functions

elector_name(Name)

-spec elector_name(atom()) -> atom().

Returns the registered name of the elector process for the given registry name.

get_epoch(Name)

-spec get_epoch(Name :: atom()) -> non_neg_integer().

Returns the current leader epoch. Increments each time a new leader is elected.

get_leader(Name)

-spec get_leader(Name :: atom()) -> dgen_registry_elector:member_id() | undefined.

Returns the current leader member id, or undefined if no leader is elected.

get_members(Name)

-spec get_members(Name :: atom()) -> [dgen_registry_elector:member_id()].

Returns the list of all current member ids in the registry.

init/1

member_name(Name)

-spec member_name(atom()) -> atom().

Returns the registered name of the member process for the given registry name.

register_name/2

-spec register_name({atom(), term()}, pid()) -> yes | no.

Registers Pid under {RegistryName, LogicalName}.

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.

send(Name, Msg)

-spec send({atom(), term()}, term()) -> pid().

Sends Msg to the process registered as Name, returning the Pid.

Exits with reason {badarg, {Name, Msg}} if the name is not registered. Called internally by gen_server/gen_event for {via, …} routing.

start_link(Name, Tenant)

-spec start_link(Name :: atom(), Tenant :: dgen_backend:tenant()) -> supervisor:startlink_ret().

Starts the registry supervisor registered as Name.

start_link(SupName, Name, Tenant)

-spec start_link(SupName :: atom(), Name :: atom(), Tenant :: dgen_backend:tenant()) ->
                    supervisor:startlink_ret().

Starts the registry supervisor registered as SupName, using Name as the registry name.

unregister_name/1

-spec unregister_name({atom(), term()}) -> ok.

Removes the registration for {RegistryName, LogicalName}.

Fire-and-forget: routes through the local member, which forwards to the leader. The local member also removes the entry from its own map immediately so snapshot reads on this node are consistent right away.

whereis_name/1

-spec whereis_name({atom(), term()}) -> pid() | undefined.

Snapshot read — served from the local member's in-memory map.

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.

whereis_name_consistent/1

-spec whereis_name_consistent({atom(), term()}) -> pid() | undefined.

Consistent read routed through the leader.

Returns the authoritative Pid for LogicalName, or undefined if not registered. More expensive than whereis_name/1 but never stale.