Can't start a :gen_statem implementation under a registry

I have a :gen_statem implementation that I want to start via a registry like this:

defmodule Arca.PodImpl do
  @moduledoc false

  @behaviour :gen_statem

  def start_link(opts) do
    cell_ref = Keyword.get(opts, :cell_ref)

    case cell_ref do
      nil ->
        {:error, "option 'cell_ref' is required to start the Pod"}

      cell_ref ->
        :gen_statem.start_link(__MODULE__, opts, name: via_registry(cell_ref))
    end
  end
end

  defp via_registry(cell_ref) do
    {:via, Registry, {Arca.Registry, {__MODULE__, cell_ref}}}
  end

The first problem is that I can’t start this module under a supervision tree because :gen_statem doesn’t implement child_spec. So then I have to implement the child_spec myself:

  # This is not automatically implemented by the gen_statem behaviour
  def child_spec(opts) do
    %{
      id: __MODULE__,
      start: {__MODULE__, :start_link, [opts]}
    }
  end

So I end up with a module like this:

defmodule Arca.PodImpl do
  @moduledoc false

  def start_link(opts) do
    cell_ref = Keyword.get(opts, :cell_ref)

    case cell_ref do
      nil ->
        {:error, "option 'cell_ref' is required to start the Pod"}

      cell_ref ->
        :gen_statem.start_link(__MODULE__, opts, name: via_registry(cell_ref))
    end
  end

  # This is not automatically implemented by the gen_statem behaviour
  def child_spec(opts) do
    %{
      id: __MODULE__,
      start: {__MODULE__, :start_link, [opts]}
    }
  end

  @impl true
  def lock(ref), do: :gen_statem.cast(via_registry(ref), :lock)

  @impl true
  def unlock(ref), do: :gen_statem.cast(via_registry(ref), :unlock)

  @impl true
  def get_state(ref), do: :gen_statem.call(via_registry(ref), :get_state)

  defp via_registry(cell_ref) do
    {:via, Registry, {Arca.Registry, {__MODULE__, cell_ref}}}
  end
end

For context, this is how I define my application children:

  def children(_target, _env) do
    [
      {Registry, keys: :unique, name: Arca.Registry},
      {Arca.PodImpl, cell_ref: "A1", lights_range: 75..95},
      {Arca.PodImpl, cell_ref: "A2", lights_range: 61..73},
      {Arca.PodImpl, cell_ref: "A3", lights_range: 20..58}
    ]
  end

Now, the main problem is that the PodImpl process seems to not be registered on my registry.

So when I start my application, the PodImpl modules seem to be linked to the main supervisor (makes sense) but when I query the Registry there are no registered processes.

So when I try to call PodImpl.get_state("A1") it returns an error saying that there is no process with that name (the one constructed by the via_registry private function)

I hope someone can help me understand what is going on.

The docs for start_link/3 state:

Equivalent to start_link/4 except that the gen_statem process is not registered with any name service.

I’m pretty sure you want:

:gen_statem.start_link(via_registry(cell_ref), __MODULE__, opts, [])
3 Likes

That solves it, thank you so much!