Here I want to describe what I discovered regarding GenServer and their name registration across the BEAM.
I’ll touch on Process.register/2
and GenServer.start_link/3
.
A basic GenServer
Below is a basic GenServer implementation, resembling a key-value store using a GenServer.
(alternatively you could achieve the same with an Agent)
defmodule Store do
use GenServer
def start_link(initial_state \\ %{}) do
GenServer.start_link(__MODULE__, initial_state)
end
def set(server, key, value) do
# ...
GenServer.cast(server, {:set, key, value})
end
def init(state) do
{:ok, state}
end
def handle_cast({:set, key, value}, state) do
# ...
{:noreply, state}
end
end
This Store
can now be used like this:
{:ok, store_pid} = Store.start_link()
Store.set(store_pid, :key, 42)
The downside is that to use the Store
you need to have a reference to store_pid
at hand.
Using Process.register
To give a unique name to a process you can use Process.register/2
Process.register(pid_or_port, name)
Registers the given pid_or_port under the given name.
name must be an atom and can then be used instead of the PID/port identifier when sending messages with Kernel.send/2.
To do so with the Store
module, a small change is needed in the init
server callback and how you cast
a message using the __MODULE__
name:
defmodule Store do
use GenServer
...
def init(state) do
Process.register(self, __MODULE__)
{:ok, state}
end
def set(key, value) do
# ...
GenServer.cast(__MODULE__, {:set, key, value})
end
...
end
This registers the single process under the given name, namely the atom :store
Now the Store
can be used without passing a process reference to the store:
{:ok, _store_pid} = Store.start_link()
Store.set(:key, 42)
Using GenServer.start_link
In the GenServer.start_link/3
documentation, you can read more about the start_link/3
spec.
The third option is a keyword list, you can use the :name
option to specify a name used for register the process.
Check out the docs about “Name registration” too, explaining the different options e.g. registering the process only locally or globally across the whole network nodes.
You can register a GenServer under the module name like so:
defmodule Store do
use GenServer
def start_link(initial_state \\ %{}) do
GenServer.start_link(__MODULE__, initial_state, name: __MODULE__)
end
...
end
Finally, you can use the GenServer without a reference to a process PID:
{:ok, _store_pid} = Store.start_link()
Store.set(:key, 42)