I have an app like
stackdelivery
├── lib
└── stackdelivery
└── application.ex
└── stackgen
└── stackgen.ex
└── supervisor.ex
stackdelivery.ex
mix.exs
application.ex is
defmodule Stackdelivery.Application do
# See http://elixir-lang.org/docs/stable/elixir/Application.html
# for more information on OTP Applications
@moduledoc false
use Application
use Supervisor
def start(_type, _args) do
import Supervisor.Spec, warn: true
# Define workers and child supervisors to be supervised
children = [
supervisor(Stackdelivery.StackGen.Supervisor, [])
]
# See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
# for other strategies and supported options
opts = [strategy: :one_for_one, name: Stackdelivery.StackAppSupervisor]
Supervisor.start_link(children, opts)
end
end
supervisor.ex is
defmodule Stackdelivery.StackGen.Supervisor do
@moduledoc """
Supervisor for StackGen module
"""
use Supervisor
alias Stackdelivery.Stack
def start_link do
Supervisor.start_link(__MODULE__, [], name: __MODULE__)
end
def init(_) do
IO.puts "starting StackGen.Supervisor supervisor.."
children = [
worker(Stack, [])
]
opts = [strategy: :simple_one_for_one, name: Stackdelivery.StackGenSupervisor]
supervise(children, opts)
end
def shoot, do: Supervisor.start_child(__MODULE__, [])
end
stackgen is a simple Stacn Genserver. for brevity:
defmodule Stackdelivery.Stack do
use GenServer
def init(_) do
IO.puts "Starting a New GenServer Stack"
{:ok, []}
end
....
I can’t start multiple child processes using Stackdelivery.StackGen.Supervisor.shoot
The way I am thinking is how supervision tree should be is:
Application (Main Supervisor)
└── StackGen.Supervisor (Module Supervisor)
└── Stackgen (GenServer)
Now when Application.start_link
happens, how can we invoke child proceses in StackGen.Supervisor for Stackgen workers?
Supervisor.start_child/2
needs a spec! See Supervisor.Spec
Also, I think it has to have an id
if you’re starting multiple GenServers of the same module.
def shoot(unique_name) do
spec = worker(Stackgen, [], [id: unique_name, restart: :transient])
Supervisor.start_child(__MODULE__, spec)
end
4 Likes
isnt transient is default for simple_one_for_one?
Is this unique name unique everytime each pid starts?
You’re right. My code doesn’t use :simple_one_for_one
. The app I took the example code from uses a :one_for_one
.
I’ll play with your code and see if I can find out what’s going on.
should each time the id be unique?
In my app, I’m not using :simple_one_for_one
strategy. Each GenServer I start has a unique id - that’s why the example I gave you has a unique_id
argument.
can you give me an example with a code snippet?
in worker(module, args, options \\ [])
[id: module,
function: :start_link,
restart: :permanent,
shutdown: 5000,
modules: [module]]
id can be __MODULE__
right?
OK. I have a working example now. Sorry to have misled you with incorrect information. I hope this makes up for it
Some notes:
- I think having
use Supervisor
in application.ex ultimately caused the problem since they likely conflict with each other. I didn’t catch that at first.
- I forgot about this gotcha: if running from IEx, the module name is prefixed with
Elxir
! (i.e. Elixir.StackDelivery.StackGen.Supervisor
), so I’m not sure if that came into play or not
Nonetheless, I’ve tested this and it seems to be working fine.
application.ex
defmodule StackDelivery.Application do
@moduledoc false
use Application
def start(_type, _args) do
import Supervisor.Spec, warn: true
children = [
supervisor(StackDelivery.StackGen.Supervisor, [])
]
opts = [strategy: :one_for_one, name: StackDelivery.Supervisor]
Supervisor.start_link(children, opts)
end
end
stack_gen_supervisor.ex
defmodule StackDelivery.StackGen.Supervisor do
use Supervisor
require Logger
alias StackDelivery.StackGen
# this format is used at https://elixir-lang.org/getting-started/mix-otp/supervisor-and-application.html#simple-one-for-one-supervisors
@name StackDelivery.StackGen.Supervisor
def start_link do
Supervisor.start_link __MODULE__, [], name: @name
end
def shoot do
Supervisor.start_child(@name, [])
end
def init(_) do
Logger.info "starting StackDelivery.StackGenSupervisor"
children = [
worker(StackGen, [])
]
opts = [strategy: :simple_one_for_one, name: StackDelivery.StackGenSupervisor]
supervise(children, opts)
end
end
stack_gen.ex
defmodule StackDelivery.StackGen do
use GenServer
require Logger
def start_link(opts \\ []) do
GenServer.start_link __MODULE__, opts
end
def init(_) do
Logger.info "starting a new #{__MODULE__}"
{:ok, []}
end
end
mix.exs
defmodule StackDelivery.Mixfile do
use Mix.Project
def project do
[app: :stack_delivery,
version: "0.1.0",
elixir: "~> 1.4",
build_embedded: Mix.env == :prod,
start_permanent: Mix.env == :prod,
deps: deps()]
end
def application do
[extra_applications: [:logger],
mod: {StackDelivery.Application, []}]
end
defp deps do
[]
end
end
idk, its not working for me
iex(1)> Stackdelivery.StackGen.Supervisor.start_link
starting..module is: Stackdelivery.StackGen.Supervisor
{:ok, #PID<0.116.0>}
iex(2)> Stackdelivery.StackGen.Supervisor.start_link
works when
defmodule Stackdelivery.StackGen.Supervisor do
@moduledoc """
Supervisor for StackGen module
"""
use Supervisor
alias Stackdelivery.Stack
@name Stackdelivery.StackGen.Supervisor
def start_link do
Supervisor.start_link(__MODULE__, [])
end
wont work when Supervisor.start_link(__MODULE__, [], name: @name)