syn (syn v3.3.0) View Source
Exposes all of the global Process Registry and Process Group APIs.
Syn implement Scopes. You may think of Scopes such as database tables, so a set of data elements, but that's where the analogy ends.
A Scope is a way to create a namespaced, logical overlay network running on top of the Erlang distribution cluster. Nodes that belong to the same Scope will form a subcluster: they will synchronize data between themselves, and themselves only.
For instance, you may have nodes in your Erlang cluster that need to handle connections to users, and other nodes that need to handle connections to physical devices. One approach is to create two Scopes: users
and devices
, where you can register your different types of connections.
Scopes are therefore a way to properly namespace your logic, but they also allow to build considerably larger scalable architectures, as it is possible to divide an Erlang cluster into subclusters which hold specific portions of data.
Please note any of the methods documented here will raise:- An
error({invalid_scope, Scope})
if the local node has not been added to the specified Scope. - An
error({invalid_remote_scope, Scope, RemoteNode})
if the Pid passed in as variable is running on a node that has not been added to the specified Scope, or if the remote scope process is temporarily down.
Link to this section Summary
Functions
Add the local node to the specified Scopes
.
Returns the count of all the groups for the specified Scope.
Node
.Returns the group names for the specified Scope.
Returns the group names for the specified Scope which have at least 1 process running on Node
.
pid()
is a member of GroupName in the specified Scope running on the local node.pid()
is a member of GroupName in the specified Scope.Equivalent to join(Scope, GroupName, Pid, undefined).
Adds a pid()
with metadata to GroupName in the specified Scope.
Removes a pid()
from GroupName in the specified Scope.
Equivalent to group_count(Scope, node()).
Equivalent to group_names(Scope, node()).
Publish a message to all group members running on the local node in the specified Scope.
Equivalent to registry_count(Scope, node()).
Looks up a registry entry in the specified Scope.
Returns the member for GroupName in the specified Scope.
Returns the count of all members for the specified Scope and GroupName.
Node
.Returns the list of all members for GroupName in the specified Scope.
Calls all group members in the specified Scope and collects their replies.
Allows a group member to reply to a multi call.
Publish a message to all group members in the specified Scope.
Equivalent to register(Scope, Name, Pid, undefined).
Registers a process with metadata in the specified Scope.
Returns the count of all registered processes for the specified Scope.
Sets the handler module.
Starts Syn manually.
Unregisters a process from specified Scope.
Updates the GroupName member metadata in the specified Scope.
Updates the registered Name metadata in the specified Scope.
Link to this section Functions
Specs
add_node_to_scopes(Scopes :: [atom()]) -> ok.
Add the local node to the specified Scopes
.
There are 2 ways to add a node to Scopes. One is by using this method, the other is to set the environment variable syn
with the key scopes
. In this latter case, you're probably best off using an application configuration file:
Elixir
config :syn,
scopes: [:devices, :users]
Erlang
{syn, [
{scopes, [devices, users]}
]}
Examples
Elixir
iex> :syn.add_node_to_scopes([:devices])
:ok
Erlang
1> syn:add_node_to_scopes([devices]).
ok
Specs
group_count(Scope :: atom()) -> non_neg_integer().
Returns the count of all the groups for the specified Scope.
Examples
Elixir
iex> :syn.group_count(:users)
321778
Erlang
1> syn:group_count(users).
321778
Specs
group_count(Scope :: atom(), Node :: node()) -> non_neg_integer().
Node
.
Specs
group_names(Scope :: atom()) -> [GroupName :: term()].
Returns the group names for the specified Scope.
The order of the group names is not guaranteed to be the same on all calls.
Examples
Elixir
iex> :syn.group_names(:users)
["area-1", "area-2"]
Erlang
1> syn:group_names(users).
["area-1", "area-2"]
Specs
group_names(Scope :: atom(), Node :: node()) -> [GroupName :: term()].
Returns the group names for the specified Scope which have at least 1 process running on Node
.
Specs
is_local_member(Scope :: atom(), GroupName :: term(), Pid :: pid()) -> boolean().
pid()
is a member of GroupName in the specified Scope running on the local node.
Specs
is_member(Scope :: atom(), GroupName :: term(), Pid :: pid()) -> boolean().
pid()
is a member of GroupName in the specified Scope.
Specs
join(Scope :: term(), Name :: term(), Pid :: term()) -> ok | {error, Reason :: term()}.
Equivalent to join(Scope, GroupName, Pid, undefined).
Specs
join(Scope :: atom(), GroupName :: term(), Pid :: pid(), Meta :: term()) -> ok | {error, Reason :: term()}.
Adds a pid()
with metadata to GroupName in the specified Scope.
A process can join multiple groups. A process may also join the same group multiple times, for example if you need to update its metadata, however it is recommended to be aware of the implications of updating metadata, see the strict_mode
option for more information.
If you want to update a process' metadata by modifying its existing one, you may consider using update_member/4
instead.
When a process joins a group, Syn will automatically monitor it.
Possible error reasons:not_alive
: Thepid()
being added is not alive.not_self
: the method is being called from a process other thanself()
, butstrict_mode
is enabled.
Examples
Elixir
iex> :syn.join(:devices, "area-1", self(), [meta: :one])
:ok
Erlang
1> syn:join(devices, "area-1", self(), [{meta, one}]).
ok
Specs
leave(Scope :: atom(), GroupName :: term(), Pid :: pid()) -> ok | {error, Reason :: term()}.
Removes a pid()
from GroupName in the specified Scope.
not_in_group
: Thepid()
is not in GroupName for the specified Scope.
Specs
local_group_count(Scope :: atom()) -> non_neg_integer().
Equivalent to group_count(Scope, node()).
Specs
local_group_names(Scope :: atom()) -> [GroupName :: term()].
Equivalent to group_names(Scope, node()).
Specs
local_member_count(Scope :: atom(), GroupName :: term()) -> non_neg_integer().
Equivalent to member_count(Scope, GroupName, node()).
Specs
local_members(Scope :: atom(), GroupName :: term()) -> [{Pid :: pid(), Meta :: term()}].
Specs
local_publish(Scope :: atom(), GroupName :: term(), Message :: term()) -> {ok, RecipientCount :: non_neg_integer()}.
Publish a message to all group members running on the local node in the specified Scope.
Works similarly topublish/3
for local processes.
Specs
local_registry_count(Scope :: atom()) -> non_neg_integer().
Equivalent to registry_count(Scope, node()).
Specs
lookup(Scope :: atom(), Name :: term()) -> {pid(), Meta :: term()} | undefined.
Looks up a registry entry in the specified Scope.
Examples
Elixir
iex> :syn.register(:devices, "SN-123-456789", self())
:ok
iex> :syn.lookup(:devices, "SN-123-456789")
{#PID<0.105.0>, undefined}
Erlang
1> syn:register(devices, "SN-123-456789", self()).
ok
2> syn:lookup(devices, "SN-123-456789").
{<0.79.0>, undefined}
Specs
member(Scope :: atom(), GroupName :: term(), Pid :: pid()) -> {Pid :: pid(), Meta :: term()} | undefined.
Returns the member for GroupName in the specified Scope.
Examples
Elixir
iex> :syn.join(:devices, "area-1", self(), :meta)
:ok
iex> :syn.member(:devices, "area-1", self())
{#PID<0.105.0>, :meta}
Erlang
1> syn:join(devices, "area-1", self(), meta).
ok
2> syn:member(devices, "area-1", self()).
{<0.69.0>, meta}
Specs
member_count(Scope :: atom(), GroupName :: term()) -> non_neg_integer().
Returns the count of all members for the specified Scope and GroupName.
Examples
Elixir
iex> :syn.member_count(:devices, "abc123")
512473
Erlang
1> syn:member_count(devices, "abc123").
512473
Specs
member_count(Scope :: atom(), GroupName :: term(), Node :: node()) -> non_neg_integer().
Node
.
Specs
members(Scope :: atom(), GroupName :: term()) -> [{Pid :: pid(), Meta :: term()}].
Returns the list of all members for GroupName in the specified Scope.
Examples
Elixir
iex> :syn.join(:devices, "area-1", self())
:ok
iex> :syn.members(:devices, "area-1")
[{#PID<0.105.0>, :undefined}]
Erlang
1> syn:join(devices, "area-1", self()).
ok
2> syn:members(devices, "area-1").
[{<0.69.0>, undefined}]
Specs
multi_call(Scope :: atom(), GroupName :: term(), Message :: term()) -> {Replies :: [{{pid(), Meta :: term()}, Reply :: term()}], BadReplies :: [{pid(), Meta :: term()}]}.
Equivalent to multi_call(Scope, GroupName, Message, 5000).
Specs
multi_call(Scope :: atom(), GroupName :: term(), Message :: term(), Timeout :: non_neg_integer()) -> {Replies :: [{{pid(), Meta :: term()}, Reply :: term()}], BadReplies :: [{pid(), Meta :: term()}]}.
Calls all group members in the specified Scope and collects their replies.
When this call is issued, all members will receive a tuple in the format:
{syn_multi_call, TestMessage, Caller, Meta}
To reply, every member MUST use the method multi_call_reply/2
.
Timeout
to receive all replies from the members. The responses will be added to the Replies
list, while the members that do not reply in time or that crash before sending a reply will be added to the BadReplies
list.
Specs
multi_call_reply(Caller :: term(), Reply :: term()) -> any().
Allows a group member to reply to a multi call.
Seemulti_call/4
for info.
Specs
node_scopes() -> [atom()].
Specs
publish(Scope :: atom(), GroupName :: term(), Message :: term()) -> {ok, RecipientCount :: non_neg_integer()}.
Publish a message to all group members in the specified Scope.
RecipientCount
is the count of the intended recipients.
Examples
Elixir
iex> :syn.join(:users, "area-1", self())
:ok
iex> :syn.publish(:users, "area-1", :my_message)
{:ok,1}
iex> flush()
Shell got :my_message
:ok
Erlang
1> syn:join(users, "area-1", self()).
ok
2> syn:publish(users, "area-1", my_message).
{ok,1}
3> flush().
Shell got my_message
ok
Specs
register(Scope :: atom(), Name :: term(), Pid :: term()) -> ok | {error, Reason :: term()}.
Equivalent to register(Scope, Name, Pid, undefined).
Specs
register(Scope :: atom(), Name :: term(), Pid :: pid(), Meta :: term()) -> ok | {error, Reason :: term()}.
Registers a process with metadata in the specified Scope.
You may register the same process with different names. You may also re-register a process multiple times, for example if you need to update its metadata, however it is recommended to be aware of the implications of updating metadata, see the strict_mode
option for more information.
If you want to update a process' metadata by modifying its existing one, you may consider using update_registry/3
instead.
When a process gets registered, Syn will automatically monitor it.
Possible error reasons:not_alive
: Thepid()
being registered is not alive.taken
: name is already registered with anotherpid()
.not_self
: the method is being called from a process other thanself()
, butstrict_mode
is enabled.
Examples
Elixir
iex> :syn.register(:devices, "SN-123-456789", self(), [meta: :one])
:ok
iex> :syn.lookup(:devices, "SN-123-456789")
{#PID<0.105.0>, [meta: :one]}
Erlang
1> syn:register(devices, "SN-123-456789", self(), [{meta, one}]).
ok
2> syn:lookup(devices, "SN-123-456789")
{<0.105.0>,[{meta, one}]}
Processes can also be registered as gen_server
names, by usage of via-tuples. This way, you can use the gen_server
API with these tuples without referring to the Pid directly. If you do so, you MUST use a gen_server
name in format:{Scope, Name}
or{Scope, Name, Meta}
i.e. your via tuple will look like {via, syn, {my_scope, <<"process name">>}}
or, with meta, {via, syn, {my_scope, <<"process name">>, process_meta}}
. See here below for examples.
Examples
Elixir
iex> tuple = {:via, :syn, {:devices, "SN-123-456789"}}.
{:via, :syn, {:devices, "SN-123-456789"}}
iex> GenServer.start_link(__MODULE__, [], name: tuple)
{ok, #PID<0.105.0>}
iex> GenServer.call(tuple, :your_message)
:your_message
Erlang
1> Tuple = {via, syn, {devices, "SN-123-456789"}}.
{via, syn, {devices, "SN-123-456789"}}
2> gen_server:start_link(Tuple, your_module, []).
{ok, <0.79.0>}
3> gen_server:call(Tuple, your_message).
your_message
Specs
registry_count(Scope :: atom()) -> non_neg_integer().
Returns the count of all registered processes for the specified Scope.
Examples
Elixir
iex> :syn.registry_count(:devices)
512473
Erlang
1> syn:registry_count(devices).
512473
Specs
registry_count(Scope :: atom(), Node :: node()) -> non_neg_integer().
Specs
set_event_handler(module()) -> ok.
Sets the handler module.
Please see syn_event_handler
for information on callbacks.
There are 2 ways to set a handler module. One is by using this method, the other is to set the environment variable syn
with the key event_handler
. In this latter case, you're probably best off using an application configuration file:
Elixir
config :syn,
event_handler: MyCustomEventHandler
Erlang
{syn, [
{event_handler, my_custom_event_handler}
]}
Examples
Elixir
iex> :syn.set_event_handler(MyCustomEventHandler)
ok
Erlang
1> syn:set_event_handler(my_custom_event_handler).
ok
Specs
start() -> ok.
Starts Syn manually.
In most cases Syn will be started as one of your application's dependencies, however you may use this helper method to start it manually.Specs
stop() -> ok | {error, Reason :: term()}.
Specs
subcluster_nodes(Manager :: registry | pg, Scope :: atom()) -> [node()].
Specs
unregister(Scope :: atom(), Name :: term()) -> ok | {error, Reason :: term()}.
Unregisters a process from specified Scope.
Possible error reasons:undefined
: name is not registered.race_condition
: the localpid()
does not correspond to the cluster value, so Syn will not succeed unregistering the value and will wait for the cluster to synchronize. This is a rare occasion.
Specs
update_member(Scope :: atom(), GroupName :: term(), Pid :: pid(), Fun :: function()) -> {ok, {Pid :: pid(), Meta :: term()}} | {error, Reason :: term()}.
Updates the GroupName member metadata in the specified Scope.
Atomically calls Fun with the current metadata, and stores the return value as new metadata. It is recommended to be aware of the implications of updating metadata, see the strict_mode
option for more information.
undefined
: Thepid()
cannot be found in GroupName.
Note: an error in the update fun will be raised in the calling process.
Examples
Elixir
iex> :syn.join(:devices, "area-1", self(), 10)
:ok
iex> :syn.update_member(:devices, "area-1", self(), fn existing_meta -> existing_meta * 2 end)
{:ok, {#PID<0.105.0>, 20}}
Erlang
1> syn:join(devices, "area-1", self(), 10).
ok
2> syn:update_member(devices, "area-1", self(), fun(ExistingMeta) -> ExistingMeta * 2 end).
{ok, {<0.69.0>, 20}}
Specs
update_registry(Scope :: atom(), Name :: term(), Fun :: function()) -> {ok, {Pid :: pid(), Meta :: term()}} | {error, Reason :: term()}.
Updates the registered Name metadata in the specified Scope.
Atomically calls Fun with the current metadata, and stores the return value as new metadata. It is recommended to be aware of the implications of updating metadata, see the strict_mode
option for more information.
undefined
: The Name cannot be found.
Note: an error in the update fun will be raised in the calling process.
Examples
Elixir
iex> :syn.register(:devices, "SN-123-456789", self(), 10)
:ok
iex> :syn.update_registry(:devices, "area-1", fn _pid, existing_meta -> existing_meta * 2 end)
{:ok, {#PID<0.105.0>, 20}}
Erlang
1> syn:register(devices, "SN-123-456789", self(), 10).
ok
2> syn:update_registry(devices, "SN-123-456789", fun(_Pid, ExistingMeta) -> ExistingMeta * 2 end).
{ok, {<0.69.0>, 20}}