Common Lisp websites - Is it possible to make changes without downtime?

108 views Asked by At

Right now, my main website is a CL executable behind an nginx proxy.

When I make changes, I have to create a new executable. Stop, the old one and restart the new one. As my traffic grows, this is concerning.

Is there a better buils system approach that doesn't have me stopping the image?

3

There are 3 answers

1
Svante On

You can minimize downtime by running the new executable on a different port, then switch the port in nginx, and only then stopping the old one.

You could also try to organize your code in such a way that you can reliably load/reload/unload only parts to transform your running image to a defined target state, but I am not aware of a systematic approach to that. There have been people who did this on the fly, but I think in general, one should try to make things reproducible.

1
Ehvince On

The Common Lisp way would be to create a Swank server from your executable, connect to it from your Slime at home, and load the new code -to update the running instance.

Hints: https://lispcookbook.github.io/cl-cookbook/debugging.html#remote-debugging

It isn't as solid as deploying a whole new binary, but if you don't develop new code in production without local checkouts, it should be reproducible.

It is anyways a useful method to poke data on the production instance, or update settings without a restart.

0
coredump On

Spawning a new server and switching the port is the most robust solution. If you want to update the live server at least you need to have a staging one to test deployment first, because if you redefine the live image too much this can cause problems. And you might need to migrate sessions between servers: this step is easier to do in a the same process I think than by restarting another server.

Anyway, you should take defensive measures to cleanly update your code.

Let's say I define this package:

(defpackage :production (:use :web-api) 
  (:export #:start #:stop #:shutdown #:*server*))

The start method in this package starts an infinite loop and binds *server* to the webserver instance. The shutdown method denies incoming requests but still processes old ones. Finally stops terminates the server.

If you have a new version of your server, you can do:

(rename-package :production :old)
(rename-package :web-api :old-web-api)

Normally the system should still be running.

If you now load the newest version of the code, with ql:quickload or asdf:make, you'll have new packages that should not interfere with the existing ones.

You can start the new server on a different port, access the database, etc. and possible copy state from the old server to the new one, shutdown the previous server, etc.

Finally you can delete the old packages.

What I want to outline is that live updating without taking steps in advance to avoid problems is not very safe, live coding can have various effects that are not seen when loading a new state from scratch. But if you do it well you can actually benefit from the dynamic nature of Lisp to e.g. change-class your objects or gracefully introduce new modules without disrupting the running instance.