I’m looking for a best way to autostart a worker process.
I have my main module autogenerated as a part of supervised app: MyApp.Application registered as MyApp.Supervisor and then I’m adding a DynamicSupervisor as it’s child:
defmodule MyApp.Application do
@moduledoc false
use Application
@impl true
def start(_type, _args) do
children = [
{
DynamicSupervisor,
strategy: :one_for_one,
name: MyApp.DynamicSupervisor
}
]
opts = [strategy: :one_for_one, name: MyApp.Supervisor]
Supervisor.start_link(children, opts)
end
end
I also have a box standard worker that requires a single string arg:
defmodule MyApp.Worker do
use GenServer
def start_link(symbol) do
GenServer.start_link(__MODULE__, symbol, name: :"#{__MODULE__}-#{symbol}")
end
def init(symbol) do
Logger.info("Starting worker #{symbol}")
{:ok, symbol}
end
end
Question is - let’s say that I have a table in database that lists all symbols that I need to autostart - I need to start many worker processes on start of the application. Currently I added another module/process that is a child of the MyApp.Application that on init -> handle_continue queries the database and starts workers:
def start_worker(symbol) do
DynamicSupervisor.start_child(
MyApp.DynamicSupervisor,
{MyApp.Worker, symbol}
)
end
Is this the only/best way to do it? How would you approach this problem?
Thank you for the reply, it looks nice but thinking about it now… how will restarting work in case of crash of the DynamicSupervisor? Obviously new DynamicSupervisor will be started but then I need my task to be run once again. I could use one_for_all strategy in MyApp.Application as long as it will be happy with Task exiting after a second or so.
Second thought - this load_workers function will be using Ecto to query and have a little bit of logic - I was thinking that the “best practice” is to avoid putting logic into supervisors etc?
The supervisor strategy here would be :rest_for_all. If you do not want to have all your app with this strategy, then you need one more layer of supervisors : App -> SymbolsSupervisorTop -> [SymbolsDynamicSupervisor, Task].
So you can set the :rest_for_one strategy on SymbolsSupervisorTop only.
Second thought - this load_workers function will be using Ecto to query and have a little bit of logic - I was thinking that the “best practice” is to avoid putting logic into supervisors etc?
The logic will not be executed by the supervisor process but by the Task, so it is all that matters. Also this code will be strongly related to starting/restarting processes so why not put it in the dynamic supervisor module.
I think this is probably the best option - I wasn’t aware that DynamicSupervisor is also a behaviour! I will add starting children logic there and querying to the Task probably - I need to see how this will look like.