armanm

armanm

Where can one learn about zero downtime deployment strategies with Elixir and Phoenix

I know zero downtime deployment can mean different things depending on your application and what your users can tolerate so expectations can vary vastly between different projects.

I’m building an app which will execute long running tasks for users and I want to be able to deploy new revisions of the code without a fear of interrupting customers’ work. Basically it’s not a nice user experience to crash hundreds of tasks which may have ran for more than an hour and ask users to start them over. I’m looking for books or other resources which offer patterns or solutions for how this could be achieved in Elixir.

Any suggestions for books and articles are much appreciated.

Most Liked

RudManusachi

RudManusachi

Hello @armanm!

In general the idea is:

You should always have a strategy how to restart a process (think of if it crashes on the same node and gets restarted instantly by its parent supervisor… for example, store in the DB some key part of the state that could help to reinitialize the process from the step it was terminated, but don’t blindly try to store the whole state).

Now when we know that we can start our process, stop it and continue with the same initial arguments, there are 2 general ways to provide with zero down time deployments (at least I’m familiar with):

  1. Using distributed process registries (for example built in :global) and distributed supervisors.
    I’d recommend to look at Pogo. There is an article about it from @szajbus Pogo - distributed supervisor for Elixir.

    There are other options like swarm, Horde, syn you could take a look at. They all have their pros and cons.

    But one thing is common: they are eventually consistent!
    That might be fine for some use cases (when it’s ok to have a duplicate process for short period of a time when network split happen), but might not be acceptable for other scenarios. (There are companies that even have an “internal ban” on those libraries because some new devs often would think of them as an easy solution for a “single global process” and get burned by their eventually consistent nature).

    I’d also recommend to read The dangers of the Single Global Process

    There are implementations of strongly consistent consensus available for erlang and elixir (e.g. ra, and waraft) but I’m not aware of any distributed process registry or a supervisor implementation based on those.

  2. Rely on 3rd party “queue”.
    For example, RabbitMQ. There is even an official tutorial with Elixir RabbitMQ tutorial - Work Queues — RabbitMQ
    In a nutshell:

    1. You place the job to the queue.
    2. One of the workers picks up the job and starts the long running process.
    3. Only once the process is done - worker acks the job.

    If the connection get’s closed because of the node termination - job get’s requeued and other active worker picks it up and starts the process (NOW here, you want to make sure that when process is restarted, it “continues” the work as mentioned earlier!)

    Another option could be “holding” a DB transaction lock. (I think that’s what Oban uses for its queues). But that might be more limiting than RabbitMQ.

    I would say, relying on 3rd party (single source of truth) queue-like mechanism is the more battle tested and trusted approach.

D4no0

D4no0

Agree, this is the most important part. It is very important to break down a big task into smaller tasks, that can be retried once the node has been restarted, without a big time loss in the overall execution of the task.

As for the persistence of jobs, nowadays I always reach for Oban, as not only you most probably will always have a DB laying around the server, but it solves 95% of potential problems you might encounter down the road out of the box.

cmo

cmo

Some inspirational viewing on the topic:

adw632

adw632

I would agree that for most apps hot code upgrades is overkill, as much as I think they are cool, they are fragile and need testing in non production to really be confident
that you don’t trip yourself up.

Some considerations:

  1. You always need to have a strategy for runtime upgrades and OS level patching and upgrades.
  2. Solving (1) can be done with a rolling release where you add new upgraded nodes to the cluster and cycle out the old ones. Phoenix 1.7.2 even added features to drain connections on large scale deployments for this purpose.
  3. Solving (2) means you need to keep your database in a state that supports old and new schemas. This is typically done using a number of phases where you only add new columns and tables using concurrent approaches that don’t lock the production workloads.
  4. The fly.io Phoenix files have a 4 part series on some strategies for safe Ecto migrations.
  5. Another option is a very clever Postgres Reshape tool for zero downtime schema migrations that don’t stop production workloads and let you have both old and new apps running seamlessly (which may or may not be elixir apps, they could be multiple other apps using the same database schema on different release cycles) so your app code can be simplified. It’s an application agnostic schema management tool so you use it’s semantic migrations to ensure all changes never lockup the db, and believe me it’s trivial to lock a table just creating a unique index. It’s probably the most well thought out approach I am aware of but will still require plenty of care and testing.

So once you solve those problems and you still think it’s necessary to do hot code upgrades, then you can delve into diminishing returns vs rolling releases. You likely won’t have have a use case for hot code upgrades, but if you do there are some resources that explain what you you need to do:

Be aware that exrm was basically replaced by distillery, and distillery was replaced by mix releases sans, hot code reloading. I am not aware of a viable tool for doing it with elixir releases but I haven’t looked too hard.

gregvaughn

gregvaughn

You could look at Erlang resources about hot code loading but that comes with plenty of caveats and provisos that have driven most of the Elixir community away, however zero downtime deployment was one of the required features the drove early Erlang/BEAM design decisions.

Where Next?

Popular in Chat/Questions Top

LegitStack
I’m not a hugely experienced programmer, just a few years. So I’m looking for resources to learn about a topic but I can’t seem to find m...
New
RKC07
I’m new to elixir. I did some coding in python and C. I want to learn elixir for starting my career in web development. I need suggestion...
New
maqbool
what books/Resources do you recommend to learn about distributed system(theory)?
New
tom_s
Hello Elixir Community! I’m new to functional programming in general and to Elixir in particular but I’m very intrigued and would like t...
New
asfand
I am Asfandyar from Pakistan. This is my first time to the forum. I develop PHP websites using CodeIgniter, which is super easy to learn...
New
Nopp
Hello there, i have a lot to read and to learn, but are there some books, that are focussing on how i “should” plan the architecture of ...
New
asfand
Hi Everyone, I am a student and know basics of web development, used some php and ruby, but I am not an expert in any. I want to learn E...
New
pillaiindu
I am a VSCode and Sublime user and I know VIM, though I don't use VIM directly but whenever I code inside Sublime or VSCode, I use Vim em...
New
Kielo
Hi, I run a language learning blog and would like to learn how to code so I can create an app to help English speakers learn French. I ...
New
Sujit
Hi Team From the title, I am entirely new to programming and i am interested in learning elixir but not sure where to start. Can someone...
New

Other popular topics Top

chrismccord
Phoenix 1.4.0 released Phoenix 1.4 is out! This release ships with exciting new features, most notably with HTTP2 support, improved deve...
688 30048 115
New
JakeBecker
TL;DR: I’ve just released an implementation of Microsoft’s IDE-independent Language Server Protocol for Elixir. It adds language support ...
1140 51847 244
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New
itssasanka
Hi all, Trying to get some more clarity over utc_datetime and naive_datetime for Ecto: https://hexdocs.pm/ecto/Ecto.Schema.html#module-...
New
chrismccord
This release brings a number of exciting features, including integration with the new Phoenix LiveDashboard and Phoenix LiveView. There h...
New
fayddelight
I tried installing elixir 1.11.2 erlang 23.3.4 via asdf in my zsh shell. Enabled the versions locally and globally. When I list them ...
New
vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New
johnnyicon
Hi all, I've just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I'm trying to use Postg...
New
Fl4m3Ph03n1x
About me? ( if you have nothing better to do than reading about some random guy in the internet :stuck_out_tongue: ) Hello all, this is ...
New

We're in Beta

About us Mission Statement