---
title: "Understanding Elixir OTP Applications - Part 1, Distribution"
description: "I love Elixir. Coming from Ruby I find myself in familiar territory with the syntax yet also rejoice at some of the things Elixir does differently. I'm doing a series on Elixir and Erlang's principals and how they work underneath. This post will cover distributed systems and processes."
canonical: https://oozou.com/blog/understanding-elixir-otp-applications-part-1-distribution-79
locale: en
date: 2020-11-25
author: "OOZOU Team"
---

# Understanding Elixir OTP Applications - Part 1, Distribution

> I love Elixir. Coming from Ruby I find myself in familiar territory with the syntax yet also rejoice at some of the things Elixir does differently. I'm doing a series on Elixir and Erlang's principals and how they work underneath. This post will cover distributed systems and processes.

Published: 2020-11-25 · Author: OOZOU Team (Engineering) · 5 min read

Elixir is built on top of Erlang. Erlang was the language developed at Ericsson, later known as Sony Ericsson, the telecom company. When the original engineers designed Erlang's [Open Telecom Protocol](https://en.wikipedia.org/wiki/Open_Telecom_Platform) they had some ideas about what kind of characteristics the system would need to handle their telecom architecture reliably.
OTP was designed to be distributed, fault-tolerant, real-time, highly-available, and hot-swappable.
That's a lot to digest - what does that all mean? In this article I'm going to go over how the distributed portion of this works.
Erlang and Elixir's concepts are fairly interchangeable. A lot of modules in Elixir are abstractions around Erlang's abstractions so you can think of them as the same concept. In this article I'm going to discuss how Elixir's [Processes](https://hexdocs.pm/elixir/Process.html) and [GenServer](https://hexdocs.pm/elixir/GenServer.html) works which is at the core of their distributed systems.

# Communicating with distributed processes through mailboxes

OTP was designed to work on a [distributed networks](https://en.wikipedia.org/wiki/Distributed_networking#:~:text=Distributed%20networking%2C%20used%20in%20distributed,are%20dependent%20upon%20each%20other.). It manages this through its mailbox system. Any Elixir Process can spawn child processes - they share no memory and are therefore completely isolated. To communicate with each other, they send messages to the processes mailbox identified by the Process ID (PID). Each Process can send messages to other processes and also configure a listener on its own mailbox to handle incoming messages.
First let's spawn a simple Process that returns a message to the parent's mailbox:

```elixir
parent = self()
spawn fn -> send(parent, {:ok, self()}) end
Process.info(parent, :messages)
=> {:messages, [ok: #PID<0.000.0>]}
```

First we're getting the PID for the current Process so we can use that within the anonymous function we're passing to spawn.
Inside the spawned function, we're calling [send/3](https://hexdocs.pm/elixir/Process.html#send/3). The first argument is the Process we are sending the message to, and the second is the message. In this case we're sending a tuple with :ok and the PID of the spawned Process. The message can be in any format you like - in Elixir it's common to use tuples so we can match against the first element in order to know how to process the second element.
We can inspect the messages using [Process.info/2](https://hexdocs.pm/elixir/Process.html#info/2) and see that our message has already been returned.
Next let's process those messages:

```elixir
parent = self()
spawn fn -> send(parent, {:ok, self()}) end
receive do
  {:ok, pid} -> "Got ok from #{inspect pid}"
  _ -> "Unknown message type"
end
```

The [receive/1](https://hexdocs.pm/elixir/Kernel.SpecialForms.html#receive/1) function allows us to create a listener that waits for a message to be sent to the inbox for the current Process. Inside the receive block then we can pattern match the type of message we received. If you're confused by the syntax of receive, that's because it's a [SpecialForm](https://hexdocs.pm/elixir/Kernel.SpecialForms.html) macro.
Ok, now let's create a loop and spawn a linked Process, ensuring our Process doesn't just die. With this simple setup we can store state or in this simple example just return a message mimicking a primitive bot.

```elixir
defmodule Bot do
  def start_link do
    Task.start_link(fn -> loop() end)
  end

  defp loop() do
    receive do
      {:help, caller} ->
        send caller, "You can ask me what today's date is by passing :date_today"
        loop()
      {:date_today, caller} ->
        send caller, "Today's date is #{Date.utc_today()}"
        loop()
    end
  end
end

{:ok, pid} = Bot.start_link
send pid, {:help, self()}
flush()
=> "You can ask me what today's date is by passing :date_today"
=> :ok
send pid, {:date_today, self()}
=> "Today's date is 2020-11-20"
=> :ok
```

Notice how the spawned task is looping each time the message is processed inside receive. This is the basis for GenServer.

# Understanding the GenServer Interface

When building the OTP libraries the developers recognized this pattern would be common, so they created an abstraction to handle looping and message processing for us.
A module that implements GenServer can implement various callbacks. We're going to use [init/1](https://hexdocs.pm/elixir/GenServer.html#c:init/1), [handle_cast/2](https://hexdocs.pm/elixir/GenServer.html#c:handle_cast/2), and [handle_call/3](https://hexdocs.pm/elixir/GenServer.html#c:handle_call/3). We can then use GenServer to start a Process which will loop, waiting for a [cast/2](https://hexdocs.pm/elixir/GenServer.html#cast/2), or [call/3](https://hexdocs.pm/elixir/GenServer.html#call/3). Once the Process receives cast/2 or call/3, it will trigger our handle_cast/2 and handle_call/3 callbacks respectively.
Let's create a key-value store, a simplified version of Redis, or Memcached. For convenience and readability we'll add start/1, set/3, get/2, and delete/2 functions to abstract the GenServer.cast/2 and GenServer.call/3 calls

```elixir
defmodule KeyValueStore do
  use GenServer

  def start(initial_state \\ %{}) do
    GenServer.start(__MODULE__, initial_state)
  end

  def set(server, key, value) do
    GenServer.cast(server, {:set, key, value})
  end

  def get(server, key) do
    GenServer.call(server, {:get, key})
  end

  def delete(server, key) do
    GenServer.cast(server, {:delete, key})
  end

  def init(state) do
    {:ok, state}
  end

  def handle_cast({:set, key, value}, state) do
    {:noreply, Map.put(state, key, value)}
  end

  def handle_cast({:delete, key}, state) do
    {:noreply, Map.delete(state, key)}
  end

  def handle_call({:get, key}, _from, state) do
    value = case Map.fetch(state, key) do
      {:ok, v} -> v
      :error -> nil
    end
    {:reply, value, state}
  end
end
```

Now we can start our KeyValueStore, pass key value pairs to it and retrieve them at a later date.

```elixir
{:ok, pid} = KeyValueStore.start
Process.register(pid, :primary_store)
:primary_store
:primary_key_value_store |> KeyValueStore.set(:foo, :bar)
=> :ok
:primary_store
|> KeyValueStore.get(:foo)
=> :bar
:primary_store
|> KeyValueStore.delete(:foo)
=> :ok
:primary_store
|> KeyValueStore.get(:foo)
=> nil
```

# What's next?

In the next post I will be going over how Elixir deals with fault-tolerance through supervisors.

# Need help?

Do you have an Elixir project that you need help with? Get in touch with us to see how we can help.

---

Contact OOZOU: hello@oozou.com · +66 2544 0000 · https://oozou.com/contact

More for AI agents: https://oozou.com/llms.txt · every page has a markdown twin at its URL plus ".md"
