Location transparency in Elixir by example

Mohammed Hewedy
3 min readAug 13, 2023
Photo by Wilhelm Gunkel on Unsplash

Elixir is a functional, concurrent, high-level general-purpose programming language that runs on the BEAM virtual machine.

BEAM is the virtual machine at the core of the Erlang Open Telecom Platform (OTP).

In Erlang send operation is location transparent — the recipient can be on any machine, and all the information needed to guide the message to the right location is encoded in the process identifier. Erlang guarantees that process identifiers are unique on the network, even across machines.

In this post, we will implement location transparency In Elixir.

First, let's create a new mix project

mhewedy@localhost:~/temp/elixir/tmp$ mix new simple_queue --sup

It will create a new Elixir (Mix) project with the following structure:

mhewedy@localhost:~/temp/elixir/tmp$ cd simple_queue

mhewedy@localhost:~/temp/elixir/tmp/simple_queue$ tree
.
├── lib
│ ├── simple_queue
│ │ └── application.ex
│ └── simple_queue.ex
├── mix.exs
├── README.md
└── test
├── simple_queue_test.exs
└── test_helper.exs

3 directories, 6 files

Now, let’s implement the module SimpleQueue (at lib/simple_queue.ex) as a GenServer as follows:

defmodule SimpleQueue do
use GenServer

# Client API
def start_link(state \\ []) do
GenServer.start(__MODULE__, state, name: {:global, __MODULE__})
end

def queue() do
GenServer.call({:global, __MODULE__}, :queue)
end

def enqueue(value) do
GenServer.cast({:global, __MODULE__}, {:enqueue, value})
end

def dequeue() do
GenServer.call({:global, __MODULE__}, :dequeue)
end

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

def handle_call(:queue, _from, state) do
{:reply, state, state}
end

def handle_call(:dequeue, _from, [value | state]) do
{:reply, value, state}
end

def handle_call(:dequeue, _from, []) do
{:reply, nil, []}
end

def handle_cast({:enqueue, value}, state) do
{:noreply, state ++ [value]}
end
end

As you can see in the previous code, the tuple {:global, __MODULE__} gives a name to the Process as SimpleQueue and the :global here used to register the process as a global process using the Erlang :global module.

And let’s modify the file lib/simple_queue/application.ex to add the SimpleQueue GenServer to the Supervision tree of our application, Under the children list, add Our SimpleQueue as follows:

children = [
# Starts a worker by calling: SimpleQueue.Worker.start_link(arg)
# {SimpleQueue.Worker, arg}4
{SimpleQueue, []}
]

Now, let’s start two iex processes (to represent two nodes/machines)

starting two iex processes

As seen above, we name the first node as alex@localhost and the second node as bob@localhost

Now, let’s establish a connection between the two nodes, we will use Node.connect function as follows:

Now, let’s start manipulating the SimpleQueue process (that was started by the supervisor) on the alex@localhost node:

Now, let’s check the queue on the bob node:

Viola! We are able to query the SimpleQueue process created on alex node from bob node!

As we have seen, regardless of the location the process was created at (alex node in our example), we are able to interact with the process as if it is local to the bob node.

--

--