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

1 Like

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
2 Likes

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.

1 Like

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.

1 Like

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”.

1 Like