How to perform Hot Code Swapping in Elixir- #1

Always be up to date…

Photo by Chris Lawton on Unsplash

This article will guide you to perform hot code swapping using GenServer:sys.change_code function.

The world is moving at a high speed. Every one is on race knowingly or unknowingly. If you stop or wait then you are letting yourself down. OK! I’ll stop being more dramatic here. It is just to make you understand how important the time is in the modern world especially in software development.

Hot code swapping is replacing or adding components without stopping or shutting down the system. It is frequently called as hot plugging.

In software development, hot swapping is used to upgrade or update the system without interrupting the current running system. It helps us with less downtime at the time of upgrading.

OK! Stories are always good to hear. Let’s dive into code.

Step 1 — Creating a GenServer

The hot code swapping can be achieved using the behavior module GenServer. Save the following lines of code into a file called demo_server.ex .

defmodule DemoServer do
use GenServer
@vsn "1"
## Client API
def start_link employee do
GenServer.start_link __MODULE__, employee, []
end

def add_money(pid, value) do
GenServer.call(pid, {:add, value})
end
## Server API
def init(employee) do # initiating state with passed employee details
{:ok, employee}
end

# add the value to the state and returns :ok
def handle_call({:add, value},_from, %{name: name, money: money} = state) do
{:reply, "#{value} added to #{name} ", Map.put(state, :money, money+value)}
end

end

This DemoServer does nothing interesting as it is intentionally limited to receive only one kind of messages to handle. It can only add the money to the employee account. I know it is silly. But, it gives you the clear idea of what we are for.

Warning ⚠️ : 
I did not register this server with a name. So, any transaction should be performed using pid the process identifier of the server not by the DemoServer module name.

If you really need to register, just update the module line from GenServer.start_link __MODULE__, employee, [] to GenServer.start_link __MODULE__, employee, [name: __MODULE__] . It serves you the purpose.

I just want you to know the idea behind how this GenServer can perform hot code swapping here. That’s why I took simple real world example.

Code Analysis

  1. @vsn "1" will let us to identify the code versions. You need to update this value in your next version. We will see that in later in this article itself.
  2. start_link/1 and add_money/2 are provided to the client to initiate the server and adding money to that specific employee here.
  3. %{name: name, money: money} this is the default state of our DemoServer
  4. “#{value} added to #{name}” Every time you add money, you will get this sentence as a response. ex "30000 added to blackode" .
  5. Map.put(state, :money, money+value) at the end, we are updating the state by adding the value to money.

Before going to update our GenServer, it is customary to check the current version is working fine. Let’s check that.

Load and Ensure DemoServer

$iex demo_server.ex
iex> DemoServer
DemoServer
Loading DemoServer

Ensure it is Working

iex>  employee = %{name: "blackode", money: 30000}
%{money: 30000, name: "blackode"}
iex> {:ok, pid} = DemoServer.start_link employee
{:ok, #PID<0.110.0>}
iex>  DemoServer.add_money pid, 30000
"30000 added to blackode "
iex>  :sys.get_state pid
%{money: 60000, name: "blackode"}

Step2 — Creating Version 2 for DemoServer

Here comes our actual implementation of the idea that we have been looking for “Hot Code Swapping”.

What are the things we are updating in the version 2 ?

  1. Updating key money to salary.
  2. After adding money, instead of responding with String.t() , we return the new state Map as a reply .

Though the changes are atomic here, it well serve our idea. I don’t want to confuse you with the complex code logic. You can make as many changes as you like.

Let’s code it.

Create a new file demo_server_2.ex

$ touch demo_server_2.ex

Code update @vsn “2”

Make sure you updated the @vsn value here. You can observe the changes in the following lines of code

Code Analysis

In the previous version lines of code, the state was pattern matched to {name: name: money: money} . But here, we updated the pattern matching with {name: name, salary: salary} ; the key money transformed to salary.

The {:add, value} message handler now reply with new_state which is a map instead of a string like before "500 added to blackode"

If we directly load this module, we will get an exception of no function clause to handle error. Because, though we updated the code, the old state still contains only {name: name, money: money} .

So, we need to update the state here. GenServer provides a server callback function called code_change which helps us here. It receives old_version, old_state and some useful information.

The code_change callback is sole responsible for updating the state here.

In the above definition, we are pattern matching to old_state and returning a tuple with {:ok, new_state} .

Step 3 — Hot Code Swapping

Photo by Suzanne D. Williams on Unsplash

This again involves three more steps.

Step 3.1 — Suspending Current Running Process

Now, with out closing the iex session run the following line of code in iex.

iex> :sys.suspend pid
:ok
# pid is the process identifier for DemoServer

Why we need to suspend the process?
When you suspend a process, it will no more receive messages to handle. As we are going to update it’s state, we should not allow others to make requests for a while.

Step 3.2 — Code Changing from Version 1 to Version 2

Before changing the code, you need to load the new_version code here. Our new version code lies inside the file demo_server_v2.ex .

iex> c "demo_server_v2.ex"
warning: redefining module DemoServer (current version defined in memory)
demo_server_v2.ex:2
[DemoServer]

It gives you a warning about re-defining the module.

After loading the new version code, you need to update the state using :sys.change_code .
You need to pass following information to :sys.change_code function.

  1. pid — Process Identifier for DemoServer.
  2. Module Name — The Name of the module where our callback lies. Here, it is DemoServer.
  3. Old_version — The previous version, here ti is “1”.
  4. nil — Other extra information. We don’t care here now. So passing nil here.
iex> :sys.change_code pid, DemoServer, "1", nil
:ok

The :sys.change_code function triggers the callback function code_change which updates the state of the DemoServer

Don’t get confuse here with :sys.change_code and a callback code_change inside the DemoServer module.

Step 3.3 — Resuming the Process and Testing

iex> :sys.resume pid
:ok

So far our employee has salary of 60000 . Now we add another 100_000 . So, with out any error it should give a map instead of string.

iex> DemoServer.add_money(pid, 100000)
%{name: "blackode", salary: 160000}

Boom!

The code was hot swapped with new state and new style of response. You have hot plugged the updates to your existing running system without shutting it down.

Photo by Samuel Zeller on Unsplash

Hope you liked it. Feel free exchange your ideas here…

How to perform Hot Code Swapping using Distillery — #2 — A (Live Demo) GenServer State update

The second part of Hot Code Swapping using Distillery, explains all about releases and hot code plugging to your remote system. Check the following link.

Thanks for reading.

Join Our Telegram Channel and support us.

https://t.me/blackoders
if worth_clapping, do: “clap”, else: nil

Happy Coding 🎉