Creating a supervised global singleton genserver

We have a process that is registered using:

def start_link do
  case GenServer.start_link(__MODULE__, :ok, name: {:global, __MODULE__}) do
    {:ok, pid} ->
      Logger.info "---- #{__MODULE__} worker started"
      {:ok, pid}
    {:error, {:already_started, pid}} ->
      Logger.info "---- #{__MODULE__} worker already running"
      {:ok, pid}
  end
end

Currently, if we kill the node running the singleton, the other node doesn’t seem to notice that the node is down, and the killed processes aren’t restarted.

E.g Supervisor.which_children on the remaining node returns the remote pid #PID<4554, 234, 0>, and :global.whereis_name returns :undefined

What we would like to see is that the supervisor restarts the process on the remaining node, but so far our attempts have been fruitless.

2 Likes

This is a good example: https://github.com/arjan/singleton

2 Likes

Thanks Dom! Singleton worked :slight_smile:

Another option, which I’ve seen in a codebase, is to lazily restart the GenServer when a client call notices that it’s down. This is instead of monitoring the process on every node and handling the :DOWN message as Singleton does.

e.g.

## Client API
def foo(name \\ __MODULE__) do
  case GenServer.whereis({:global, name}) do
    nil ->
      # restart on an available node, e.g. via Supervisor.restart_child 
      GenServer.call({:global, name}, :foo)
    pid ->
      GenServer.call(pid, :foo)
  end
end

## Server callbacks
def handle_call(:foo, _from, state) do
  # update state and reply
end
3 Likes