Table of Contents
As you’ve probably guessed (based on the name of our company), we’re fans of Ruby on Rails. It helped us implement various projects and feel comfortable in the world of programming.
Nevertheless, everything is constantly evolving. Every day appear new languages, frameworks, and architectures that bring new approaches and opportunities.
This episode will be devoted to one of these opportunities: the Elixir language.
Among all the thousand of various programming languages this powerful technology will allow you to implement your project using simple but at the same time elegant solutions.
Before we tell you the story of our experience with the Elixir language, let’s find out how it joins the ranks of top-class coding languages.
The story behind the Elixir language
Elixir was invented by José Valim, who’s famous Ruby developer and creator of many cool gems. When developing Elixir, he wanted to enable higher extensibility and productivity in the Erlang Virtual Machine, while keeping compatibility with Erlang’s ecosystem. Here’s what José tells how he comes to that matter:
“It is a long story, but I will try to make it short and sweet. Back in 2010, I was working on improving Rails performance when working with multi-core systems, as our machines and production systems are shipping with more and more cores.
However, the whole experience was quite frustrating as Ruby does not provide the proper tool for solving concurrency problems. That’s when I started to look at other technologies and I eventually fell in love with the Erlang Virtual Machine. I started using Erlang more and more and, with experience, I noticed that I was missing some constructs available in many other languages, including functional ones.
That’s when I decided to create Elixir, as an attempt to bring different constructs and excellent tooling on top of the Erlang VM.”
No sooner said than done and José Valim made his dream a reality.
This is how the world saw Elixir.
As soon as we realized that this technology can make the development process even more effective, we started using it without a second thought!
Alright, let’s get to the point. Imagine a usual morning of some usual software developer. They make themselves a cup of coffee, open some post about the advantages of a programming language and see the following phrase:
“Just add more servers ¯\_(ツ)_/¯”
Here’s the point, when a programming language is quite slow in terms of its performance, this advice can be considered just as one of the justifications.
Just add more servers…
Actually, let’s be honest, thanks to the orchestration tools of containers (Kubernetes, OpenStack), you won’t need to make much efforts to meet this challenge.
Nevertheless, there’s one downside.
A software package should be implemented as a stateless service. If we talk about regular web-service, it all comes down to storing the files in a cloud (Amazon S3, Minio) as well as using some database management system. Having made our application stateless, we just move tasks of scaling from one tool to another(from runtime to a database).
Honestly, do you really find Node.js a good tool? After all this?
You can use any other platform instead nodejs and you’ll get the same valid statement. Even various esoteric languages can be scaled this way. On the other hand, the question is: “How difficult will it be to use these languages for writing the programs?”
Thanks to the stateless approach, we can unnoticeably replace containers and provide a high availability to the server.
Now we’re getting to real life examples where we used the magic of Elixir.
Our experience with Elixir language
One of the projects we worked with was an online game. Each session of this game can last for 4 hours, where can participate about 4 players. There’s a fixed time limit for each player to make a move. So there were the following non-functional requirements:
- concurrency and availability (our game should always answer and can’t be blocked by other processes on the server);
- bidirectional communication (the data should be retrieved by the client both on his request and the server’s initiative);
- save server upgrade (there should be a mechanism for updating the servers without losing any data);
- performance (this requirement is especially important when it comes to games);
By implementing all these requirements, putting stateless to absolute will make more harm rather than good.
For writing the functionality, we used the Elixir language.
As you know this programming language uses the famous Erlang virtual machine — BEAM. Perfect pro-competitive measures and multithreading, exchange of asynchronous notifications in accordance with the actors models: that’s all we need to lay a proper foundation for our app.
One of the things a game server is responsible for is to be an initiator for some changes along with players. It shouldn’t only broadcast notifications from one players to other.
This means that there should be a distinct process, which will be responsible for this task. A proper work with processes is a “must-have.” The more effective it is, the better it is.
There are some games that stop their servers for updating. On the other hand, there are games that add new servers and allow old games to finish. However, all these processes overload DevOps department.
There’s one more option: upgrade a server code on-the-fly. Despite the fact that there are not so many processes with this feature, we found it in BEAM.
This allowed us to provide a communication between a server and clients.
Having used web-sockets with hot code swapping, we got rid of any problems with an internet connection during the updating.
However, the time of client connection with the server may negatively influence the game results. At the same time, we reserve the right to use the first two options, if necessary. It’s always cool to have several options, isn’t it?
Since speed is crucial criteria for every game, we don’t have to choose between In-Memory state and persistence. Our games are isolated from each other so that we use a separate process for every game (GenServer), which save a condition in itself.
In case, we wanted to create something like that on Ruby (one of the main tool we use), we’d probably use celluloid. When reviewing the issues on Github, out attention was attracted to the following issues memory leak with timers.
We also need to consider the fact that the version 0.17.3. is considered as a stable one. Elixir, in its turn, doesn’t have such a challenge as BEAM provides an automatic garbage collection for every process individually. As we put the results of the game into the persistent database only after the end of the game, this provides us with a high speed.
Nevertheless, there’s no need to concentrate exclusively on stateless and 12-factors. Otherwise, it may significantly complicate the project implementation by looking for the technologies you won’t need at all. Mainly it happens because of the main tools limitation.
The Bottom Line
Based on our experience, we can definitely say that one of the most effective solutions was achieved using BEAM. Its effectiveness and at the same time simplicity of inter process communication is amazing!
It has great development tools for building concurrent apps.
Basically, you won’t find anything better if your app will have a great number of users, such as an IoT, chat or social media app. It has a comprehensive documentation with highly accessible tooling and resources.
Elixir is one of those technologies that allow you to use a phrase “Just add more cores” before “Just add more servers.”