Tradeoffs around Registry usage

Hi everyone, what are the tradeoffs in running a single Registry (or
a few Registries) vs. many Registries? I’ve found that right now,
due to using via_tuples on kinds of processes that each had their
set of ids, I’ve been creating one Registry per kind of process. Now
I’m looking at what it would mean for multiple kinds of processes
under a single key, for instance, and I’m wondering if I should keep
on starting a single Registry per purpose or if “a god Registry” is an
acceptable idea… I’m aware this is a strangely open question, so I can provide more details on my specific scenario if you’re interested…

2 Likes

If you are using it within the same application, a single registry is reasonable, given you can “namespace” the keys, such as {:foo, 1}, {:bar, 1}, etc.

2 Likes

Hmm. Okay.

I did a little bit of testing. I use a via tuple to register processes, and so that would mean I would need to pass the module name in the via_tuple, something like

def via_tuple(id) do
  {:via, Registry, {MyRegistry, {__MODULE__, id}}}
end

And it’s not necessarily a huge change, I mean, in my first attempt at passing more varied objects through my system, I ended up with this:

  def arrive(new_location, {{module, id}, public_info}, from) do
    GenServer.call(via_tuple(new_location), {:arrive, {{module, id}, public_info}, from})
  end

  def handle_call({:arrive, {{module, mob_id}, public_info}, from_loc}, _from, state) do
    state.entities
    |> Map.keys
    |> Enum.each(fn({module, id}) ->
      Kernel.apply(module, :handle, [id, {:arrive, public_info, from_loc}]) end)
    {:reply, :ok, %Location{state | entities: Map.put(state.entities, {module, mob_id}, public_info)}}
  end

The impetus for the question is this kind of complex and ugly pipeline where I send a message to a bunch of modules (eventually all of those will have the same Behaviour), and now that I have this basic proof-of-concept working, it seems like pub-sub might be a good approach here, I just don’t know if using the Registry for said pub-sub would be a good idea…

Is this making any sense?

1 Like

I think it makes sense, but I would suggest using a duplicate registry to get the equivalent of process groups. Then you can register all of them under a single name (“mobs” or “entities” or whatever they are) and dispatch to that. Or create groups if you need to. I think you don’t need the “arrive” function either - just send the message directly and process it in handle_info. See:

https://hexdocs.pm/elixir/master/Registry.html#module-using-as-a-pubsub

https://hexdocs.pm/elixir/master/GenServer.html#module-receiving-regular-messages

1 Like

Well, now we’re getting to some interesting questions about intent!

  1. I do need the arrive function because it does something; it changes the state of the process (and I need this for more than just broadcasting purposes)
  2. Maybe the arrive function shouldn’t be responsible for doing the dispatching, but it probably should be responsible for saying “this event needs to be broadcast” – and something else will decide what to do with it…
  3. Is it in fact the correct intent to let handle_info do things like this? In the past I’ve done something like this:
  def send_heartbeat(_calling_pid) do
    Registry.dispatch(Registry.Tick, :subject_to_time, fn entries ->
      for {pid, _} <- entries, do: GenServer.cast(pid, :tick)
    end)
  end

I’m gonna keep handle_info in mind just in case, but I thought the intent for that was basically for … Meta messages?

1 Like