I'm trying to resolve a GenServer managed by an Application by a registered name in elixir 1.9. Here's some example code:
defmodule Experiment do
use Application
def start(_type, _args) do
child_specs = [
config_spec()
]
Supervisor.start_link(child_specs, name: Experiment, strategy: :one_for_one)
end
defp config_spec() do
%{
id: Experiment.Data,
start: {
Experiment.Data,
:start_link,
[
"some data",
# set name of child process here
[:name, Experiment.Data]
]
}
}
end
end
defmodule Experiment.Data do
use GenServer
require Logger
def start_link(data, options \\ []) do
Logger.info("starting with data '#{data}' and options [#{Enum.join(options, ", ")}]")
GenServer.start_link(__MODULE__, data, options)
end
@impl true
def init(data) do
Logger.info("starting")
{:ok, data}
end
@impl true
def handle_call(:data, _from, state) do
{:reply, state, state}
end
@impl true
def terminate(reason, _state) do
Logger.info("terminated: #{reason}")
end
def get_data(server) do
GenServer.call(server, :data)
end
end
I've tested this in iex (iex -S mix
):
iex(1)> {:ok, sup_pid} = Experiment.start(nil, nil)
10:45:36.914 [info] starting with data 'some data' and options [name, Elixir.Experiment.Data]
10:45:36.916 [info] starting
{:ok, #PID<0.189.0>}
iex(2)> Experiment.Data.get_data(Experiment.Data)
** (exit) exited in: GenServer.call(Experiment.Data, :data, 5000)
** (EXIT) no process: the process is not alive or there's no process currently associated with the given name, possibly because its application isn't started
(elixir) lib/gen_server.ex:979: GenServer.call/3
iex(2)> Experiment.Data.get_data(Elixir.Experiment.Data)
** (exit) exited in: GenServer.call(Experiment.Data, :data, 5000)
** (EXIT) no process: the process is not alive or there's no process currently associated with the given name, possibly because its application isn't started
(elixir) lib/gen_server.ex:979: GenServer.call/3
iex(2)> send(Experiment.Data, {self(), "hi"})
** (ArgumentError) argument error
:erlang.send(Experiment.Data, {#PID<0.187.0>, "hi"})
iex(2)> send(Experiment, {self(), "hi"})
{#PID<0.187.0>, "hi"}
iex(3)>
10:46:05.410 [error] Supervisor received unexpected message: {#PID<0.187.0>, "hi"}
What's confusing me here is that I've tried to register Experiment.Data
with the name Experiment.Data
. But when I try to send a message to the process named Experiment.Data
it can't be found. I've added the terminate
hook to check it's not dying early, but this doesn't seem to be called. I've added logging lines which show me that the process is being started. I've also tried calling the application by registered name, which works. However calling its child (Experiment.Data
) by name does not. I understand the way child processes are started seems to have changed recently which seems to have caused some confusion. What am I doing wrong?
EDIT
Changing the config_spec function to this seems to have allowed it to work:
defp config_spec() do
%{
id: Experiment.Data,
start: {
Experiment.Data,
:start_link,
[
"some data",
[
{:name, Experiment.Data}
]
]
}
}
end
GenServer.start_link
wants a keyword list for options, or in other words a list of 2-tuples. [:name, Experiment.Data]
is a list of two atoms, so it doesn't take effect. You can write it either as [{:name, Experiment.Data}]
or using special syntax for keyword lists [name: Experiment.Data]
.