Search code examples
elixirphoenix-frameworkerlang-otperlang-supervisorgen-server

Dynamic Supervisor Spec in Elixir


I have created a GameSupervisor supervision module that I use to dynamically create a child of instance GameServer (GenServer). I can see GameServer's start_link method is invoked when GameSupervisor.start function is called but it doesn't keep the pid alive. Process.alive?(pid) in iex always return false if the restart strategy is set to temporary. If I set the restart to transient or permanent it ends up calling GameServer.start_link again when I call GenServer.cast on that pid.

Is invoking start_child not automatically add the pid to the supervision tree and keep it alive?

GameSupervisor.ex

defmodule Prest.GameSupervisor do

  alias Prest.GameServer
  use Supervisor

  @name :game_sup

  def start_link() do
    IO.puts "start link"
    Supervisor.start_link(__MODULE__, [], [name: @name])
  end

  def start(uid) do
    IO.puts "start bucket"
    {:ok, child} = Supervisor.start_child(@name, [uid])
  end

  def init([]) do
    IO.puts "init sup"
    children = [
      worker(GameServer, [], restart: :transient)
    ]

    supervise(children, strategy: :simple_one_for_one)
  end

end

GameServer.ex

  defmodule Prest.GameServer do
  use GenServer

  # Client API

  def start_link(uid) do
    IO.puts "start game server"
    GenServer.start_link(__MODULE__, uid, [])
  end

  def post(pid, event_id) do
    :gen_server.cast(pid, {:event, event_id})
  end

  # Server API

  def init(uid) do
    {:ok, {uid, [], []}}
  end

  def handle_cast({:event, event_id}, state) do
    #state = [event_id|state]
    {:noreply, "ok", state}
  end
end

Thanks


Solution

  • According to the docs:

    • A :permanent process is always restarted, even if it terminates normally.
    • A :transient process is restarted only if it terminates abnormally.
    • A :temporary process is never restarted.

    More than likely, your GameServer process is crashing for one reason or another, and the supervisor is handling restarts the way it's configured to.

    To debug this, you'll want to check your logs (which may only be output to the terminal) to see the reason why the process is crashing. If you're able to get an {:ok, pid} for it, then it's probably not crashing on init, which means a handle_cast, handle_call, or handle_info clause is responsible for the crash.

    Without code, it's hard to offer more specific help.

    In this case, it looks like your handle_cast has a bad return value. It should return {:noreply, state}, but is returning {:noreply, "ok", state}. This is often caused by changing a handle_call to handle_cast, and forgetting to remove the reply value.