Background jobs with #fork

Eric Wong normalperson at yhbt.net
Thu Apr 12 20:39:56 UTC 2012


paddor <paddor at gmail.com> wrote:
> Hi
> 
> I've migrated from Passenger to Unicorn about a week ago. It's great.
> Great transparency and management, thanks for this great software!

:>

> A few of my Rails applications start background jobs using
> Kernel#fork. Of course, the ActiveRecord connections are closed and
> reopened again in the parent and child processes. The child process
> also does its job.

OK, that's good.

> Unfortunately, it seems that the parent (a Unicorn worker) waits for
> the child (background job) to finish before serving any new requests.
> Process.detach is done in the child. Process.setsid is not done. The
> child's STDOUT, STDERR and the Rails logger are redirected to their
> own files right after forking.

So you're only calling fork and not exec (or system/popen, right?)  It
may be the case that the client socket is kept alive in the background
process.

The client socket has the close-on-exec flag (FD_CLOEXEC) set, but
there's no close-on-fork flag, so you might have to find + close it
yourself.  Here's is a nasty workaround for the child process:

  ObjectSpace.each_object(Kgio::Socket) do |io|
    io.close unless io.closed?
  end

> The problem persists even when multiple workers are started. And the
> problem was not present in the old setup with Passenger.
> 
> My question: Does Unicorn somehow check/wait for child processes
> forked by the worker processes?

Unicorn workers do not explicitly wait on child processes themselves,
unicorn workers set: trap(:CHLD,"DEFAULT") after forking, even (the
unicorn master must handle SIGCHLD, of course)

The difference between nginx+unicorn and Passenger is probabably: nginx
relies on unicorn generating an EOF to signal the end-of-response (nginx
<-> unicorn uses HTTP/1.0), this I'm sure about.  I think Passenger uses a
protocol which can signal the end-of-request inline without relying on
an EOF on the socket (Hongli can correct me on this if I'm wrong).

However, nginx can still forward subsequent requests to the same unicorn
(even the same unicorn worker), because as far as the unicorn worker is
concerned (but not the OS), it's done with the original request.  It's
just the original request (perhaps the original client) is stuck
waiting for the background process to finish.

I can probably writeup a better explanation (perhaps on the usp.ruby
(Unix Systems Programming for Ruby mailing list) if this doesn't make
sense.


More information about the mongrel-unicorn mailing list