Proper syntax to supervise two Agent instances?

Hi, I have a simple agent called Queue and I need to start two queues from the supervisor. However, can’t figure out the syntax - either I’m running into “already started” or I can’t access any identifier…

This is the piece of supervisor (with various futile attempts)

children = [
            Counter,
            Supervisor.child_spec({Queue,initial_value: 0, name: :q1}, id: :q1),
            Supervisor.child_spec({Queue,initial_value: 0, name: :q2}, id: :q2)
            # Supervisor.child_spec({Queue, :start_link}, id: :q1),
            # Supervisor.child_spec({Queue, :start_link}, id: :q2)
            # Supervisor.child_spec(Queue, id: :queue2)
            # {Queue, name: QueueOne},
            # {Queue, name: QueueTwo}
            # worker(Queue, [name: :q1]),
            # worker(Queue, [name: :q2])
        ]

and this is the Queue agent for completeness. Not sure the start_link params are defined properly either:

defmodule Queue do
    use Agent
  
    def start(initial_value \\ []) do
      Agent.start(fn -> initial_value end)
    end

    def start_link(initial_value \\ [], name \\ __MODULE__) do
      IO.puts "Starting queue..."
      Agent.start_link(fn -> initial_value end, name: name)
    end
  
    def all(process) do
      Agent.get(process,& &1)
    end
  
    def push(process, value) do
      Agent.update(process, &[value | &1])
    end

    def pop(process) do
        Agent.get_and_update(process, &List.pop_at(&1, -1))
    end

end

Ideally, I would have liked something like QueueOne and QueueTwo to be able to call QueueOne.all etc.

Thank you for your help

Should be this:

    def start_link(args) do
      init = args[:initial_value]
      name = args[:name] || __MODULE__
      IO.puts "Starting queue..."
      Agent.start_link(fn -> init end, name: name)
    end

Do you also always find solution only once you post the problem online? :slight_smile:

So this seems to work for me:

children = [
    Counter,
        worker(Queue, [[], :q1], restart: :permanent, id: :q1),
        worker(Queue, [[], :q2], restart: :permanent, id: :q2)
    ]

However, not sure why this works? Why I need to use worker and how to avoid the duplication of :q1 and :q2 ?

The worker macro is deprecated, please use my proposed solution to fix the callee. Your supervisors child spec looks correct from first glance.

Alternatively you could overwrite your Queue.child_spec/1, though I’d not suggest that without understanding the current concept of child spec generation.

Thank you, your solutions seems to work too! Had to adjust my call of Counter agent too, but managed to do it in a similar fashion.

Is there a way to assign and use QueueOne.all instead of using Queue.all(:q1) please?

There have been parameterized modules (or kind of those) once, but now there aren’t anymore.

Unless you have a singleton instance with a singleton name, you have to identify the Queue you want to send a message to when calling the external API.

You can either split your current single module into 2, or you can pass the name as one of the arguments.

Rubber duck debugging. :slight_smile:

Somebody told me some time ago that some people call it the “StackOverflow syndrome” – “once you post your problem and immediately you think of several possible solutions that weren’t obvious before you posted”.