ostinelli / syn Goto Github PK
View Code? Open in Web Editor NEWA scalable global Process Registry and Process Group manager for Erlang and Elixir.
License: MIT License
A scalable global Process Registry and Process Group manager for Erlang and Elixir.
License: MIT License
I find when a node is down, neither on_process_exit
or on_group_process_exit
is triggered. Is this behaviour by design, or am I doing something wrong?
Hi, a colleague show me using a benchmark than the use of ets:select/2
is slower than ets:lookup/2
:
Quick benchmark:
Name Iters Op/s Min P50 Avg P90 P95 Max
ets lookup 350000 4525963 201.6 ns 207.8 ns 220.9 ns 224.1 ns 233.9 ns 1.298 us
ets select 80000 999365 917.3 ns 977.0 ns 1.001 us 996.8 ns 1.032 us 5.244 us
In the implementation of the registry, even when it is not needed, you use it:
Line 570 in 5c7aeaf
What's the advantage? I mean, I can see here:
find_registry_entry_by_name(Name) ->
case ets:select(syn_registry_by_name, [{
{{Name, '$2'}, '$3', '_', '_', '_'},
[],
['$_']
}]) of
[RegistryTuple] -> RegistryTuple;
_ -> undefined
end.
You are using {Name, Pid}
as the key and in the other ETS table syn_registry_by_pid
, you are using {Pid, Name}
, what is the improvement instead of using as two elements in the beginning of the tuple?
Hello Roberto,
I'm facing an issue with syn
where duplicate processes with the same name can be registered (on different Erlang nodes) and syn
never kills the duplicates.
I've tried with both :syn
v1.6.3
and with v2.0-rc.1
and it reproes with both.
My repro steps are:
:syn.init
on each one (if using v1.6.3
):ping
to {:via, :syn, "123"}
using GenServer.call
and creates it locally (and registers with syn
) if it's not found, sleeps for 1sec and repeats. The "123"
process simply replies :pong
to :ping
calls.And while it's unfortunate that multiple processes with the same name "123" can be created (it would be more desirable for us if registration for duplicates were to fail), the worst issue is that this split-brain situation never gets resolved. According to documentation, we expect one of the duplicate "123" processes to be killed by syn
, but it never happens.
I would appreciate any ideas/workarounds/fixes.
Please let me know if you need more information or have any questions.
Thank you, Dmitry.
whereis_name/2 export is missing on syn.erl ..
its says undefined.
Given an app, which its nodes are not determined at the start, can Syn be used in that context?
The app in my case can accept new manually started nodes to scale performance. My problem is: The connection between the new node and the cluster can be initiate from the new node, or from one of the cluster old nodes.
From what I see in the syn source, at syn:init, there is a step to initialize mnesia table on the current nodes list of the current node. After, it tries to duplicate the syn table to the current node if it is already exists on other nodes.
Is there a functionality in Syn that I can use from the connection initializer node to initialize Syn on other nodes after connecting to them ?
Would be good to have the ability to configure the timeout value for syn:multi_call. For example multi_call/3 with an added Timeout argument. 5000 is a good default but it doesn't fit all use cases.
Additionally it might be worth to monitor to exit early if a crash happened. I think this functionality should try to mirror the gen:call mechanism as much as possible, otherwise it can be dangerous.
I'm using Syn in an environment where hosts are being dynamically deployed, i.e. they join and leave the cluster without manual configuration and without control from within the node. Sometimes this leads to inconsistencies, where process that were registered on a remote node will be still in the local table, but will not respond because the remote node doesn't respond.
This leads to timeouts, even when trying to unregister the name:
** (stop) exited in: :gen_server.call({:syn_registry, :"[email protected]"}, {:unregister_on_node, {:my_proc, "some-name"}})
Now I would assume that these timeouts eventually lead to a check of the node and it being removed from the known cluster, but apparently it just stays there and the entries will remain broken. Which leads (in my case) to an endless loop of start -> try to unregister the name (to be able to register oneself) -> timeout -> crash -> restart -> ...
So the question is, is there already some function I can call from the point of the node that detects the timeouts, or can we maybe improve syn so it can handle these situations more automatically?
Edit: Some more info on this: The node in question seems to be alive (i.e. running in a way that it's not kicked from the erlang cluster) but at least the app running our businesslogic crashed internally or hangs or what, which results in the timeouts.
Testing with two nodes, after a conflict resolution, syn:whereis_name
will sometimes return undefined
on one of the two nodes. It can be the node where the process was shutdown (general case), it can be the other node, or it will also work as intended and the call on both node will return the same pid (in its local form and its remote form depending on the node the function is called).
Please see this commit where I tried to implement a test: lud@8c29218 .
We are using Swarm now and it has an issue that if a registered pid dies and restarted by a supervisor, then it is not registered anymore. See bitwalker/swarm#6
How does Syn deal with it?
Hello!
We were transitioning from syn
on version 3.2.1
, to version 3.3.0
but when rolling out the upgrade, we have noticed the following error originating in the nodes with the latest syn
:
GenServer X terminating
** (CaseClauseError) no case clause matching: {:ok, {:on_process_registered, [_meta], _ts, :syn_registry_by_name, :syn_registry_by_pid}}
(syn 3.3.0) /app/deps/syn/src/syn_registry.erl:119: :syn_registry.register_or_update/4
(syn 3.3.0) /app/deps/syn/src/syn_registry.erl:87: :syn_registry.register/4
...
Similar error was reported by the nodes in the cluster that were running the previous version of syn
:
GenServer Y terminating
** (CaseClauseError) no case clause matching: {:ok, {:on_process_registered, :undefined, [_meta], _ts, :syn_registry_by_name, :syn_registry_by_pid}}
(syn 3.2.1) /app/deps/syn/src/syn_registry.erl:119: :syn_registry.register_or_update/4
(syn 3.2.1) /app/deps/syn/src/syn_registry.erl:87: :syn_registry.register/4
The issue seems to be caused by the commit Add update callbacks with previous meta info, because now we might get a tuple of unexpected arity from the syn_gen_scope:call
on both nodes (version 3.3.0
may get result in the shape {ok, {CallbackMethod, Meta, Time, TableByName, TableByPid}}
which won't match and version 3.2.1
will fail to match if the second tuple contains the additional PreviousMeta
).
Making the change backwards compatible should probably be as easy as adding an extra clause in the case
statement that matches if the tuple is missing PreviousMeta
and we can possibly get rid of this once releasing syn
to a new major version.
However, to make upgrades smooth we would also need the change to be forward-compatible. That could have been achieved by returning a map instead of the tuple, therefore the old version could ignore the added key.
But for that it is probably too late, hehe.
Would it make sense to add the extra clause to handle tuples without PreviousMeta
in order to at least mitigate the issue?
I'd be more than happy to help, of course!
Thanks for the library, it has been serving us very well:)
Hi,
syn:member
written as;
member(GroupName, Pid) ->
syn_groups:member(GroupName, Pid).
but syn_groups:member
is declared as;
member(Pid, GroupName)
as I checked syn_groups
other functions, to make alignment with them, seems syn_groups:member
should declare as
member(GroupName, Pid)
like syn_groups:leave
function.
There is same issue for local_member
on syn
and syn_groups
modules.
I've fixed these issues on
https://github.com/tsenturk/syn/pull/new/fix-syn-groups-member-local-member-declare
but not sure, eventually it seems a breaking change.
When we leave a process that belongs to multiple groups, it is unlinked. This should be the case only when that process belongs to one group and only one. Otherwise the gen_server is no more able to track EXIT messages to properly clean the other groups that process belongs to when it will die.
You claim that Name is any(), but there is an issue:
syn:join({"topic",1212},self()).
** {{aborted,{badarg,[syn_pg_table,
[{{syn_pg_table,'$1','$2','_'},
[{'=:=','$1',{"topic",1212}},{'=:=','$2',<0.878.0>}],
['$2']}]]}},
I fixed this by using term_to_binary:
syn:join(term_to_binary({topic,"1212"}),self()).
ok
I am not sure how to fix this. Would it be OK to just wrapping Name with term_to_binary? Or the possible solution lies deeper in the code?
Hello, dears.
I am using version 3.3.0
. Suppose we add node to a scope like this: syn:add_node_to_scopes([users])
and register a process like this:
Pid = self().
syn:register(users, "hedy", Pid).
If I then call syn:lookup
with a non-existent scope name like this:
syn:lookup(sers, "hedy").
I will get this error:
** exception error: {invalid_scope,sers}
in function syn_registry:lookup/2 (_build/default/lib/syn/src/syn_registry.erl, line 76)
If I call syn:lookup
with an existing scope like this:
syn:lookup(users, "hedy").
I will get undefined
. If I try to register Pid
again, I will get the following error:
{error,not_alive}
Can you help me understand what is incorrect? Also, why is it that if we perform a lookup in a non-existent scope, all existing scopes will be missed?
I found myself implementing this feature many times and I think that this feature would be useful to be shipped with syn.
By anycast I mean sending message to one process of the group which is choosen with some algorithm.
Possible algorithms:
I don't think that in would be useful, but anycall would make sense too
Hi,
I do have to register 2k processes (sometimes more) at the exact same time. And sometimes join/2
was timing out. Is there a solution about that?
Thanks.
UPDATE: I just saw your blog post with the syn benchmark. I don't understand why I cannot achieve that. Registry messages keep getting stuck in the :syn_groups genserver. The only possible reason I could find is that I also heavily do :mnesia inserts, maybe it is slowing down mnesia? (doing something like 15M inserts in a 30 minutes frame)
UPDATE2:
So to avoid the timeout thing, I forked the project and added async_join
just to see how fast it would add the processes to the group.
The :syn_groups genserver went up to 120k (I have something like 330k processes to add to the group) join messages in its mailbox and took approx. 30 minutes to handle them all. It was mostly hanging on :mnesia functions. Feels like the :bag type table is just slow.
I recognize that being able to share group names / process keys across nodes is the primary purpose; however, there are several cases where I'd like to be able to restrict the sharing to the local node, yet retain the API and functionality of :syn
.
Specifically, I'm looking at a homogenous codebase that is started in N instances; for each of them I'd like to be able to create multiple groups of the form {local_events, EventType}
. Using the current API for :syn
, I'd need to add a Node
key to the tuple, e.g. {local_events, EventType, node()}
to ensure uniqueness across nodes.
The same general usage applies to Keys: I need an {event_coordinator, EventType}
on each node, yet unless it is tagged with the node as well, there will be conflicts.
I am happy using {..., node()}
as a work around, but this would be a useful addition to the API in my view.
Currently we only support gen_server
via tuples for single processes, i.e. you can start a gen_server with a name that is managed by syn
, but that name cannot be a process group.
It would be interesting to transparently support groups as well, because you can then use one gen_server:cast/2
to send a cast to a group of processes, managed by syn
.
gproc
has this feature, by means of matching on different naming schemes:
{n, g, <<"your name">>}. %% would be a global name for a process
{p, g, <<"your name">>}. %% would be a global name for a process group
Both of these can be used with the gen_server
name API and its simple for the registry to distinguish between single processes and groups that way.
While this is one possibility, we could also simply add another branch in syn:send/2
that gets executed when the name was not registered for a single process and simply publishes to a group with the given name, hoping that it exists - though thats seemingly a more dirty solution.
send(Name, Message) ->
case whereis_name(Name) of
undefined ->
case syn_groups:publish(Name, Message) of
{ok, 0} -> {badarg, {Name, Message}};
{ok, _} -> ok
end;
Pid -> Pid ! Message, Pid
end.
Generally I really like that gproc
's interface for registering names and groups etc. is the same for both and works by distinguishing between them with these special tuples.
I've got two nodes, A and B which service users who can communicate with one another. In the event A splits from B, and later rejoins, A no longer receives messages originated from B. Since I can't call syn.init()
again, how do I correct this behavior?
I'm getting error on exit with reason {:shutdown, :left}
inside elixir phoenix. Is it possible to customize the handle_info EXIT
in syn_registry.erl
to avoid this error?
for example, i have node A_1, A_2, B_1, B_2, and i only run syn on node A_1, A_2, will cause error
Terminating with reason: {badarg,[{erlang,length,[{badrpc,{'EXIT',{undef,[{syn_registry,sync_get_local_registry_tuples,['[email protected]'],[]},{rpc,'-handle_call_call/6-fun-0-',5,[{file,"rpc.erl"},{line,197}]}]}}}],[]},{syn_registry,'-handle_info/2-fun-1-',2,[{file,"src/syn_registry.erl"},{line,237}]},{global,trans,4,[{file,"global.erl"},{line,430}]},{syn_registry,handle_info,2,[{file,"src/syn_registry.erl"},{line,230}]},{gen_server,try_dispatch,4,[{file,"gen_server.erl"},{line,616}]},{gen_server,handle_msg,6,[{file,"gen_server.erl"},{line,686}]},{proc_lib,init_p_do_apply,3,[{file,"proc_lib.erl"},{line,247}]}]}
I'm having an issue where it seems like syn is not unregistering processes when they exit. I'm able to get a pid from syn:whereis_name
but if I try to send messages to the result I get a no such process
error. I've verified the process indeed does not exist.
Any idea?
It would be nice to have a local callback for when a progress has been added to the registry in the same way there is an exit and on conflict. This way I don't have to poll the group/etc for new membership, etc.
registry_process_enter_callback, [my_callback, callback_on_process_enter]
Can you add also callbacks when a process join a group or a new pid is registered ? Right now I see callbacks are only when a pid leaves a group...
Silviu
Hi,
First of all - great work! This library is solving a lot of issues for me, and with great ease! Thanks!
My only issue is that when a process disconnects, syn prints an error log (syn_groups.erl#L264) - I don't see in the code any way to turn that off, or maybe I'm missing something? For my project, it's very normal for a process to terminate in an "unhealthy" way, which means that my log is full of supposed "errors".
Would it be possible to allow turning this logger off? Thanks!
Custom resolve_registry_conflict function can also rely on Time.
Can we extend (Name, {Pid1, Meta1}, {Pid2, Meta2}) tuples with Time like in do_resolve_registry_conflict :
Name, {Pid1, Meta1, Time1}, {Pid2, Meta2, Time2} ?
The i_find_by_pid(...) at.
https://github.com/ostinelli/syn/blob/master/src/syn_backbone.erl#L331
expects a list with only one element which is not correct due to process can be registered more than once, i.e. under several different aliases. As a consequence, such a process will never be cleaned up and table will "leak".
I have tried to solve this issue in an ongoing pull request(just as an illustration), but it is required to change the return type of find_by_pid/1 function to list
It would be nice if I could have a group with a set of keys inside of it. For example: I have a collection of processes that belong to a group. However I need to make sure that certain requests always goes to a specific process within the group.
Group -> [{Key, Pid}, {Key2, Pid2}]
This way I can hash the requester ID to one of these Pid's in the group. Right now I have to pull all the processes out of the group and then pull key for each process, then do my mapping.
Example Elixir code: If Erlang is needed I can rewrite it
def processForKey(key) do
# Basic list comprehension to get all the pids with their keys (keys are integers)
members = for pid <- :syn.get_members(:mygroup) do
pidKey = :syn.find_by_pid(pid)
{pidKey, pid}
end
# sort the keys
members = Enum.sort(fn({k1, _}, {k2, _}) -> k1 > k2 end)
# pull out the process that should map to the passed in key
{_, pid} = Enum.at(members, rem(:erlang.phash2(key), length(members)))
pid
end
It would be nice if when adding the group I could add a meta/key value that I can pull out with a call like
:syn.get_members(:my group, :with_meta)
Thanks!
In case a node is permanently removed from a cluster, allow to remove it from the underlying mnesia database db_nodes.
We need to add support for Elxiir "via-tuple" ProcessRegistry API.
Note: Gproc already supports this, making usage easier in Elixir without introducing another wrapper.
Example Target Usage:
GenServer.start_link(My.GenServer.Module, [], [name: {:via, :syn, "my-key"}])
GenServer.cast({:via, :syn, "my-key"}, :message)
GenServer.call({:via, :syn, "my-key"}, :message)
I think the multi aliases feature should be live, because only library users can know real purpose of registration.
I need register process by several aliases. It is very useful for me.
I can make patch for that, but not sure that it will be included in main code.
i met:
1> syn:register(a, self()).
ok
2> syn:register(b, self()).
{error,pid_already_registered}
how i can use syn for multi aliases registration ?
thanks.
Hi, I was trying to see if I could properly shutdown a process on name conflict (I need it to finish work before being killed).
But I cannot have my event handler to be called. (By the way the docs tells that syn:set_event_handler/1
returns ok
but that is not the case.
Here is my test code, that I run with two shells as iex --sname alice tt.exs
and the "bob" version, and then call H.connect/0
.
Mix.install([{:syn, "~> 3.0"}])
defmodule X do
use GenServer
def init(_), do: {:ok, []}
def handle_info(info, state) do
info |> IO.inspect(label: ~S[info])
{:noreply, state}
end
def terminate(reason, state) do
reason |> IO.inspect(label: ~S[reason])
{:noreply, state}
end
end
defmodule Ev do
@behaviour :syn_event_handler
def resolve_registry_conflict(name, pid_a, pid_b) do
binding |> IO.inspect(label: ~S[resolve_registry_conflict])
pid_a |> IO.inspect(label: ~S[keep])
raise "not called"
end
def on_process_joined(scope, group_name, pid, meta, reason) do
binding |> IO.inspect(label: ~S[on_process_joined])
end
end
defmodule H do
def connect do
case node() do
:"alice@lud-elitebook" -> :"bob@lud-elitebook"
:"bob@lud-elitebook" -> :"alice@lud-elitebook"
end
|> Node.connect()
end
end
[_ | _] = :syn.set_event_handler(Ev)
:syn.add_node_to_scopes([:spool_scope])
GenServer.start_link(X, nil, name: {:via, :syn, {:spool_scope, :myspool}})
Can you see if I am doing something wrong?
Thank you.
I saw get_members_with_meta at #7 method, why to the latest version, did not return this feature now( 3.3.0)
This method is very useful. Is there any other way to implement this requirement?
-spec get_members_with_meta(Name :: any()) -> [{pid(), any()}].
get_members_with_meta(Name) ->
i_get_members_with_meta(Name).
We have a cluster of nodes running our product that is compiled with syn 2.0.1.
I'm trying to update nodes one by one (using rolling update) to a newer version of our product compiled with syn 2.1.1.
But new nodes fail to start because of the following syn failure:
2020-06-17 00:36:26.923 [info ] Syn(:"[email protected]"): Terminating with reason: {:function_clause, [{:syn_registry, :"-registry_automerge/2-fun-0-", [{"GlobalSelfScaler", #PID<37561.2151.0>, :undefined}], [file: '/opt/app/deps/prod/syn/src/syn_registry.erl', line: 648]}, {:lists, :foreach, 2, [file: 'lists.erl', line: 1338]}, {:syn_registry, :"-registry_automerge/2-fun-1-", 2, [file: '/opt/app/deps/prod/syn/src/syn_registry.erl', line: 652]}, {:global, :trans, 4, [file: 'global.erl', line: 425]}, {:syn_registry, :handle_info, 2, [file: '/opt/app/deps/prod/syn/src/syn_registry.erl', line: 406]}, {:gen_server, :try_dispatch, 4, [file: 'gen_server.erl', line: 637]}, {:gen_server, :handle_msg, 6, [file: 'gen_server.erl', line: 711]}, {:proc_lib, :init_p_do_apply, 3, [file: 'proc_lib.erl', line: 249]}]}
| (pid=<0.1528.0> )
2020-06-17 00:36:26.923 [error] GenServer :syn_registry terminating
** (FunctionClauseError) no function clause matching in anonymous fn/1 in :syn_registry.registry_automerge/2
(syn 2.1.1) /opt/app/deps/prod/syn/src/syn_registry.erl:648: anonymous fn({"GlobalSelfScaler", #PID<37561.2151.0>, :undefined}) in :syn_registry.registry_automerge/2
(stdlib 3.12.1) lists.erl:1338: :lists.foreach/2
(syn 2.1.1) /opt/app/deps/prod/syn/src/syn_registry.erl:652: anonymous fn/2 in :syn_registry.registry_automerge/2
(kernel 6.5.2) global.erl:425: :global.trans/4
(syn 2.1.1) /opt/app/deps/prod/syn/src/syn_registry.erl:406: :syn_registry.handle_info/2
(stdlib 3.12.1) gen_server.erl:637: :gen_server.try_dispatch/4
(stdlib 3.12.1) gen_server.erl:711: :gen_server.handle_msg/6
(stdlib 3.12.1) proc_lib.erl:249: :proc_lib.init_p_do_apply/3
Last message: {:nodeup, :"[email protected]"}
State: {:state, :syn_event_handler, :undefined, :undefined, #PID<0.1530.0>} | (pid=<0.1528.0> module=gen_server function=error_info/7 line=889 )
2020-06-17 00:36:26.924 [error] Process :syn_registry (#PID<0.1528.0>) terminating
** (FunctionClauseError) no function clause matching in anonymous fn/1 in :syn_registry.registry_automerge/2
(syn 2.1.1) /opt/app/deps/prod/syn/src/syn_registry.erl:648: anonymous fn({"GlobalSelfScaler", #PID<37561.2151.0>, :undefined}) in :syn_registry.registry_automerge/2
(stdlib 3.12.1) lists.erl:1338: :lists.foreach/2
(syn 2.1.1) /opt/app/deps/prod/syn/src/syn_registry.erl:652: anonymous fn/2 in :syn_registry.registry_automerge/2
(kernel 6.5.2) global.erl:425: :global.trans/4
(syn 2.1.1) /opt/app/deps/prod/syn/src/syn_registry.erl:406: :syn_registry.handle_info/2
(stdlib 3.12.1) gen_server.erl:637: :gen_server.try_dispatch/4
(stdlib 3.12.1) gen_server.erl:711: :gen_server.handle_msg/6
(stdlib 3.12.1) proc_lib.erl:249: :proc_lib.init_p_do_apply/3
Initial Call: :syn_registry.init/1
Just like process registries have a syn:registry_count()
and syn:registry_count(Node)
, would it be possible to have something similar for groups (get the total number of groups and number of local groups)?
I'm wondering if there's a way to do that without impacting performance (maybe adding counters somewhere).
If you point me in the right direction I could also look at how to implement this myself. Thank you!
Following the instructions in the readme, I have the following application section in Mix.exs:
def application do
[applications: [:logger, :syn],
mod: {MyMod, []}]
end
And in MyMod.start/2:
def start(_type, _args) do
import Supervisor.Spec, warn: false
connect_nodes() # currently does nothing
:syn.init
children = [] # for later
opts = [strategy: :one_for_one, name: MyMod.Supervisor]
Supervisor.start_link(children, opts)
end
end
When I start the application via iex -S mix
, I get the following error:
** (Mix) Could not start application my_mod: exited in: MyMod.start(:normal, [])
** (EXIT) an exception was raised:
** (MatchError) no match of right hand side value: {:error, {:aborted, {:node_not_running, :nonode@nohost}}}
src/syn.erl:63: :syn.init/0
(my_mod) lib/my_mod.ex:7: MyMod.start/2
(kernel) application_master.erl:273: :application_master.start_it_old/4
Adding :mnesia.start
before :syn.init
allows the application to start, but I'm concerned that there might be a side-effect I'm not anticipating, or an issue I might not see in single-node mode.
hi,
Trying to get started with syn, but it doesn't seem to work :
I have two node in my machine (foo@my-pc
and bar@my-pc
).
in foo
i have a simple genserver that is started with this name : {:via, :syn, :example_server}
,
and handles a :get
call.
running : GenServer.whereis({:via, :syn, :example_server})
gives me the pid.
My problem is in bar
:
I connected the nodes :
Node.connect :'foo@my-pc'
And than tried running GenServer.whereis({:via, :syn, :example_server})
but i get nil
and of course GenServer.call({:via, :syn, :example_server}, :get)
doesn't work :(
What am i missing ?
Syn doesn't accept some names as the following snippet shows, despite saying it accepts any()
.
Probably related to #3 and actually sounds like the problem was fixed in the past and is back.
2> application:ensure_all_started(syn).
{ok,[mnesia,syn]}
3> syn:join({stream, {server, <<"hello">>}}, self()).
** exception exit: {{aborted,
{no_exists,
[syn_groups_table,
[{{syn_groups_table,'$1','$2','_','_'},
[{'==','$1',{{stream,{server,<<"hello">>}}}},
{'=:=','$2',<0.96.0>}],
['$_']}]]}},
{gen_server,call,
[{syn_groups,nonode@nohost},
{join,{stream,{server,<<"hello">>}},<0.96.0>,undefined}]}}
in function gen_server:call/2 (gen_server.erl, line 204)
4>
=INFO REPORT==== 6-Apr-2017::10:22:33 ===
Terminating syn_groups with reason: {aborted,
{no_exists,
[syn_groups_table,
[{{syn_groups_table,'$1','$2','_','_'},
[{'==','$1',
{{stream,{server,<<"hello">>}}}},
{'=:=','$2',<0.96.0>}],
['$_']}]]}}
=ERROR REPORT==== 6-Apr-2017::10:22:33 ===
** Generic server syn_groups terminating
** Last message in was {join,{stream,{server,<<"hello">>}},<0.96.0>,undefined}
** When Server state == {state,undefined,undefined}
** Reason for termination ==
** {{aborted,{no_exists,[syn_groups_table,
[{{syn_groups_table,'$1','$2','_','_'},
[{'==','$1',{{stream,{server,<<"hello">>}}}},
{'=:=','$2',<0.96.0>}],
['$_']}]]}},
[{mnesia,abort,1,[{file,"mnesia.erl"},{line,318}]},
{syn_groups,i_find_by_pid_and_name,2,
[{file,"/home/essen/tmp/syn/_build/default/lib/syn/src/syn_groups.erl"},
{line,319}]},
{syn_groups,handle_call,3,
[{file,"/home/essen/tmp/syn/_build/default/lib/syn/src/syn_groups.erl"},
{line,180}]},
{gen_server,try_handle_call,4,[{file,"gen_server.erl"},{line,615}]},
{gen_server,handle_msg,5,[{file,"gen_server.erl"},{line,647}]},
{proc_lib,init_p_do_apply,3,[{file,"proc_lib.erl"},{line,247}]}]}
It looks like in some cases when starting a gen_server process via syn through a supervisor if the process crashes and the supervisor restarts it I get an already_started error which I imagine is because the process isn't unregistered fast enough (?) I am just guessing here.
Anyway if I replace the {via, syn, Name} with {local, Name} it works as expected (but naturally I want to use syn to be able to name my processes with terms rather than atoms).
I attached a zip that contains the stuff that I just started working on. If you fire up the docker containers and then start the app with rebar3 shell, in the shell you can call kyu:kill(test).
and that should do the trick (the problem doesn't always occur at first but if you keep trying it will happen).
I needed this for the use case from README
Typical Use Case: a chatroom.
To send a system message about user leaving chatroom
Using is_alive
?
Hi,
I am currently trying to setup Syn
with 2-nodes cluster app. Here are steps to reproduce my case:
start/2
I connect nodes between each other via net_kernel:connect/1
;syn:init/0
to initialize Syn
itself;syn:join({user, UserId}, self(), [ %% META_HERE %%]).
;syn:get_members({user, UserId}, with_meta).
, and get[{ <0.1234.0>, [ %% META_HERE %% ] }]
at first server and []
at second one.For sure node/0
and nodes/0
shows that my first
and second
nodes inside same cluster.
Furthermore if I manually shutdown one of node, other node echo this log message
Received a MNESIA down event, removing on node...
in my opinion this means that nodes been connected and Syn
correctly handled shutting down one of them.
Please hint me what I do wrong, and possible way to bypass and/or debug this issue.
Also I can provide any additional information.
Hello.
Syn version 2.* had syn.unregister_and_register
function to make this operations atomic. Is it plans to make the same function in Syn 3.0 or another solution exists?
Thank you for your work!
Hi @ostinelli, I found this case which would be nice to classify first (before more detailed test and clarification).
syn:is_member/3
syn:members/2
The first call makes the negative false
until syn:members/2
invocation has done from the client side.
It seems that the second call ensures the consistency while the first one doesn't.
If so (is_member/3
isn't consistent by nature) the right doc could be done (to describe the feature properly).
If not (there is a bug 🐛) maybe I could help (at least to give more info and try to reproduce via ct test).
What do you think?
Best regards, Slava
I didn't see a description anywhere which versions are supported. I would assume R18 since that was around the time this was written, but I am also curious about R19.
I would like to suggest extending the via-tuple support to allow registering metadata as well. This would enable one to do as follows:
iex> tuple = {:via, :syn, {:devices, "SN-123-456789", [meta: :one]}}
{:via, :syn, {:devices, "SN-123-456789", [meta: :one]}}
iex> GenServer.start_link(__MODULE__, [], name: tuple)
{ok, #PID<0.105.0>}
iex> :syn.lookup(:devices, "SN-123-456789")
{#PID<0.105.0>, [meta: :one]}
For reference, registering a value in a via-tuple is already supported by the Elixir Registry, see: https://hexdocs.pm/elixir/1.12/Registry.html#module-using-in-via
If this feature is considered appropriate and useful, I would like to implement it and make a PR with the changes.
on syn_groups just the case of a process being in one group is handled when a process receive Exit signal
handle_info({'EXIT', Pid, Reason}, State) ->
%% check if pid is in table
case i_find_by_pid(Pid) of
undefined ->
%% log
case Reason of
normal -> ok;
killed -> ok;
_ ->
error_logger:error_msg("Received an exit message from an unlinked process ~p with reason: ~p", [Pid, Reason])
end;
Process ->
%% get group
Name = Process#syn_groups_table.name,
%% log
case Reason of
normal -> ok;
killed -> ok;
_ ->
error_logger:error_msg("Process of group ~p and pid ~p exited with reason: ~p", [Name, Pid, Reason])
end,
%% delete from table
remove_process(Process)
end,
#######
i_find_by_pid(Pid) ->
case mnesia:dirty_index_read(syn_groups_table, Pid, #syn_groups_table.pid) of
[Process] -> Process;
_ -> undefined
end.
This discussion shouldn’t be considered as an issue, rather as a feature.
The main point that is Syn can be used by different applications under the common shared release.
{syn, [
{event_handler, my_custom_event_handler}
]}
This global setup listed above doesn’t meet the criteria:
The solution:
register(Name, Pid, Meta, Handler);
join(GroupName, Pid, Meta, Handler);
Where each interested party can setup their own event_handler
within particular called scope. This argument will have the higher priority then env
parameter and would provide the users with flexibility to manage their own registry scopes.
P.S. If this feature is considered as useful I may provide appropriate PR as solution, thanks in advance.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.