Prevent Dynamic Supervisor from restarting a process on normal exit

I have a dynamic supervisor that starts a gen server that should terminate itself after a specified time. However, the supervisor seems to restart the process, even when restart option is set to transient.

Here’s the relevant code (redacted so there may be errors, but you’ll get the idea):

Dynamic supervisor:

def init(:ok) do
    DynamicSupervisor.init(strategy: :one_for_one, restart: :transient)
  end

def start_genserver() do
    spec = %{
      start: {Module.Name, :start_link, []},
      restart: :transient
    }

    DynamicSupervisor.start_child(__MODULE__, spec)
  end

in the gen server:

@impl true
  def init(state) do
    Process.send_after(self(), :terminate, 3_000)
    {:ok, state}
  end

  @impl true
  def handle_info(:terminate, state) do
    {:stop, :normal, state}
  end

However the Gen Server process keeps coming back with a different PID. What am I missing here?

Try use GenServer, restart: :transient on the GenServer module

I think you may have redacted something important, because I can’t reproduce the behavior you’re seeing. This script can be run with just elixir foo.exs:

defmodule FooSupervisor do
  use DynamicSupervisor

  def start_link(init_arg) do
    DynamicSupervisor.start_link(__MODULE__, init_arg, name: __MODULE__)
  end

  def init(:ok) do
    DynamicSupervisor.init(strategy: :one_for_one, restart: :transient)
  end

  def start_genserver() do
    spec = %{
      id: "nope",
      start: {FooWorker, :start_link, []},
      restart: :transient
    }

    IO.puts("starting child")
    DynamicSupervisor.start_child(__MODULE__, spec)
  end
end

defmodule FooWorker do
  use GenServer

  def start_link() do
    GenServer.start_link(__MODULE__, [])
  end

  @impl true
  def init(state) do
    IO.puts("child init")
    Process.send_after(self(), :terminate, 3000)
    {:ok, state}
  end

  @impl true
  def handle_info(:terminate, state) do
    IO.puts("child stop")
    {:stop, :normal, state}
  end
end

FooSupervisor.start_link(:ok)

FooSupervisor.start_genserver()

Process.sleep(:infinity)

When I run this, I get:

starting child
child init
child stop

and then nothing else.

1 Like