Using the Registry in Elixir 1.4

adam mokan
ElixirLabs
Published in
4 min readJan 9, 2017

--

One of the new additions in Elixir 1.4 that I’ve been looking forward to is the new Registry module. Let’s look at one way to take advantage of this handy feature that is now part of the core library.

Github Example - https://github.com/amokan/registry_sample

If you are new to Elixir and learning the basics of OTP, especially while using the REPL (iex), you may find yourself overwhelmed with the abundance of process IDs or PIDs you end up keeping track of. With the actor model, it is not uncommon to have hundreds or thousands of processes running in a system. Some ways we reference these processes are by keeping track of a PID or by a specified name when we launch a process. The Registry module provides us another option without using an external library.

Imagine we have a scenario where we are selling widgets to customers/accounts. Our boss wants us to build a realtime UI that shows every account that has placed an order in the current day along with some basic info about each account. If an account doesn’t place another order within 24 hours, it should fall off the UI.

One quick solution is to spawn an account process when an account places an order. We could have a function that terminates processes that have not been updated with new orders after the 24 hour threshold. But how do we know which PID is related to a specific account? This is a good case for the Registry module which can almost act like a DNS lookup with some sort of name, in our case the account_id, and provide us with reference to the running process.

We will use a registry like a white pages directory for account processes (image © 2010 by Tomasz Sienicki https://commons.wikimedia.org/wiki/File%3ATelefonbog_ubt-1.JPG )

First off, we will need to start a registry and give it a name using Registry.start_link. Let’s just add the registry we need to our application supervision tree. Note that we are creating a :unique registry.

children = [
supervisor(Registry, [:unique, :account_process_registry])
]

Now we have a unique registry named :account_process_registry that is ready to keep track of process IDs for us. We will just need to implement a function that returns a :via tuple, based on a value we provide. GenServer provides the plumbing to make use of this :via tuple and automatically handle the registration of our name (account_id) to the actual process.

defmodule Account do
use GenServer
def start_link(account_id) do
name = via_tuple(account_id)
GenServer.start_link(__MODULE__, [account_id], name: name)
end
defp via_tuple(account_id) do
{:via, Registry, {:account_process_registry, account_id}}
end
def order_placed(account_id) do
GenServer.call(via_tuple(account_id), :order_placed)
end
# genserver callbacks not shown for simplicityend

Our via_tuple function, along with the Registry module, now allows us to spawn a process for a given account_id and make subsequent calls on the process using the same id, rather than a PID. No longer are we relying on cryptic PIDs or hacking together a naming system inside our start_link function. If we want to find a process in a system that is running thousands, it’s as easy as just providing the account_id.

iex> Account.start_link(5)
{:ok, #PID<0.114.0>}
iex> Account.start_link(20)
{:ok, #PID<0.118.0>}
iex> Account.order_placed(20)
1

What happens if we try to start a process for account_id #5 again?

iex> Account.start_link(5)
{:error, {:already_started, #PID<0.114.0>}}

The registry knows we are already tracking a process for account_id #5 and we get a message back indicating :already_started.

This is a good start. We can now create a process with a name or identifier that is important to us internally and also know that the system will not let us create a duplicate process for this specific identifier. The other big plus is that if our process crashes and is restarted for any reason, the system is capable of registering the new PID with the same registration name we used initially. A standard reference to a specific PID would not work this way.

Where do we go from here? The rest of the process would be basic GenServer calls and state updates, but I think getting the process identification handled within the Elixir language itself is excellent and allows us to focus on business logic. Obviously we would still need to implement a web socket implementation to push data to a UI, but we won’t go down that road today. (Hint: I’d recommend cowboy if you just need a simple web socket)

If you look in my example Github repo, you will find some potentially helpful generic functions utilizing the registry in the RegistrySample.AccountSupervisor module. Functions like find_or_create_process/1 are things I tend to commonly write in scenarios like this to make process management easy.

Feel free to adjust the code to your needs and let me know if you have any questions!

--

--

adam mokan
ElixirLabs

Husband and father. Director of Engineering @ HiringSolved. Functional programming fan. Electronics guy. Music nerd. Habitual button-pusher. From Michigan.