Hello,
I think this is related: I had a hard time using in combination start_child/2 to spawn children dynamically and start_supervised/2 for testing them, using the same child_spec for both.
In a supervisor if I start children (from let say a Game module) like this:
def start_game(player1, player2) do
Supervisor.start_child(__MODULE__, [{player1, player2}])
end
def init(:ok) do
Supervisor.init([Game], strategy: :simple_one_for_one)
end
I get this error (truncated) when I start_game:
** (MatchError) no match of right hand side value: {:error, {:EXIT, {:undef, [{Game, :start_link, [[], {"player1", "player2"}], []},...
From what I understand, the {"player1", "player2"}
args don’t override the default []
arg but append to it, so I am trying to spawn a child with an unexisting Game.start_link/2
function passing to it the args [], {"player1", "player2"}
.
The documentation is clear: “The child process will then be started by appending the given list to the existing function arguments in the child specification” except for the fact that I did not ask for a default []
arg when I wrote Supervisor.init([Game], strategy: :simple_one_for_one)
.
At this point I am thinking that it would be nice if:
-
Supervisor.init([Game, :hello], strategy: :simple_one_for_one)
would rely on Game.child_spec/1
passing it the default :hello
arg (this is the actual behavior)
- but also if
Supervisor.init([Game], strategy: :simple_one_for_one)
would rely on a new Game.child_spec/0
that defines a child_spec with no default arg.
Since this second feature does not exist (maybe it’s a bad idea anyway), the solution I came up with is to override the child_spec so that there is no default “arguments in the child specification” and then Game.start_link/1
will be called with {player1, player2}
.
defmodule Game do
use GenServer, start: {Game, :start_link, []}, restart: :transient
def start_link(args) do
GenServer.start_link(__MODULE__, args)
end
...
And indeed it works!
But in my tests:
start_supervised {Game, {player1, player2}}
Is now failing:
** (MatchError) no match of right hand side value: {:error, {{:EXIT, {:undef, [{Game, :start_link, [], []}
If I remove the child_spec :start
option in the Game module the tests will run, but I am back with the first error.
Finally, I have this solution:
- don’t specify a
:start
key as in use GenServer, start: {Game, :start_link, []}, restart: :transient
so that start_supervised
works
- but define it in a custom spec within the supervisor:
def init(:ok) do
game_spec = %{id: Game, restart: :transient, shutdown: 5000, start: {Game, :start_link, []}, type: :worker}
Supervisor.init([game_spec], strategy: :simple_one_for_one)
end
I wanted to share this because it was hard to find a solution, and hard remembering why it is needed (maybe I am wrong somewhere).
Adding to it the fact that:
- the Phoenix app generator does not use Elixir 1.5 style of supervisor children declaration
-
DynamicSupervisors are coming in master
-
start_supervised {Game, {player1, player2}}
does not require args to be in a list when Supervisor.start_child(__MODULE__, [{player1, player2}])
does
…my brains fry a little