🎓 A1Learner
🔍
0 of 46 lessons completed

Elixir Registry

A registry is a name book that maps keys to running processes. A process registers under a key, and another process finds it through that key.

A unique registry maps one key to one worker

The worker registers itself when it starts. Stopping it clears its entry.

defmodule RegWorker do
  use GenServer
  def start_link({key, val}), do: GenServer.start_link(__MODULE__, {key, val})
  def init({key, val}) do
    Registry.register(RegU, key, val)
    {:ok, val}
  end
  def handle_call(:get, _from, v), do: {:reply, v, v}
end
{:ok, _} = Registry.start_link(keys: :unique, name: RegU)
{:ok, worker} = RegWorker.start_link({"k1", "meta1"})
IO.inspect(Enum.map(Registry.lookup(RegU, "k1"), fn {_p, val} -> val end))
IO.inspect(Registry.count(RegU))
GenServer.stop(worker)
IO.inspect(Enum.map(Registry.lookup(RegU, "k1"), fn {_p, val} -> val end))
IO.inspect(Registry.count(RegU))

A via name reaches a worker without a process identifier

The via name stands in for the worker address. A caller uses the name to send a call.

defmodule ViaWorker do
  use GenServer
  def init(v), do: {:ok, v}
  def handle_call(:get, _from, v), do: {:reply, v, v}
end
{:ok, _} = Registry.start_link(keys: :unique, name: RegV)
{:ok, _via} = GenServer.start_link(ViaWorker, "beta", name: {:via, Registry, {RegV, "k2"}})
IO.inspect(GenServer.call({:via, Registry, {RegV, "k2"}}, :get))
IO.inspect(is_pid(Registry.whereis_name({RegV, "k2"})))
GenServer.stop({:via, Registry, {RegV, "k2"}})
IO.inspect(is_pid(Registry.whereis_name({RegV, "k2"})))

A duplicate registry sends one message to every worker under a key

Two workers share one key in a duplicate registry. One dispatch reaches both of them.

defmodule DupWorker do
  use GenServer
  def start_link({key, val}), do: GenServer.start_link(__MODULE__, {key, val})
  def init({key, val}) do
    Registry.register(RegD, key, val)
    {:ok, val}
  end
  def handle_info({:ping, from}, v) do
    send(from, {:pong, v})
    {:noreply, v}
  end
end
{:ok, _} = Registry.start_link(keys: :duplicate, name: RegD)
{:ok, d1} = DupWorker.start_link({"job", "one"})
{:ok, d2} = DupWorker.start_link({"job", "two"})
IO.inspect(length(Registry.lookup(RegD, "job")))
Registry.dispatch(RegD, "job", fn entries ->
  for {pid, _} <- entries, do: send(pid, {:ping, self()})
end)
vals = for _ <- 1..2 do
  receive do
    {:pong, v} -> v
  end
end
IO.inspect(Enum.sort(vals))
GenServer.stop(d1)
GenServer.stop(d2)
IO.inspect(length(Registry.lookup(RegD, "job")))

Example

defmodule RoomWorker do
  use GenServer
  def start_link({key, val}), do: GenServer.start_link(__MODULE__, {key, val})
  def init({key, val}) do
    Registry.register(RegRoom, key, val)
    {:ok, val}
  end
  def handle_call(:get, _from, v), do: {:reply, v, v}
end
defmodule DeskWorker do
  use GenServer
  def init(v), do: {:ok, v}
  def handle_call(:get, _from, v), do: {:reply, v, v}
end
{:ok, _} = Registry.start_link(keys: :unique, name: RegRoom)
{:ok, alice} = RoomWorker.start_link({"alice", "hi from alice"})
{:ok, bob} = RoomWorker.start_link({"bob", "hi from bob"})
{:ok, _desk} = GenServer.start_link(DeskWorker, "desk open", name: {:via, Registry, {RegRoom, "desk"}})
IO.inspect(Enum.map(Registry.lookup(RegRoom, "alice"), fn {_p, val} -> val end))
IO.inspect(GenServer.call({:via, Registry, {RegRoom, "desk"}}, :get))
IO.inspect(Registry.count(RegRoom))
GenServer.stop(alice)
IO.inspect(Registry.count(RegRoom))
GenServer.stop(bob)
GenServer.stop({:via, Registry, {RegRoom, "desk"}})

Key points

  • Lookup: a lookup by key returns the processes registered under that key.
  • Unique keys: each key in a unique registry belongs to one process only.
  • Duplicate keys: a duplicate registry lets many processes share one key.
  • Via names: a via name lets a caller reach a worker without holding its process identifier.
  • Cleanup: a stopped process drops out of the registry on its own.

In this example, two workers register under their own keys and a desk worker registers under a via name, a lookup finds the first worker, a via call reaches the desk, the count shows three keys, stopping the first worker drops the count to two, and the desk worker is stopped last.

Note

registration happens inside the worker itself, so the registry tracks the right process.

Try it Yourself

Edit the code, then press Run.

Running is simulated

Exercise

What does a lookup return for a key with no workers?