How can Nginx be upgraded without dropping any requests? How can Nginx be upgraded without dropping any requests? nginx nginx

How can Nginx be upgraded without dropping any requests?


For specifics: http://www.csc.villanova.edu/~mdamian/Sockets/TcpSockets.htm describes the C library for TCP sockets.

I think the key is that after a process forks while holding a socket file descriptor, the parent and child are both able to call accept() on it.

So here's the flow. Nginx, started normally:

  1. Calls socket() and bind() and listen() to set up a socket, referenced by a file descriptor (integer).
  2. Starts a thread that calls accept() on the file descriptor in a loop to handle incoming connections.

Then Nginx forks. The parent keeps running as usual, but the child immediately execs the new binary. exec() wipes out the old program, memory, and running threads, but inherits open file descriptors: see http://linux.die.net/man/2/execve. I suspect the exec() call passes the number of the open file descriptor as a command line parameter.

The child, started as part of an upgrade:

  1. Reads the open file descriptor's number from the command line.
  2. Starts a thread that calls accept() on the file descriptor in a loop to handle incoming connections.
  3. Tells the parent to drain (stop accept()ing, and finish existing connections), and to die.


I have no idea how nginx does it, but basically, it could just exec the new binary, carrying the listening socket with it the new process (actually, it remains the same process, it just replaces the program executing in it). The listening socket has a backlog of incoming connections, and as long as it's fast enough to boot up, it should be able to start processing them before it overflows. If not, it could probably fork first, exec, and wait for it to boot up to the point where it's ready to process incoming requests, then hand over the command of the listening socket (file descriptors are inherited when forking, both have access to it) via some internal mechanism, before exiting. Noting your observations, this looks like what it's doing (if your parent process dies, your ppid is reassigned to init, i.e. pid 1)

If it has multiple processes competing to accept on the same listening socket (again, I have no idea how nginx does it, perhaps it has a dispatching process?), then you could replace them one by one, by ordering them to exec the new program, as above, but one at a time, as to never drop the ball. Note that during such a process there would never be any new pids or parent/child relationship changes.

At least, I think that's probably how I would do it, off the top of my head.