christhekeele

christhekeele

Understanding DynamicSupervisor & no initial children

I ran into a situation recently where it would be very useful to start up a DynamicSupervisor in an application’s sup tree, always with a specific list of child specs to boot on init—much the way normal Supervisors work, but with the option to dynamically start new children easily later.

I started writing a question on how to do so… Which turned into an experiment with handle_continue (since DynamicSupervisor is implemented as a GenServer)… Which turned into a half-written proposal to the core mailing list… Which turned into an incomplete proof-of-concept fork implementing support for this within Elixir… Before discovering that during the development of DynamicSupervisor, support for this was intentionally dropped because of technical difficulties expanding child specifications at boot time.

Problem is, I’m not sure I understand the original motivation or the difficulties in doing so. Based on my toy branch I had no difficultly using the existing validate_child logic to expand child specs given at init, so either the implementation of DynamicSupervisor has changed enough to overcome the obstacles present when it was first created, or (more likely) I’m missing something obvious that still prohibits it today.

(The implementation in my fork uses handle_continue when it doesn’t need to, and in fact shouldn’t, but that’s the circuitous route I took to get here. It’d be reworked in a proper PR.)

Does anyone have any insight into if and why we couldn’t pass DynamicSupervisor’s init procedure a list of child specs to boot at start? Perhaps you can explain the original rationale in a way I understand better? (Without spam @'ing them) do any of the original implementers mind chipping in? I’m reluctant to work more on a PR/open a feature proposal in the core mailing list without fully understanding why this was decided against originally.

Marked As Solved

josevalim

josevalim

Creator of Elixir

I would suggest to start a DynamicSupervisor with a Task under a rest_for_one supervisor. This way you can start children immediately after the supervisor boots. IIRC that was the main reason to keep the DynamicSupervisor API simpler, since this behaviour is not common and it can be easily replicated.

Also Liked

iautom8things

iautom8things

Just in case someone else (like me) happens upon this thread looking for guidance, but doesn’t quite grok what José is suggesting (also like me, initially):

I happened to find this great TIL repo by @slashdotdash that gives you a working example:

https://github.com/slashdotdash/til/blob/master/elixir/dynamic-supervisor-start-children.md

Thank you, everyone! :bowing_man:

Phillipp

Phillipp

I also have a DynamicSupervisor which needs to start some children on app startup.

For that I used the module based DynamicSupervisor so I can hook into the init function like that:

  def init(_arg) do
    Manager.subject_server_startup()
    DynamicSupervisor.init(strategy: :one_for_one)
  end

My Manager is in itself a GenServer, here are some snippets from my code:

  def subject_server_startup() do
    GenServer.cast(__MODULE__, :subjects_supervisor_startup)
  end

  def handle_cast(:subjects_supervisor_startup, state) do
    for subject <- config() do
      start_subject(subject)
    end

    {:noreply, state}
  end
edisonywh

edisonywh

I think it’s because of the restart strategy of the Task module, here’s the documentation:

Opposite to GenServer, Agent and Supervisor, a Task has a default :restart of :temporary . This means the task will not be restarted even if it crashes. If you desire the task to be restarted for non-successful exits, do:

You can simulate this by creating a Task module yourself,

defmodule MyTask do
  require Logger
  # use Task, restart: :transient
  use Task

  def start_link(arg) do
    Task.start_link(__MODULE__, :run, [arg])
  end

  def run(arg) do
    Logger.info("Hello")
  end
end

Then put in in your supervision tree like normal {MyTask, []}. Kill the first process and you’ll see that MyTask doesn’t restart, then now try with the restart: :transient flag, and you’ll see it works

Last Post!

brentjanderson

brentjanderson

This thread helped me recently, and I wanted to add in that a Task will run asynchronously, whereas you can do the same thing with an Agent.

defmodule MyApp.SomeSupervisor do
  use Supervisor

  require Logger

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

  def init(_init_arg) do
    children = [
      {DynamicSupervisor, name: __MODULE__.DynamicSupervisor, strategy: :one_for_one},
      {Agent, # If you want this to run asynchronously, change this to Task
       fn ->
         Logger.info("Task started synchronously")
         
         {:ok, _pid} = DynamicSupervisor.start_child(__MODULE__.DynamicSupervisor, child_spec)

         %{} # Returning an empty state here
    ]

    Supervisor.init(children, strategy: :rest_for_one)
  end
end

If you want to block while adding the initial children to the DynamicSupervisor, this is one way to do it.

Where Next?

Popular in Questions Top

rms.mrcs
Hi, I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list. ...
New
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod – where is this set? Thanks.
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers’ Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New
Lily
In templates/appointment/index.html.eex: &lt;%= for appointment &lt;- @appointments do %&gt; &lt;tr&gt; &lt;td&gt;&lt;%= appoi...
New
WestKeys
Currently suffering from paralysis by [HTTP client] analysis. This is rather unusual in Elixirland as there tends to be consensus on the ...
New
Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New
jason.o
In the code below, if the create action is not set to accept “extra_key” as an input, it errors out with a message shown above. Is there ...
New

Other popular topics Top

Qqwy
Update: How to use the Blogs &amp; Podcasts section You can post links to your blog posts or podcasts either in one of the Official Blog...
3271 130286 1222
New
vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
grych
Hi folks, Few months ago I have announced the proof-of-concept of the library to manipulate the browsers DOM objects directly from Elixi...
639 54006 488
New
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod – where is this set? Thanks.
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
New

We're in Beta

About us Mission Statement