Fl4m3Ph03n1x

Fl4m3Ph03n1x

Background

I am reading the “Functional Web Development with Elixir, OTP and Phoenix” book, and I finished a Supervisor that supervises Games. Games can start and end so this is in reality a DynamicSupervisor, but when the book was written the strategy :simple_one_for_one was still not deprecated and so that is what they used.

My objective is to replace the deprecated Supervisor with a Dynamic one and get rid of the deprecation warnings.

Code

Following is the (deprecated) Supervisor the book gives (I added specs):

defmodule IslandsEngine.GameSupervisor do
  use Supervisor

  alias IslandsEngine.Game

  @spec start_link(any) :: Supervisor.on_start
  def start_link(_args), do:
    Supervisor.start_link(__MODULE__, :ok, name: __MODULE__)

  @spec start_game(String.t) :: Supervisor.on_start_child
  def start_game(name), do:
    Supervisor.start_child(__MODULE__, [name])

  @spec stop_game(String.t) :: :ok | {:error, :not_found | :simple_one_for_one}
  def stop_game(name) do
    :ets.delete(:game_state, name)
    Supervisor.terminate_child(__MODULE__, pid_from_name(name))
  end

  @impl Supervisor
  @spec init(:ok) :: {:ok, tuple}
  def init(:ok), do:
    Supervisor.init([Game], strategy: :simple_one_for_one)

  defp pid_from_name(name) do
    name
    |> Game.via_tuple()
    |> GenServer.whereis()
  end
end

This code works, but makes Dyalizer go crazy. Furthermore, the strategy used here is also deprecated.

This is my attempt at upgrading this code:

defmodule IslandsEngine.GameSupervisor do
  use DynamicSupervisor

  alias IslandsEngine.Game

  @spec start_link(any) :: DynamicSupervisor.on_start
  def start_link(_args), do:
    DynamicSupervisor.start_link(__MODULE__, :ok, name: __MODULE__)

  @spec start_game(String.t) :: DynamicSupervisor.on_start_child
  def start_game(name), do:
    DynamicSupervisor.start_child(__MODULE__, {Game, [name]})

  @spec stop_game(String.t) :: :ok | {:error, :not_found}
  def stop_game(name) do
    :ets.delete(:game_state, name)
    DynamicSupervisor.terminate_child(__MODULE__, pid_from_name(name))
  end

  @impl DynamicSupervisor
  @spec init(:ok) :: {:ok, DynamicSupervisor.sup_flags}
  def init(:ok), do:
    DynamicSupervisor.init(strategy: :one_for_one)

  defp pid_from_name(name) do
    name
    |> Game.via_tuple()
    |> GenServer.whereis()
  end

end

Problem

However, when I run my version of the DynamicSupervisor I get the following error:

IslandsEngine.GameSupervisor.start_game("Fred") 
{:error,
 {:undef,
  [
    {IslandsEngine.Game, :start_link, [], []},
    {DynamicSupervisor, :start_child, 3,
     [file: 'lib/dynamic_supervisor.ex', line: 690]},
    {DynamicSupervisor, :handle_start_child, 2,
     [file: 'lib/dynamic_supervisor.ex', line: 676]},
    {:gen_server, :try_handle_call, 4, [file: 'gen_server.erl', line: 661]},
    {:gen_server, :handle_msg, 6, [file: 'gen_server.erl', line: 690]},
    {:proc_lib, :init_p_do_apply, 3, [file: 'proc_lib.erl', line: 249]}
  ]}}

The deprecated version works just fine.

Questions

What am I doing wrong? Why are the two Supervisors not equivalent?

Showing Posts 1 to 8

josevalim

josevalim

Creator of Elixir

From the stacktrace you can see that Game.start_link/0 is being called. But why start_link with arity 0? The default should be to call start_link(game_name), which is what you expect, UNLESS there is a child_spec/1 function in the Game module (or an argument on use GenServer) that is instructing it to pass no arguments to Game.start_link.

Once you remove the custom child_spec, everything should work.

Fl4m3Ph03n1x

Fl4m3Ph03n1x OP

Indeed, according to the book this is the case. The thing I don’t understand is why the deprecated version works without changing the Game.child_spec but the DynamicSupervisor version doesn’t.

Are these 2 versions incompatible in a way I am missing here?

Chrichton

Chrichton

Unfortunately, the solution did not work for me. I removed the child_spec in use GenServer,
tried: IslandsEngine.GameSupervisor.start_game(“Fred”)
and got:

{:function_clause,
[
{IslandsEngine.Game, :start_link, [[“Fred”]],
[file: ‘lib/islands_engine/game.ex’, line: 27]},
{DynamicSupervisor, :start_child, 3,
[file: ‘lib/dynamic_supervisor.ex’, line: 692]},
{DynamicSupervisor, :handle_start_child, 2,
[file: ‘lib/dynamic_supervisor.ex’, line: 678]},
{:gen_server, :try_handle_call, 4, [file: ‘gen_server.erl’, line: 706]},
{:gen_server, :handle_msg, 6, [file: ‘gen_server.erl’, line: 735]},
{:proc_lib, :init_p_do_apply, 3, [file: ‘proc_lib.erl’, line: 226]}
]}}

