Process Registry doesn't automatically registry with {:via, module, term}

I’m trying to create a simple and custom process registry that stores the pids from a started process. From my understanding, the process should be automatically registered when i use the {:via, module, term} command but that’s not happening. Here’s my code.

Registry

defmodule Registry.Orders do
  use GenServer
  require Logger
  def start_link(opts) do
    # Register the order registry
    Logger.info("starting #{__MODULE__}")
    GenServer.start_link(__MODULE__, opts, name: __MODULE__)
  end

  def whereis_name(franchise_id) do
    Logger.info("#{__MODULE__} searching for franchise")
    GenServer.call(__MODULE__, {:whereis_name, franchise_id})
  end

  def register_name(franchise_id, pid) do
    Logger.info("#{__MODULE__} registering name")
    GenServer.cast(__MODULE__, {:register_name, franchise_id, pid})

  end


  def unregister_name(franchise_id) do
    GenServer.cast(__MODULE__, {:unregister_name, franchise_id})
  end

  def send(franchise_id, message) do
    case whereis_name(franchise_id) do
      :undefined ->
        {:not_found, {franchise_id, message}}
      pid ->
        Kernel.send(pid, message)
        pid

    end
  end

Worker

defmodule Workers.Orders do
  alias Transactions.Orders
  require Logger
  use GenServer
  

  def start_link(args) do
    id = Keyword.fetch!(args, :id)
    Logger.info("Starting order queue #{id} for #{__MODULE__}")
    GenServer.start_link(__MODULE__, name: via_tuple(id))
  end

  @impl true
  def init(_) do
    {:ok, Map.new}
  end

  defp via_tuple(id) do
    {:via, Haberdash.Registry.Orders, id}
  end

  @impl true
  def handle_call({:create_order, order}, _from, state) do
    temp_id = Ecto.UUID.generate()
    order = %{order | id: temp_id}
    state = Map.put(state, temp_id, order)
    {:reply, {:ok, temp_id}, state}
  end

  @impl true
  def handle_call({:update_order, temp_id,order}, _from, state) do
    state = Map.put(state, temp_id, order)
    {:reply, :ok, state}
  end


  @impl true
  def handle_cast({:delete_order, temp_id}, state) do
    state = Map.delete(state, temp_id)
    {:noreply, state}
  end

  def hande_call({:submit_order, temp_id, attrs}, _from, state) do

    with %Orders{} = order <- Map.get(state, temp_id, nil),
         {:ok, order} <- Transactions.create_orders(order, attrs),
         state <- Map.delete(state, temp_id) do
      {:reply, {:ok, order}, state}
    else
      nil ->
        {:error, :not_found}
      {:error, changeset} ->
        {:reply, {:error, changeset}, state}
    end


  end
end

Registered applications

 def start(_type, _args) do
    children = [
      
      {Phoenix.PubSub, name: Haberdash.PubSub},
      HaberdashWeb.Endpoint,
      {Haberdash.Registry.Orders, name: Haberdash.Registry.Orders},
    ]

    # See https://hexdocs.pm/elixir/Supervisor.html
    # for other strategies and supported options
    opts = [strategy: :one_for_one, name: Haberdash.Supervisor]
    Supervisor.start_link(children, opts)
  end
  1. I strongly recommend using Registry if possible, it will have higher performance.
  2. I’ll presume the implementation of the cast/call functions are correct, you didn’t include them.
  3. You should probably use call everywhere. Registering and Unregistering are expected to be able to fail.
  4. Check the documents for erlang global (which is the official reference registry implementation; the docs everywhere say “do it like global”). https://erlang.org/doc/man/global.html#register_name-2 The typespecs for the functions do not match what you’re outputting.
  5. Also look at the source code for Elixir’s Registry: https://github.com/elixir-lang/elixir/blob/v1.10.4/lib/elixir/lib/registry.ex#L245
  6. Note that the typespecs in :global and implemention of Registry output :yes or :no as their outputs.
3 Likes

Fixed the issue, I wasn’t passing the name: via_tuple(id) as the third variable

1 Like