GenStage and Supervisor

When my phoenix application gets starts, I am just starting a process, for which I am have added a supervisor such as

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

  def init(:ok) do
    Task.start_link(&initiate_workers/0)
    extractor_children = [worker(Extractor, [], restart: :permanent)]
    supervise(extractor_children, strategy: :simple_one_for_one, max_restarts: 1_000_000)
    cloud_extractor_childern = [worker(CloudExtractor, [], restart: :permanent)]
    supervise(cloud_extractor_childern, strategy: :simple_one_for_one, max_restarts: 1_000_000)
    timelapse_children = [worker(TimelaspeCreator, [], restart: :permanent)]
    supervise(timelapse_children, strategy: :simple_one_for_one, max_restarts: 1_000_000)
  end

I am not starting any child_spec for those supervisors and in initiate_workers, All I am doing is

Process.whereis(:"snapshot_extractor_#{extractor.id}")
|> get_process_pid(EvercamMedia.SnapshotExtractor.Extractor, extractor.id)
|> GenStage.cast({:snapshot_extractor, get_config(extractor, :local)})

Sending a GenStage.cast, I want to upgrade it to DynamicSupervisor, but my question is do I really need a supervisor for this? Just to start this EvercamMedia.SnapshotExtractor.ExtractorSupervisor module on the application restart.

If you have to start a task during init then that’s a red flag. Have you researched GenServer.handle_continue?

how this is a red flag?

Because there’s an officially sanctioned OTP way of doing extra work during initialisation. Using Task.start_link inside init seems brittle while deferring to handle_continue (basically extra initialisation function hook) is how OTP wants you to do it so you don’t block your supervisor or tasks inadvertently.

Okay but I am using GenStage at the moment. As I am upgrading my supervisor to Dynamic one. to remove one to one strategy,
My issue is: Should I keep the supervisor?

or Is there anyother way to start a method on application start.?

I believe OTP primitives are the right tool for starting something on app start. So keep them IMO.

Okay, Thanks, Can you show any example? from where I can learn this?

Always try the forum search function first: When to use `handle_continue`?

This also seems like a good link: https://elixirschool.com/blog/til-genserver-handle-continue/

Hmm, I understand, But I will ask again: I am using GenStage not GenServer.

I want to just run a module method on application restart (Stopped and Then Started)?

Until now i didnt understand how handle_continue will work here but I will read about it

Thank you

Hey @ijunaidfarooq

To echo @dimitarvp, this supervisor init function has a lot of issues. You need to return one supervision tree, but you call supervise 3 times which basically sets up 3 children sets but only actually returns the last one. You never want to call start link directly within a supervisor init, it won’t be supevised properly.

If you want to dynamically start 3 different kinds of children I’d look at Dynamic supervisor, it lets you do that.

2 Likes

I am pretty much happy to just remove supervisors , my only problem is to just start a module, Abc.Bcd.start when application get start and stop, otherwise I dont really need supervisor,

Also The GenStage part will work independently,

Applications, at least OTP applications, assume that they are starting and monitoring a supervisor at the top-level so you should start a supervisor here. As @benwilson512 pointed out calling supervise does not start the children it just returns a structure which should be returned for the init function which tells the supervisor what to do. In this case all the children should be in one list which is passed as an argument to supervise.

If you want to dynamically add or remove children to/from the supervisor then you must tell the supervisor to do that and for that you typically create add and delete functions which do that.

1 Like