Fl4m3Ph03n1x

Fl4m3Ph03n1x

Converting deprecated :simple_one_for_one to DynamicSupervisor

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?

Marked As Solved

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.

Also Liked

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
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.

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.

Where Next?

Trending in Questions Top

lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New
senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
New
hariharasudhan94
Lets say I have map like this fetching from my database %{"_id" => #BSON.ObjectId<58eb1a7a9ad169198c3dXXXX>, "email" => ...
New
tj0
I’ve been following the steps here for the upgrade from 1.6 to 1.7 and it has gone relatively smoothly all the way till the phoenix_view ...
New
cgraham
Hi! What is currently the best library/method for parsing text and tabular data out of PDF files in Elixir or Erlang?
New
stefanchrobot
Hi, I need a way to handle data migrations in my application. I found an article by @wojtekmach about manual migrations: Automatic and ma...
New
stjefim
Hello! Suppose you are building workflow (order / task / payment) processing system with the following requirements: Each workflow con...
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
JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
kip
Localize is the next generation localisation library for Elixir. Think of it as ex_cldr version 3.0. The first version will be released ...
New
webofbits
Squid Mesh is an open source workflow automation runtime for Elixir applications. It is aimed at Phoenix and OTP apps that want to defin...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
kip
In 2021 I started a new library called Tempo with the objective of modelling time as a set of intervals - not as instants. In 2022 I gave...
New

We're in Beta

About us Mission Statement