∴ikura

Hot routes with cowboy

When evolving a never-should-stop OTP application
that talks to the world via the web, there’s a corner
case with the popular
cowboy web server
that needs some explanation.

Cowboy routes are mappings from request URLs to
handler code, and this configuration is typically
located in the parent application module for that
OTP application.

Not all systems, especially web applications, need to
be upgraded without being taken out of service. But at
ikura, we need it. We do ‘relups’ all the time: we
don’t take our service down whenever updates are pushed
live.

With cowboy, loading new route-mappings is built right
into the library. But, there’s a nice pattern we use at
ikura so that this is never a manual process.

Not entirely obvious to some, this is how we accomplish
updating new routes during a real-time release upgrade.

Getting familiar

First, let’s take a look at the procedure for
updating the cowboy routes when done manually.

Taking for granted what the routes actually are, we
will just gloss over that term and say it lives in the
routes/0 routine for the moment. We will pretend this
routine lives in — and is exported from — a module named
‘lava_web_app.erl.’ We can access it via the Erlang shell
as follows:

You’ll notice that we are serving four routes: one static, and
three handled by a made-up handler lava_web_html_handler.

Relup & snappy routing updates

If we were to add a mapping to our routes, in order to get our cowboy
web server to acknowledge the new resource, we’d have to run those
three Erlang expressions earlier after tweaking the ‘routes’ routine.
In a hot-code release update, it’s incredibly important to automate
this. It’s simply not going to cut it to attach to a shell and do
this by hand.

The way to handle this is via our application’s ‘appup’ file. So as
we migrate to a new version, we explicitly want to:

load the new ‘lava_web_app.erl’ file, which includes the updated routes/0 routine;

instruct cowboy to adjust the routes.

There’s no better place than ‘lava_web_app.erl’ to house our routing
update code, so let’s write & export a routine called update_routes/0.

With this exported routine, we’re now able to connect all the dots
for a ‘relup.’

We’re going to keep things simple and assume the only new code in
our new release is that of a new route. Everything else will be the
same, even though in a normal circumstance, we would expect the
handler to be sporting new code as well. But let’s just take that
for granted. So with that assumption made, here’s the updated
routes/0 routine, as well as
‘lava_web.appup.src’ which contains the instructions on how to
deal with migrating to & from the old version.

Minding Ps & Qs

As with any target release upgrade, there are quite a few
things that can go wrong. Therefore, if you have a system
that can go off-line for a moment during a system upgrade,
then do yourself a favor: bypass ‘relups’ altogether.

For those who must maintain uptime during upgrades, there’s
really nothing to fear if you stay cool headed. Just be sure
to try things out in mock runs, make a checklist that’s easy
to follow & deploy with confidence. There’s no better feeling
than a ‘relup’ gone right!