How to check globally named Agent?

I need to make sure that there is already an agent with the same name exist before I create a new one,
here is what I do:

    unless is_nil(Process.whereis({:global, user_id})) do 
      Agent.start_link(fn -> %{} end, [name: {:global, user_id}])
    end

however, I got error:

[error] an exception was raised:
    ** (ArgumentError) argument error
        :erlang.whereis({:global, "23123"})
        (elixir) lib/process.ex:646: Process.whereis/1

why I got such error?

You should use :global module to find processes registered with :global. Process.whereis is for processes registered with Process.register.

case :global.whereis_name(user_id) do
  :undefined ->
    # You should probably check here for a race condition 
    Agent.start_link(fn -> %{} end, [name: {:global, user_id}])
  pid when is_pid(pid) -> {:ok, pid}
  _ -> {:error, :something_went_wrong}
end
2 Likes

You can not start a process with the same name if there is already a process with that name…

Exactly, so this might fail…

Agent.start_link(fn -> %{} end, [name: {:global, user_id}])

… if a starting process is not yet seen here

:global.whereis_name(user_id)

or is it my mistake?

As was already pointed out, the BEAM won’t let you start a second process with the same name. Since in your code you’re not handling the return:

you could just have Agent.start_link(fn -> %{} end, [name: {:global, user_id}]) and get the same result. Assuming that’s just example code and you do care about the response, you can do something like:

def ensure_agent(user_id) do
  case Agent.start_link(fn -> %{} end, [name: {:global, user_id}]) do
    {:ok, pid} -> {:ok, pid}
    {:error, {:already_started, pid}} -> {:ok, pid}
    response -> response
  end
end

Your code can then call ensure_agent/1and will know that if {:ok, pid} is returned an agent is ready.

4 Likes

Thanks everybody :slightly_smiling_face: