To directly answer your question, the problem is in the {:ok, _storage} = Supervisor.start_child(supervisor, {Usersystem.Storage, name: {:via, Registry, {Usersystem.Registry, "storage"}}})
.
First, there is no name
in child_spec, the closest field you might find is the id
, but it also does not solve your issue, since this id is local to the supervisor. Your tuple is valid enough for starting the supervisor, but the {:via, Registry, _}
does not mean registering it in the registry table. You need to add this tuple to the Usersystem.Storage
server instead:
defmodule Stackoverflow do
require Logger
def start() do
opts = [strategy: :one_for_one, name: Usersystem.Supervisor]
{:ok, supervisor} = Supervisor.start_link([], opts)
# The supervisor starts a registry table, which can be used by servers later
{:ok, _} =
Supervisor.start_child(supervisor, {Registry, keys: :unique, name: Usersystem.Registry})
{:ok, _storage} =
Supervisor.start_child(
supervisor,
# A map child spec, check https://hexdocs.pm/elixir/1.12/Supervisor.html#child_spec/2
%{
# {Module callback, function callback from module, params to the function (in this case ignored)}
start: {Usersystem.Storage, :start_link, [:ok]},
# This id is internal to the supervisor, it is only recognizable under `Usersystem.Supervisor`
id: :storage
}
)
# I did not imported Plug.Cowboy since this example does not need it
# {:ok, _router} = Supervisor.start_child(supervisor, {Plug.Cowboy, scheme: :http, plug: Usersystem.Router, options: [port: 8080] })
res = Registry.lookup(Usersystem.Registry, "storage")
sup_children = Supervisor.which_children(Usersystem.Supervisor)
Logger.info("registry response: #{inspect(res)}")
# [info] registry response: [{#PID<0.149.0>, nil}]
# I will not log the long response, but note how the supervisor logs the storage child with the `:storage` id we provided
Logger.info("Supervisors children response: #{inspect(sup_children)}")
{:ok, supervisor}
end
end
defmodule Usersystem.Storage do
use GenServer
def start_link(_) do
# This will register the server properly in the Usersystem.Registry table under "storage"
GenServer.start_link(__MODULE__, [], name: {:via, Registry, {Usersystem.Registry, "storage"}})
end
def init(_), do: {:ok, nil}
end
However, if you have only one storage server, maybe you don't even need the Registry. instead of GenServer.start_link(__MODULE__, [], name: {:via, Registry, {Usersystem.Registry, "storage"}})
, you could just do GenServer.start_link(__MODULE__, [], name: :my_storage_server
. Which makes the server start under the atom name you provided. Note that you could name this as :storage
and it would not conflict with the supervisor child id also called :storage
at all, since the sup id is internal, I'm just using a different name to make this example clearer. You can verify the name is reachable, by simply starting the supervisor and typing: Process.where_is(:my_storage_server)
, which will return the id of your server. When your server restarts, it will be registered under the same atom name, so it will be available without knowing its pid. Since it is a genserver, any process calling the GenServer.call/cast passing the my_storage_server
as first parameter will find the storage server.
Some notes:
GenServer.start_link(__MODULE__, [], name: __MODULE__)
or even GenServer.start_link(__MODULE__, [], name: MyCustomServerName)
. It is valid as long as we pass an atom not registered yet. Note that module names are just atoms under the hood.