I have a Registry to create dynamic GenServers (each holding users’ parameters). I want to be able to take advantage of GenServer’s distributed multi_call / abcast, but since I’m using Registry, I must use a via tuple. Seems that mutli_call doesn’t like via tuples… Am I doing something wrong, is this by design, bug, or…? Thanks!
def start_link(uid, pid, params \\ %{}) do
GenServer.start_link(__MODULE__, [{pid, params}], name: via_tuple(uid))
end
defp via_tuple(uid), do: {:via, Registry, {@users_registry_name, uid}}
def get_params(uid) do
{replies, _bad} = via_tuple(uid) |> GenServer.multi_call(:get_params)
end
Error returned:
** (FunctionClauseError) no function clause matching in :gen_server.start_monitor/2
(stdlib) gen_server.erl:576: :gen_server.start_monitor(:node1@host, {:via, Registry, {:users_registry, "test"}})
(stdlib) gen_server.erl:467: :gen_server.send_nodes/5
(stdlib) gen_server.erl:425: :gen_server.do_multi_call/4
(project_name) lib/project_name/users_monitor.ex:17: ProjectName.UsersMonitor.get_params/1
Right, so my question is - how do I create dynamically-named GenServers and cast to them across multiple nodes?
What I mean is - I could have multiple GenServers with the same dynamically-generated name on different nodes. I want to be able to retrieve all params I’ve saved on those GenServers regardless of what node they are on.
I think you’re looking for a process registry that works in a distributed manner. Registry is only a local process registry, so maybe it’s not the right choice for you?
Maybe swarm or syn are a better match for your case.
Thanks! I was thinking about Swarm actually, but was wondering if there was anything native that I could use instead. Also - do names in Swarm have to be unique? If I start two GenServers with the same name, will sending to that name send to both?
Hmm… I’m not sure that it would. My scenario is this: a user connects with certain params, and I need to save those params. The same user can connect multiple times, with either similar or different params, but what is constant is his user_id (which in this case would be the “group”). I need to be able to retrieve all params saved under that user_id. My problem is that I could have many users connecting, so I can’t use a dynamically-created atom from the user_id as the group name…
One approach might be to have an aggregate store of user-related information, either on a centralised node or distributed using something like Riak Core or Mnesia, and have all nodes write to and read from there.
A simpler option, though potentially a bottleneck, would be the following: on each node run a regular named GenServer that dispatches to the local user processes through Registry. You can then use multi_call and abcast to broadcast a tuple {uid, message} to that named process, and each node would unwrap the message and relay it locally using the via-tuple in uid. For calls you may have to spawn a process to handle the reply asynchronously, to avoid blocking the dispatcher.
Yes, both syn and swarm can register arbitrary erlang terms as groups / names. I heartily recommend syn, by the way. It’s very pleasant to work with. I’ve never used it for anything big, though, but it seems to do a fairly decent job of auto-synchronizing names/groups on/after netsplits.