How can I make many equal child for supervisor?

def start() do
    login = Application.get_env(:server, :rabbit_login)
    password = Application.get_env(:server, :rabbit_passw)
    exchange = Application.get_env(:server, :rabbit_ex)
    {:ok, conn} = AMQP.Connection.open("amqp://#{login}:#{password}@localhost")
    {:ok, chan} = AMQP.Channel.open(conn)
    :ok = AMQP.Exchange.declare(chan, exchange)

    children = [
      supervisor(SimpleQueue, [{chan, exchange}], id: :test_1),
      supervisor(SimpleQueue, [{chan, exchange}], id: :test_2)
    ]

    opts = [strategy: :one_for_all, name: SimpleQueue.Supervisor]
    Supervisor.start_link(children, opts)
  end

if I ran code I take this error

** (EXIT from #PID<0.295.0>) shell process exited with reason: shutdown: failed to start child: :test_2
    ** (EXIT) already started: #PID<0.313.0>

Try this?

-      supervisor(SimpleQueue, [{chan, exchange}], id: :test_1),
+      supervisor(SimpleQueue, [{chan, exchange}], id: :test_1, name: :test_1),
-      supervisor(SimpleQueue, [{chan, exchange}], id: :test_2)
+      supervisor(SimpleQueue, [{chan, exchange}], id: :test_2, name: :test_2)

I have just. Doesnt work.

Can you share SimpleQueue.start_link implementation?

1 Like
defmodule SimpleQueue do
  require Logger
  require Jason

  use GenServer
  use AMQP

  def start_link(state \\ []) do
    GenServer.start_link(__MODULE__, state, name: __MODULE__)
  end

  def init(chan) do
    {:ok, chan}
  end

  def handle_info(_, {chan, exchange}) do
    {:noreply, {chan, exchange}}
  end

  def handle_call({_ , _}, {chan, exchange}) do
    {:reply, {chan, exchange}}
  end

  def handle_cast({ :push, %{ "service" => s, "method" => m, "args" => a, :uuid => u } }, {chan, exchange}) do
    case Jason.encode(%{ "service" => s, "method" => m, "args" => a, :uuid => u }) do
      {:ok, json} ->
        Logger.info("was sended to rabbit: #{json}")
        IO.puts(json)
        #AMQP.Basic.publish(chan, exchange, "", json)
      {:error, _} ->
        Logger.warn("tuple was not encoded")
    end
    {:noreply, {chan, exchange}}
  end

  def handle_cast({_ , _}, {chan, exchange}) do
    {:noreply, {chan, exchange}}
  end

end

I think you should remove the name from GenServer.start_link(), because you’re now starting two GenServers with the same name.

I might be wrong, though, I’m relatively new to the language :slight_smile:

3 Likes

You need to have both unique id and name. The id is to tell different children apart within the supervisor, and the name is the locally registered name. For your case you’d have to provide a custom child_spec/1 function.
https://hexdocs.pm/elixir/Supervisor.html#module-child_spec-1

2 Likes

if I remove the name from GenServer.start_link() the code does run without errors, but the function “handle_cast({ :push, …}, {chan, exchange})” does not work, messages don’t arrive.

How exactly are you sending messages to the process? I assume via the name, which used to be SimpleQueue?

You now removed the name from the process, as such you need to send the messages explicitly to the PID. But I assume since you spun up the processes under a supervision tree that you don’t have immediate access to said PID.

This begs the question: Why did you want to spawn two instances of SimpleQueue in the first place?

As you noticed names need to be unique, and if you want to send messages to the SimpleQueue via the name then you can (by definition) only send messages to a single process.

It would be helpful if you took a few minutes to write down what exactly you want to accomplish.

1 Like

I have both unique name for both children. But the code does not run. I take error.

defmodule SimpleQueue.Supervisor do
  use AMQP

  def init(_) do

    login = Application.get_env(:server, :rabbit_login)
    password = Application.get_env(:server, :rabbit_passw)
    exchange = Application.get_env(:server, :rabbit_ex)
    {:ok, conn} = AMQP.Connection.open("amqp://#{login}:#{password}@localhost")
    {:ok, chan} = AMQP.Channel.open(conn)
    :ok = AMQP.Exchange.declare(chan, exchange)

    children = [
      %{
        name: :test,
        id: :some,
        start: {SimpleQueue, :start_link, [{chan, exchange}]}
      },
      %{
        name: :test_two,
        id: :some_two,
        start: {SimpleQueue, :start_link, [{chan, exchange}]}
      }
    ]

    opts = [strategy: :one_for_one, name: SimpleQueue.Supervisor]
    Supervisor.start_link(children, opts)

  end
end
** (EXIT from #PID<0.295.0>) shell process exited with reason: shutdown: failed to start child: :some_two
    ** (EXIT) already started: #PID<0.313.0>

I want to run two equal “SimpleQueue” using “GenServer” children for supervisor.

defmodule SimpleQueue.Supervisor do
  use AMQP

  def init(_) do

    login = Application.get_env(:server, :rabbit_login)
    password = Application.get_env(:server, :rabbit_passw)
    exchange = Application.get_env(:server, :rabbit_ex)
    {:ok, conn} = AMQP.Connection.open("amqp://#{login}:#{password}@localhost")
    {:ok, chan} = AMQP.Channel.open(conn)
    :ok = AMQP.Exchange.declare(chan, exchange)

    children = [
      %{
        name: :test,
        id: :some,
        start: {SimpleQueue, :start_link, [{chan, exchange}]}
      },
      %{
        name: :test_two,
        id: :some_two,
        start: {SimpleQueue, :start_link, [{chan, exchange}]}
      }
    ]

    opts = [strategy: :one_for_one, name: SimpleQueue.Supervisor]
    Supervisor.start_link(children, opts)

  end
end

I understood that. The question is why do you want to do that?

1 Like

I think that one will not do job well.

Just remember that 2 processes doesn’t mean that job will be done 2 times faster.

But, how can i run two equal children? Is it possibly ?

Yes, as long as these have unique names (or no name at all). This is exactly how connection pools are working (for example in Ecto).

1 Like

There is no name key in the child_spec, only id. name shall be passed down to start_link as a parameter to GenServer.start_link/3