I changed:
def start_game(name), do: DynamicSupervisor.start_child(MODULE, {Game, [name]})
to:
def start_game(name), do: DynamicSupervisor.start_child(MODULE, {Game, name})

and now everything works.

sotoseattle

sotoseattle

I had the same issue. The thing that did the trick for me was how you define the spec:

spec = %{id: Game, start: {Game, :start_link, [name]}}

So the whole GameSupervisor looks like this:

defmodule IslandsEngine.GameSupervisor do
  use DynamicSupervisor

  alias IslandsEngine.Game

  def start_link(_options) do
    IO.puts("starting the game supervisor...")
    DynamicSupervisor.start_link(__MODULE__, :ok, name: __MODULE__)
  end

  def start_game(name) do
    spec = %{id: Game, start: {Game, :start_link, [name]}}
    DynamicSupervisor.start_child(__MODULE__, spec)
  end

  def stop_game(name) do
    DynamicSupervisor.terminate_child(__MODULE__, pid_from_name(name))
  end

  defp pid_from_name(name) do
    name
    |> Game.via_tuple()
    |> GenServer.whereis()
  end

  def init(_args) do
    DynamicSupervisor.init(
      strategy: :one_for_one)
  end
end
rm-rf-etc

rm-rf-etc

This code doesn’t make sense to me, start_game doesn’t call pid_from_name, but stop_game does. It doesn’t look like the via_tuple is associated with the process at startup.

Fl4m3Ph03n1x

Fl4m3Ph03n1x OP

Reading the book will help understand it. I recommend you have a look at the list of discounts the forum offers:

There might be other threads, I recommend you search for a few.

kigila

kigila

Hello, did you manage to sold the issue ?
I solved the start_game method, but the stop_game method is broken. pid_from_name method return nil always. Thats suckes considering I have been religiously following the book. I believe it is some problem with Registry but I have no clue what is wrong. Man I wish I had a ginie. ChatGPT says it cannot help.

kigila

kigila

:smiling_face_with_tear: :smiling_face_with_tear: :smiling_face_with_tear: → Its funny and sad that after 5 hours I have the solution. I hope nobody else reading this book will face the same hardship I had to go throw. man, thats crazy. Here is the solution:

In the application module include the GameSupervisor to the supervision tree like this:

…
{DynamicSupervisor, name: IslandEngine.GameSupervisor}
…

In the game.ex module your GenServer.start_link should be called like this:

def start_game(name) when is_binary(name), do: GenServer.start_link(MODULE, [name], name: via_tuple(name))

you can see that I wrap mine in a public method I called start_game( it beautiful this way)

And finally in the GameSupervisor module, here is how I defined the start_game module:

def start_game(name) do
   child_spec = %{id: Game, start: {Game, :start_game, [name]}}
   DynamicSupervisor.start_child(__MODULE__, child_spec)
end

Maybe I have a ginie and dont know it. Hope that helps you. No soffer no more.

— All posts loaded —

Where Next? Top

Trending in Questions Top

katta
I having some trouble figuring out if I have set myself too strict of standards for my production server. Currently I can handle 75% of r...
New
achenet
Hello, I’m trying to build a basic Phoenix web-app, and I’d like to use Tailwind. However, when I launch mix phx.server, I get an error...
New
bradley
I really like the adapter patterns that ecto, nebulex, waffle, etc. use and would love find something similar for a key management servic...
New
unaware8150
Hello folks! So at work, we are seeing some situations where we have to define some “fixed” strings that are used across the codebase in...
New
Cxx-mlr
I’m working on a small exercise involving update_in/3, and I came up with this solution: data = %{ name: "Periodic Table", category:...
New
Alvinkariuki
How Can I Optimise Compile Time Dependencies I have been building an elixir application for about 2 years now. Many modules and files ha...
New
dillonoconnor
Is there any way to avoid the Hologram compiler running when using iex? It seems like the front-end code could potentially be disregarded...
New

Other Trending Topics Top

GenericJam
Edit: 2026 May 15 - This post is archived. Mob is alive!! Main docs: mob v0.7.11 — Documentation A bit of explanation for the slightly c...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
budgie
A little off-topic, but I feel like people here have a good head on their shoulders. I used to be quite good at making software. Was luc...
New
KristerV
Hey. Is there anyone here who creates agents in their apps? Not talking about using agents, but creating them. I’m finding it pretty diff...
New
mudasobwa
I fully migrated to my own harness from Anthropic/Gemini and I think it’s time to share it. Welcome DSH, the DeepSeek Harness, fully writ...
New
mcass19
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews