Playing with Elixir- Part 2: Process Registry

Playing with Elixir- Part 2: Process Registry

Processes are the basic building blocks in Elixir. These are used for building distributed fault tolerant systems. These can be compared to "actors" in AKKA (AKKA actors are inspired from Erlang). Processes are isolated from each other, run concurrent to one another and communicate via message passing.

You can start a process by using spawn and you will get a PID back. This PID can be used to send and receive messages to the corresponding message. Lets take a simple example

defmodule Example do
  def listen do
    receive do
     {:ok, "hello"} -> IO.puts("World")
    end

  listen
 end
end

then you can test it in iex session

iex> pid = spawn(Example, :listen, [])
#PID<0.108.0>

iex> send pid, {:ok, "hello"}
World
{:ok, "hello"}

Recently I tried to build a online multiplayer tic-tac-toe game using Elixir. Where each game was handled by a process. Whenever someone starts a new game, dynamically we create a process and we keep the reference to this process. And each user action has to be communicated to the corresponding process.

Since each game needs to store the game state, I used GenServer to start the process. A GenServer is a process like any other Elixir process and it can be used to keep state, execute code asynchronously and so on. The GenServer behaviour abstracts the common client-server interaction. You can see basics of GenServers https://hexdocs.pm/elixir/GenServer.html.

You can have thousands of processes running in the system (could be in the same node or in distributed manner). Number of PIDs we need to keep track can be a problem.

One way would be to start the GenServer with a name and refer it by that. From the documentation we can see that GenServer can be registered with

  • atom : GenServer is registered locally with the given name
  • {:global, term} : GenServer is registered globally with the given term using the functions in the :global module. Mostly used in distributed mode
  • {:via, module, term} : GenServer is registered with the given mechanism and name. One such example is the :global module which uses these functions for keeping the list of names of processes and their associated PIDs that are available globally for a network of Elixir nodes. Elixir also ships with a local, decentralized and scalable registry called Registry for locally storing names that are generated dynamically.

Since we need to dynamically create processes for each game, we can't use the first option.

The second option is used to register a process globally, across multiple nodes. This also means it requires synchronization across the entire cluster, which is not what we are looking right now.

Third option expects a module with register_name/2, unregister_name/1, whereis_name/1 and send/2. That means, once we provide a module with these methods, system will register PIDs once it started, will use whereis_name to find the PID to send message and use unregister_name to remove once the process is stopped.

In that module we can use a simple Map, where we use the game name(randomly created unique for each game) as the key and PID as the value.

  • register_name : Add to the Map
  • unregister_name : remove to the Map
  • whereis_name : get from Map

Since we are building a fault tolerant system, there is another issue we need fix. If any process gets killed/crashed or restarted, our Map is unaware of this and will contain dead process PID. This can be easily solved by monitoring the started PIDs by the Registry the we will get :DOWN message with PID and other information.
We just need to remove this from our MAP.

Here is a final Registry module we came up with Registry.

Finally the code can be found here (https://github.com/vmuneeb/elixir-multiplayer-tictactoe)

You can check the final game here.

Earlier when I was working with AKKA actors, there was ActorSelection for keeping track of actor references. Given an actor path with address information you could get hold of an ActorRef to any actor. And you could use any other data structure to store these references for future use. In this example you could see that all the available worker actors references were kept in a HashMap.
But it looks like in the newer version of AKKA, they got rid of that and introduced Registry service similar to this. Official Documentaion here.