Demystifying the Registry module in Elixir

Arpan Ghoshal
6 min readAug 8, 2024

--

The Registry module in Elixir is often unknown to many developers, in this blog post we will explore what it's all about and some common use cases.

(This blog post is taken from my free async elixir livebook, which deep dives into many other Elixir’s concurrency features)

Intro graphic

From the official documentation

A local, decentralized and scalable key-value process storage. It allows developers to lookup one or more processes with a given key.

Let's go through some important points…

  • The Registry in Elixir is a process store that stores key-value pairs, allowing us to register a process under a specific name.
  • There are two types of Registries: unique and duplicate. A unique Registry only permits one process to be registered under a given name, while a duplicate Registry permits multiple processes to be registered under the same name
  • Each entry in the Registry is associated with the process that registered it. If the process crashes, the Registry automatically removes the keys related to that process.
  • The Registry compares keys using the match operation (===/2).
    Partitioning the Registry is possible, allowing for more scalable behavior in highly concurrent environments with thousands or millions of entries.
  • It uses ETS tables to store data under the hood.
  • Registries can only be run locally and do not support distributed access.

Where to use Registry?

The most common use of Registry is to name process. The :via is frequently used to specify the process name when using the Registry.

In addition to process naming, the Registry offers other useful features such as a dispatch mechanism that enables developers to implement custom logic for request initiation. With this dispatching mechanism, developers can build scalable and highly efficient systems, such as a local PubSub, by utilizing the dispatch/4 function.

Naming processes using Registry

The most common use of Registry is in naming processes. Let's go through an example.

First, we start the Registry process

Starting a Registry

Now let's use this registry to name a GenServer, let's create a simple GenServer.

A simple stack genserver

Now that we have a simple GenServer let's try to start 2 instances of this GenServer and name each of them using the Registry.

Naming Genservers using Registry

When we register a process under a Registry, we have the option to store an associated metadata with that entry. In the second example mentioned above, we not only registered an instance of our Stack GenServer process under the registry but also stored the value :second_stack along with its corresponding entry.

Now let's call our Stack GenServer using its Registered name, we can use the lookup/2 function that returns a list like [{pid(), value()}].

Using registry to lookup processes

Let us now explore how a Registry operates when we permit the storage of duplicate entries.

When utilizing duplicate registries, it is not possible to use the :via option. To illustrate how duplicate registries function, let us attempt to register the current process twice using the register/3 function.

Registering duplicate entries

Observe how the invocation of Registry.lookup/2 resulted in a list containing 2 tuples, each representing a process along with its associated metadata. These two processes were registered under the identical name, “async_city”.

Dispatching using Registry

Dispatching allows us to fetch all entries for all processes registered under a given key. We pass a callback function that would receive the list of {pid, value} for every entry registered under the given key.

It is worth noting that dispatching takes place in the process that initiates the dispatch/4 call, either serially or concurrently in the case of multiple partitions.

To better understand the concept of dispatching, let us take a look at an example.

Dispatching using registry

The above code outputs the following

Building a pubsub system with Registry

We can also use this dispatch/3 function to implement a local, non-distributed PubSub. This works by registering multiple processes under a given key which acts like a pubsub topic.

We can then send a message to all processes registered under a key to emulate a pubsub broadcast. Here we also set the number of partitions to the number of schedulers online, which will make the registry more performant in highly concurrent environments.

Let's see this in action.

Pubsub using registry

By using this approach, we can register multiple processes under a single key within a Registry and subsequently dispatch messages to all the processes associated with that key.

Other registry functions and match specs

Apart from the register/3 and lookup/2 functions, the Registry module has several other useful functions which allow us to find and manipulate data inside the Registry. Most of these functions are straightforward to understand.

However, it's worth noting that some functions use match specs to find matching entries from the Registry let us look at some examples to understand how match specs work.

From the official documentation

A match spec is a pattern that must be an atom or a tuple that will match the structure of the value stored in the registry. The atom `:_` can be used to ignore a given value or tuple element, while the atom `:”$1"` can be used to temporarily assign part of pattern to a variable for a subsequent comparison.

Optionally, it is possible to pass a list of guard conditions for more precise matching. Each guard is a tuple, which describes checks that should be passed by assigned part of pattern. For example the `$1 > 1` guard condition would be expressed as the `{:>, :”$1", 1}` tuple. Please note that guard conditions will work only for assigned variables like :”$1", :”$2", and so forth.

Let's consider the match/4 functions in the Registry module that returns entries from the Registry that matches the match spec passed.

Using match specs with registry

The above code returns the following output

Other functions like count_match/4, select/2, etc in the Registry module also use match specs for filtering entries in the Registry.

That's all for today, folks! I hope you learned something useful.

If you found this blog post interesting check out my free async Elixir livebook which dives into many other Elixir’s concurrency features.

Resources

Photo by Howie R on Unsplash

--

--

Arpan Ghoshal
Arpan Ghoshal

Written by Arpan Ghoshal

I like to build new stuff and I have passion for coding

No responses yet