I find it surprising that :via
is not supported for duplicate registries. If you can’t register a pool of processes under the same name what exactly is the use-case for having duplicate-key registries? The docs include an example of dispatching (Registry — Elixir v1.18.0-dev), but I’m not clear how you would even put a process into a custom Registry if :via
is not allowed. What am I missing? How do you actually use a custom Registry with duplicate keys?
Registry.register
exists. You can manually (and explicitly) call it. :via
is just a way to let OTP take over the process registration. But that comes with the downside that via expects a given via tuple to map to a single process, not multiple ones.
Process pooling is not included in the standard library of Elixir. There a few excellent libraries on hex.pm:
Some use-cases for having duplicate-key registries are explained in your linked documentation, if only you read a few more paragraphs from that point, you will find them.
:via
isn’t specifically related to Registry
, it’s just a fancy way to tell :gen
’s name handling to call register_name
and whereis_name
on a specified module:
(related: Elixir’s wrappers transform a bare atom name into {:local, name}
)
Conversely, Registry
isn’t otherwise related to process naming - it’s just a key-value store that automatically cleans up keys when the registering process exits.
The simplest thing to build with a :duplicate
registry is a local publish-subscribe bus:
- processes that are interested in a topic
{:foo, 123}
useRegistry.register
to “subscribe” to that key - when a message needs to be delivered for topic
{:foo, 123}
, the sender usesRegistry.dispatch
to send it to each PID - when a process that’s subscribed to a topic stops, its registration is automatically cleaned up
You might even use both a :unique
and a :duplicate
registry with the same keys, for instance in the classic “multiplayer game” architecture:
- the
:unique
registry tracks a GenServer per “game”. A:via
tuple pointing to this registry is used when sending moves, joins, etc to the game - the
:duplicate
registry tracks the Liveview processes of players per “game”, and is used to distribute updates to all players
Thank you for the informative responses! This is stuff that I’m going to have to ponder about for a while, but I see the set/get is pretty straightforward using Registry.register/3
and Registry.lookup/2
:
iex> {:ok, _} = Registry.start_link(keys: :duplicate, name: ClusterRegistry)
iex> Registry.register(ClusterRegistry, :group, "a")
iex> Registry.register(ClusterRegistry, :group, "b")
iex> Registry.register(ClusterRegistry, :group, "c")
iex> Registry.lookup(ClusterRegistry, :group)
[{#PID<0.148.0>, "a"}, {#PID<0.148.0>, "b"}, {#PID<0.148.0>, "c"}]
What is interesting to me is that Registry.register/3
really is all about storing the current process – the value seems almost secondary. The other thing that is interesting is that when a process terminates, anything that it registered gets removed (as. you pointed out Matt):
iex> Task.start(fn ->
Registry.register(ClusterRegistry, :group, "hello?")
Process.sleep(10_000)
end)
iex> Registry.lookup(ClusterRegistry, :group)
[{#PID<0.148.0>, "a"}, {#PID<0.148.0>, "b"}, {#PID<0.148.0>, "c"}, {#PID<0.148.0>, "hello?"}]
# Wait 10 seconds
iex> Registry.lookup(ClusterRegistry, :group)
[{#PID<0.148.0>, "a"}, {#PID<0.148.0>, "b"}, {#PID<0.148.0>, "c"}]
I feel like I’m only becoming dimly aware of the things that you can do with these tools…
That’s true indeed. In the end it’s a registry, so exists to hold registrations of processes. But the value can be very useful for certain usecases within the space of process registration.