Registry: Supervising and Dispatching at the same time?

It’s very easy to supervise :simple_one_for_one children with Registry. The Registry is unique, and via tuple works very good.
But the question is: How to dispatch to all these children?
It seems to me, that is possible to dispatch only to duplicate registry!
Why it’s not possible to list all registered in Registry pid’s?

The workaround is - using Supervisor.which_children get all pids and dispatch messages to them. But it looks a bit rusty.

Have you taken a look at the Using as a dispatcher section of the documentation? If that still doesn’t answer your question, can you give a bit more information about what you are trying to do?

You could use Registry.match/3 for that:

Registry.start_link(:unique, MyRegistry)
Registry.register(MyRegistry, :foo, nil)
spawn(fn -> Registry.register(MyRegistry, :bar, nil); :timer.sleep(:infinity) end)
Registry.match(MyRegistry, :_, :_)
# [{#PID<0.80.0>, nil}, {#PID<0.86.0>, nil}]

However, I feel that it’s better to have another (:duplicate) registry for dispatching. The unique registry should be used when you want to find a particular process which plays some role in your system. The duplicate registry should be used when you want to dispatch to a group of processes. Those are two different needs, so I believe that using different registries describes the idea more explicitly. For what it’s worth, this is how I’d do it myself. If a process needs to be uniquely found, but also belongs to some process group, I’d register it with two registries.

Two registeries is great idea as for me from the point of good architect. I thought about it.

But the problem is with consistency:

Registration in duplicate registry is doing manualy, as I understand. For example, in init function:

def init(_) do
  Registry.register(MyRegistry, "worker_class_name", "my_worker_name")
end

But if the supervisor restarts the simple_one_for_one worker after failure, PID is updated in only uniq register.

So, as I understand, init should be rewrited in such manner:

def init(_) do
  # check here, that registry already have entry with such name, and if not
  Registry.register(MyRegistry, "worker_class_name", "my_worker_name")
  # and if yes - firstly remove that entry, but hard to understand how, and then
  Registry.register(MyRegistry, "worker_class_name", "my_worker_name")
end

So, may be there are some recipes of doing this already? Or may be Registry need some additional functionality? Or even, may be my approach is totally wrong.

It’s going to work fine for both registries. If the process terminates, it’s deregistered from all the registries. When the new process is started, it will be properly registered with both registries. You don’t need to check whether the process is already registered, nor remove it from the registry manually.

2 Likes