Registered names (name: :foo) must be atoms, and
atoms are never garbage collected. To name thousands
of dynamic processes (one per user, room, bucket)
use a Registry, which maps any term to a pid.
1
{:ok,_}=Registry.start_link(keys::unique,name:Rooms)defmoduleRoomdouseGenServer# A `:via` tuple tells GenServer to register the# name through Registry instead of the atom table.defvia(id),do:{:via,Registry,{Rooms,id}}defstart_link(id),do:GenServer.start_link(__MODULE__,id,name:via(id))defjoin(id,user),do:GenServer.call(via(id),{:join,user})@impltruedefinit(id),do:{:ok,%{id:id,users:[]}}@impltruedefhandle_call({:join,user},_from,state)dousers=[user|state.users]{:reply,Enum.reverse(users),%{state|users:users}}endend
Entries are removed automatically when the process
dies, but asynchronously: the Registry learns about
the exit from a message. Right after stop a lookup
may still return the dead pid, so code that needs
the name gone has to wait for it.
Pitfall:String.to_atom("room_" <> id) to name
dynamic processes leaks atoms; the atom table has a
fixed limit (about a million by default) and the VM
crashes when it's full. Use a Registry.
Pitfall: "the process stopped, so its name is
free" is a race. This example once printed the dead
pid when run in the browser and [] natively. Start
the replacement and handle {:already_started, pid},
or wait as above; don't assume instant cleanup.
Pitfall: Registry is local to one node. In a
cluster, a name registered on node A is invisible on
node B. For cluster-wide names look at :global,
or :pg for groups (see the Distributed track).