Hi!
I’m writing an Elixir application with a DynamicSupervisor
. Once the application has started, I’d like to add some children to this dynamic supervisor, but I receive an error when calling start_child()
:
** (Mix) Could not start application backend: exited in: Backend.Application.start(:normal, [])
** (EXIT) exited in: GenServer.call(Backend.Crawler.CrawlerSupervisor, {:start_child, {{Backend.Crawler.Server, :start_link, [[domain: "mastodon.social"]]}, :permanent, 5000, :worker, [Backend.Crawler.Server]}}, :infinity)
** (EXIT) no process: the process is not alive or there's no process currently associated with the given name, possibly because its application isn't started
The same error occurs when I run through the steps one at a time in iex.
I’m confused by this error because I know that the dynamic supervisor CrawlerSupervisor
is alive (I can see it in the supervision tree). The GenServer
I’m trying to start, Backend.Crawler.Server
, does exist – but why would it need to be alive before I call start_child
? Can someone clear this up for me?
Here’s the full code, if it’s helpful.
The application
defmodule Backend.Application do
@moduledoc false
use Application
alias Backend.Crawler.CrawlerSupervisor
alias Backend.{Instance, Repo}
import Ecto.Query
def start(_type, _args) do
children = [
Backend.Repo,
BackendWeb.Endpoint,
CrawlerSupervisor
]
opts = [strategy: :one_for_one, name: Backend.Supervisor]
case Supervisor.start_link(children, opts) do
ok = {:ok, _pid} ->
start_instance_servers()
ok
other ->
other
end
end
defp start_instance_servers() do
domains = ["mastodon.social"]
domains
|> Enum.each(fn domain -> CrawlerSupervisor.start_child(domain) end)
end
end
The dynamic supervisor
defmodule Backend.Crawler.CrawlerSupervisor do
use DynamicSupervisor
alias Backend.Crawler.Server
def start_link(_init_arg) do
DynamicSupervisor.start_link(__MODULE__, name: __MODULE__)
end
@impl true
def init(_opts) do
DynamicSupervisor.init(strategy: :one_for_one)
end
def start_child(domain) do
spec = {Server, domain: domain}
DynamicSupervisor.start_child(__MODULE__, spec)
end
end
The GenServer child process
defmodule Backend.Crawler.Server do
use GenServer
import Backend.Crawler.Util
alias Backend.Crawler.Crawler
# Client
def start_link(domain) do
GenServer.start_link(__MODULE__, domain, name: domain)
end
# Server
@impl true
def init(domain) do
schedule_crawl()
{:ok, domain}
end
@impl true
def handle_cast(:crawl, state) do
Crawler.run(state)
schedule_crawl()
{:noreply, state}
end
defp schedule_crawl() do
interval = get_config(:crawl_interval_mins) * 60_000
Process.send_after(self(), :crawl, interval)
end
end