Howto use/update a registry for a queue?

i was following a tutorial on using registry to store state located here: https://medium.com/@adammokan/registry-in-elixir-1-4-0-d6750fb5aeb but am having some problems with the docs maybe i just dont understand so here goes.

 defmodule City.Server do

  use GenServer

  def start_link(city) do
    name = via_tuple(city)
    GenServer.start_link(__MODULE__, [city], name: name)
  end

  defp via_tuple(city) do
      {:via, Registry, {:city_registry, city}}
    end

  def top_agent(city) do
        GenServer.call(via_tuple(city), :top_agent)
      end

  def agent(city) do
        GenServer.call(via_tuple(city), :agent)
      end

  def loadAgents(city) do

  agents = Site.AgentController.getAgents(city)

   :queue.from_list(agents)
  end


       ## Server callbacks

  def init(:ok) do
  state = loadAgents()
   {:ok, state }
  end

  def process_next_in_queue({q, nil}) do

       Logger.debug("ProcessNext - Current agent is nil, Processing Queue: #{inspect q}")

       case :queue.out(q) do
         {:empty, q}        -> {q, nil}
         {{:value, agent}, q} -> {q, Task.Supervisor.async(:city_registry, agent)}
       end
     end

and the in the site sup
supervisor(Registry, [:unique, :city_registry])

however i am missing something, the registry does start and the city loads with the agents, however i am having trouble with the fact i have multiple citys, id like to start each as a genserver can anyone help

One of the problems I see is how you init your genserver. See how it’s done in the article you link https://github.com/amokan/registry_sample/blob/master/lib/registry_sample/account.ex#L77

so that if you pass [city] in

def start_link(city) do
  name = via_tuple(city)
  GenServer.start_link(__MODULE__,
    [city], # <-- here
    name: name)
end

then you need to expect it in init/1. Try this

def init([city]) do
  state = load_agents(city)
  {:ok, state}
end

instead of this

def init(:ok) do
  state = loadAgents()
  {:ok, state }
end

i fixed that the resulting code is here and it does work i do have the agents listed in the state of a city genserver, i know how to load in iex but how do i load a genserver per city from list of [citys] in phoenix? i will have a later operation that will fetch the agent at the top of that citys queue in order to send a message via channel. simple ordered queue per city. im thinking i should use registry to hold the processes started with the genservers (provided i can start them) and somehow route to them via another genserver. is this something more suited for genstage? im having trouble with should i have a supervisor start the genserver have it fetch an initial city list and then have that server make the same sup start more genservers as it enums through the list. is this the correct steps i should take or am i way off?

defmodule City.Server do
require Logger
  use GenServer

  def start_link(city) do
    name = via_tuple(city)
    GenServer.start_link(__MODULE__, city, name: name)
  end

  defp via_tuple(city) do
      {:via, Registry, {:city_registry, city}}
    end

  def top_agent(city) do
        GenServer.call(via_tuple(city), :top_agent)
      end

  def agent(city) do
        GenServer.call(via_tuple(city), :agent)
      end

  def loadAgents(city) do

  agents = Site.ScorecardController.getAgentsbyCity(city)

   :queue.from_list(agents)
  end

   def read(pid) do
      GenServer.call(pid, {:read})
    end

 def add(pid, item) do
    GenServer.cast(pid, {:add, item})
  end

       ## Server callbacks

  def init(city) do
  state = loadAgents(city)
   {:ok, state }
  end
   def handle_call({:read}, from, state) do
      {:reply, state, state}
    end

Yes, you can start them in a supervisor. I don’t think there is a need for genstage.

btw, messages don’t have to be tuples, so you can just use :read and not {:read}.

can you explain how i would start them in a sup, im still kinda new to elixir, im confused where i would pull my list of cities to start the process and how/where i start the sup i cant find any examples of how to do this your help is appreciated, i think i see tho i can run the db wuery in the init method of the sup right? how do i impliment a router tho? do i have to use registry /

Suppose your citys come and go (that is, they are :transient), then your city supervisor might look like this

defmodule City.Supervisor do
  use Supervisor

  def start_link do
    Supervisor.start_link(__MODULE__, nil, name: __MODULE__)
  end

  def start_city(city_id) do
    Supervisor.start_child(__MODULE__, [city_id])
  end

  def stop_city(city_id) do
    case Registry.lookup(:city_registry, {:city_registry, city_id}) do
      [] -> :ok
      [{pid, _}] ->
        Process.exit(pid, :shutdown)
        :ok
    end
  end

  def init(_) do
    children = [worker(City, [], restart: :transient)]
    supervise(children, [strategy: :simple_one_for_one])
  end
end

and you can start them in your application

defmodule City.Application do
  use Application

  def start(_type, _args) do
    import Supervisor.Spec, warn: false

    children = [
      supervisor(Registry, [:unique, :city_registry]),
      supervisor(City.Supervisor, []),
      worker(Task, [&start_cities/0], restart: :transient)
    ]

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


  defp start_cities do
    citi_ids = # get the list of (initial) cities to start somehow
    for city_id <- city_ids, do: City.Supervisor.start_city(city_id)
  end
end

1 Like

thanks so much i understand now!! didnt know about transient

You can learn more about supervisors here https://hexdocs.pm/elixir/Supervisor.html and here https://hexdocs.pm/elixir/Supervisor.Spec.html

I’m glad we could help. As a minor point, punctuation really does matter. A large block of text with no paragraph breakups, punctuation, and word contractions in my view says that a person does not care enough about their question to format it congenially.

I am disabled with 15% usage of my hands and program 40h a week i miss punctuation n stuff cuz of distal interphalingeal disorder etc etc

Fair enough.