Starting and stopping a supervisor child on demand

I want to start or stop a child of my application top supervisor (the child is a supervisor itself but I do not think it matters.)

This is what I came up with, but I find it unclear.

def start_endpoint() do
  case Supervisor.start_child(MyApp.Supervisor, MyApp.MyChild) do
    {:ok, pid} when is_pid(pid) ->
      {:ok, pid}

    {:error, {:already_started, pid}} ->
      {:ok, pid}

    {:error, :already_present} ->
      :ok = Supervisor.delete_child(MyApp.Supervisor, MyApp.MyChild)
      start_endpoint()
  end
end

def stop_endpoint() do
  :ok = Supervisor.terminate_child(MyApp.Supervisor, MyApp.MyChild)
end

Any better way to do that ? My start/stop functions actually act like ensure_started and ensure_stopped types of functions – this is what I want.

Thank you

Edit: It is actually not “unclear” but it seems that I missed a way to tell the supervisor “you have that child spec, and no process started for it, start it now, thanks”.

Maybe you nead DynamicSupervisor.

Thank you but no, this is a single, identified child. It’s basically a phoenix endpoint.

I had a similar use case where I needed to start supervisor process per domain record during runtime. Your best bet is using DynamicSupervisors to achieve what you want. Can you elaborate on what is the use case?

Hi, thank you for your answer.

I do not think that DynamicSupervisor would be a good fit here. The child that I want to stop and start is a single supervisor, I do not want to start many copies of that process. I just want it to be either up or down.

My use case is that the app can be used as a CLI tool to perform some actions (send automated messages via mail or slack), but it can also be started permanently and will send those messages at regular intervals.
When started permanently, it also exposes a Phoenix endpoint to receive data updates via HTTP.

Now I need to debug those incoming HTTP calls and I want to turn down the phoenix endpoint and replace it by a local python web server listening on the same port that will log all received HTTP requests in the terminal just to see the data I am sent. So I just call MyApp.stop_endpoint(), start my python server, look at the requests I am sent (through ngrok), and when I know how to handle the data I can turn down python and start phoenix again.

Edit: I am lazy :slight_smile: I do not want to restart the whole app with or without phoenix because at startup it fetches some data and that takes time.

I see, well, all I can add is a few things that might help the mental model:

  • DynamicSupervisor doesn’t immediately mean multiple processes, their function is to start one or more children during runtime.

  • Supervisor dynamic or not can supervise other supervisors.

In my case, the architecture looks something like

Application Supervisor -> DynamicSupervisor Parent -> 1…n DynamicSupervisor Children -> 1…n Workers