Account

Log in (OpenID enabled)

Hot code swapping for servers not written in Erlang

Category: Internet, Programming, Python  

The recent server benchmark posted here comparing Erlang, Haskell, and Python understandably upset many people. Today we will move away from polarizing benchmarks and more about features.

Even though Erlang may not be the fastest language, it still has at least one great feature: hot swapping code without restarting the server. This is something that every long running server should have, but unfortunately it comes at the cost of performance, and some languages simply are not built to support it.

However, here is a way to emulate it for a web server, thanks to techniques borrowed from Nginx.

Here are the steps:

  1. Use the spawn family of system calls (ex. spawnl spawnvp) with the flag P_NOWAIT to execute itself. This new process has access to the open sockets.
  2. The new process should open a predetermined file containing the file descriptors of the sockets it needs to use saved by the old process.
  3. Start calling “accept” on the same listening sockets that the old process was accepting.
  4. Signal the old process to stop accepting, and to quit when all requests are finished.

While Python has a rudimentary reload, it may still be preferable to use this method as it greatly simplifies the upgrade logic. Upgrading functions piecewise may lead to corruption of logic and data.

Here is an example in Python that reloads itself in a loop using this method. It saves the file descriptors for 2 connected sockets which allows the new process to signal the old one at will. Simply run the program, and then edit the print statements below to watch it change.

import os
import time
import socket
import sys
import fcntl
import _multiprocessing
from multiprocessing import Pipe
 
# global pipe variable
pipe       = None
spare_pipe = None
 
def PipeFromFD(fd):
    return _multiprocessing.Connection(fd)
 
def recordPipe(p1, p2):
    with open('fd.txt', 'w') as f:
        f.write('%s,%s' % (p1.fileno(), p2.fileno()))
 
def upgrade():
    os.spawnlp(os.P_NOWAIT, 'python', 'python', sys.argv[0], '-upgrade')
 
def upgradeBootstrap():
    global pipe, spare_pipe
    with open('fd.txt', 'r') as f:
        p1, p2 = (int(fd) for fd in f.read().split(','))
        pipe = PipeFromFD(p1)
        spare_pipe = PipeFromFD(p2)
 
    recordPipe(spare_pipe, pipe)
    pipe.send("I am upgraded!")
 
if __name__ == '__main__':    
 
    if '-upgrade' in sys.argv:
        upgradeBootstrap()
    else:
        pipe, spare_pipe = Pipe()
        recordPipe(spare_pipe, pipe)
 
    pid = os.getpid()
    print '%i: Version 1. Upgrade in 5 seconds.' % pid
    time.sleep(5)
    print '%i: Upgrading' % pid
    upgrade()
    print '%i: Message from new process: %s' % (pid, pipe.recv())
    print '%i: Upgrade Complete. Exiting' % pid

Example Output

user@test:/mnt/shared$ python upgrade.py
22930: Version 1. Upgrade in 10 seconds.
22930: Upgrading
22971: Version 2. Upgrade in 10 seconds.
22930: Message from new process: I am upgraded!
22930: Upgrade Complete. Exiting
user@test:/mnt/shared$ 22971: Upgrading
23025: Version 3. Upgrade in 10 seconds.
22971: Message from new process: I am upgraded!
22971: Upgrade Complete. Exiting
killall python
  • Reddit
  • HackerNews
  • Twitter
  • DZone
  • del.icio.us
  • FriendFeed
  • StumbleUpon
  • RSS

Related posts:

  1. Debunking the Erlang and Haskell hype for servers
  2. Clearing Passwords in Memory with Python
  3. Host Your Own Email: Easy Disposable Addresses
  4. Patching a Program Without Source Code: How to be like the Skype Hacker for Newbies
  5. A Better Python Reload

2 Comments  »

  1. I am not sure what is the point you want to make here. Spawning a new process always read the latest copy from the disk. That is not a discovery. What erlang provides is hot code loading into the currently running VM with no special code.

    • admin says:

      The point here is that it is possible to seamlessly transfer control of the sockets to a new process. Therefore to a client, it appears as though nothing has changed.

RSS feed for comments on this post, TrackBack URI

Leave a Comment

(Cookies must be enabled)