Global Dynamic Supervisor in a cluster

I posted this question on stackoverflow but didn’t get any response:
I have a unique issue that I have not had a need to address in elxir.

I need to use the dynamic supervisor to start (n) amount of children dynamicly in a clustered environment. I am using libcluster to manage the clustering and use the global process registry to lookup the dynamic supervisor pid… Here is what is happening:

global: Name conflict terminating {:packer_supervisor, #PID<31555.1430.0>}

Here is the code for the supervisor:

defmodule EcompackingCore.PackerSupervisor do
  use DynamicSupervisor
  require Logger

  def start_link() do
    DynamicSupervisor.start_link(__MODULE__, :ok, name: {:global, :packer_supervisor})
  end

  def init(:ok) do
    Logger.info("Starting Packer Supervisor")
    DynamicSupervisor.init(strategy: :one_for_one)
  end

  def add_packer(badge_id, packer_name) do
    child_spec = {EcompackingCore.Packer, {badge_id, packer_name}}
    DynamicSupervisor.start_child(:global.whereis_name(:packer_supervisor), child_spec)
  end

  def remove_packer(packer_pid) do
    DynamicSupervisor.terminate_child(:global.whereis_name(:packer_supervisor), packer_pid)
  end

  def children do
    DynamicSupervisor.which_children(:global.whereis_name(:packer_supervisor))
  end

  def count_children do
    DynamicSupervisor.count_children(:global.whereis_name(:packer_supervisor))
  end

end

The issue seems to be that the supervisor is started on both nodes. What would be the best way to handle this? I really need the supervisor to be dynamic so I can manage the worker modules effectively. Possibly a different registry?

Thanks for your help.

2 Likes

Hello,

You have a conflict because the process is already started. Instead of having

  def start_link() do
    DynamicSupervisor.start_link(__MODULE__, :ok, name: {:global, :packer_supervisor})
  end

you could have something like:

  :global.trans({__MODULE__ , :packer_supervisor}, fn ->
    case DynamicSupervisor.start_link(__MODULE__, :ok, name: {:global, :packer_supervisor})
      {:ok, pid} ->  {:ok, pid}
      {:error, {:already_started, pid}} ->
        Process.link(pid)
        {:ok, pid}
      error ->  error
end

:global.trans will ensure you don t have concurrency issue while starting the process. It won t crash as it handles the case where the process is already started.

2 Likes

Hey, I was wondering wouldn’t the Process.link(pid) in your example mean that this process will remain alive and linked with the original process with the same name, hence having two identical processes?

Move your registration to the init function and use this function call
https://erlang.org/doc/man/global.html#register_name-3

Then for the resolve function, you can use random_exit_name/3 if you want it to exit or the notify version if you want to keep the process alive as a backup.
https://erlang.org/doc/man/global.html#random_exit_name-3