I want to create a child for every time a game is created by my users:
{:ok, room} = Sem.RoomMessaging.get_room_code()
IO.inspect(Supervisor.child_spec({Sem.Room, %{room: room}}, id: room, shutdown: 10_000))
{:ok, pid} =
DynamicSupervisor.start_child(
Sem.RoomManager,
Supervisor.child_spec({Sem.Room, %{room: room}}, id: room, shutdown: 10_000)
)
when I console log the output of child_spec I get a child spec with a unique id
%{
id: :"767476717986",
shutdown: 10000,
start: {Sem.Room, :start_link, [%{room: :"767476717986"}]}
}
however when I call this a second time I get:
** (MatchError) no match of right hand side value: {:error, {:already_started, #PID<0.372.0>}}
Any idea on how to fix this?
Hello and welcome,
Don’t use the room as id for users, use something unique…
2 Likes
get_room_code()/1 gets a completely unique and unused room code
I log them every time I create a new child:
iex(1)> %{
id: :"718687887279",
shutdown: 10000,
start: {Sem.Room, :start_link, [%{room: :"718687887279"}]}
}
iex(1)> %{
id: :"767476717986",
shutdown: 10000,
start: {Sem.Room, :start_link, [%{room: :"767476717986"}]}
}
iex(1)>
11:31:02.192 [error] Ranch protocol #PID<0.375.0> of listener None.HTTP (connection #PID<0.374.0>, stream id 1) terminated
an exception was raised:
** (MatchError) no match of right hand side value: {:error, {:already_started, #PID<0.372.0>}}
(sem 0.1.0) lib/client.ex:18: Sem.SocketHandler.init/2
(cowboy 2.9.0) c:/Users/ebarr/Desktop/coding/stacked/kertamen/sem/deps/cowboy/src/cowboy_handler.erl:37: :cowboy_handler.execute/2
(cowboy 2.9.0) c:/Users/ebarr/Desktop/coding/stacked/kertamen/sem/deps/cowboy/src/cowboy_stream_h.erl:306: :cowboy_stream_h.execute/3
(cowboy 2.9.0) c:/Users/ebarr/Desktop/coding/stacked/kertamen/sem/deps/cowboy/src/cowboy_stream_h.erl:295: :cowboy_stream_h.request_process/3
(stdlib 3.15) proc_lib.erl:226: :proc_lib.init_p_do_apply/3
here is get_room_code()/1
@spec get_room_code :: {:ok, atom()}
def get_room_code() do
rand = randomcode(6)
if (Registry.lookup(Sem.RoomRegistry, rand) != []) do
{:ok, get_room_code()}
else
{:ok, String.to_atom(rand)}
end
end
How do You name the worker?
It should be the name param in the start_link of the worker.
1 Like
The problem was I was using the module as the name of the Genserver (the worker) instance
@spec start_link(props()) :: :ignore | {:error, any} | {:ok, pid}
def start_link(props) do
GenServer.start_link(__MODULE__, props, name: __MODULE__)
end
I replaced it with the unique atom and it worked
It’s common to use a Registry with dynamic supervisor, and use via in the worker.
It could be local, or distributed.
Also, I prefer to use something like this.
case DynamicSupervisor.start_child(
Sem.RoomManager,
Supervisor.child_spec({Sem.Room, %{room: room}}, id: room, shutdown: 10_000)
) do
{:ok, pid} -> pid
{:error, {:already_started, pid}} -> pid
_ -> # something else...
end
1 Like
Also, the title should be
DynamicSupervisor Disallows Creating Two Children From The Same Module with Different IDs