(#3581) Stop forking around: Add fork helper method #620

Merged
merged 1 commit into from Apr 10, 2012

Projects

None yet

4 participants

@kelseyhightower

Without this patch we are forking all over the place. All this forking
around is problematic when it comes to things like ActiveRecord and
database connections. It turns out that forking has a nasty side effect
of aggressively closing database connections and spewing errors all over
the place.

This patch introduces a new Puppet::Util#safe_posix_fork method that
does forking the right way (closing file descriptors, and resetting
stdin, stdout, and stderr). Tagmail, Puppet kick, and
Puppet::Util#execute_posix have been updated to make use of this new
functionality.

In the future, Puppet::Util#safe_posix_fork should be used in-place of
direct calls to Kernel#fork.

This patch includes related spec tests.

Kelsey Hightower (#3581) Stop forking around: Add fork helper method
Without this patch we are forking all over the place. All this forking
around is problematic when it comes to things like ActiveRecord and
database connections. It turns out that forking has a nasty side effect
of aggressively closing database connections and spewing errors all over
the place.

This patch introduces a new `Puppet::Util#safe_posix_fork` method that
does forking the right way (closing file descriptors, and resetting
stdin, stdout, and stderr). Tagmail, Puppet kick, and
`Puppet::Util#execute_posix` have been updated to make use of this new
functionality.

In the future, `Puppet::Util#safe_posix_fork` should be used in-place of
direct calls to `Kernel#fork`.

This patch includes related spec tests.
1b810b1
@stschulte
Member

Does it actually work and ActiveRecord runs correctly? I do not know if calling close is the right thing. And counting from 3 to 256 seems a bit arbitrary to me since a process can hold much more than 256 filehandles. But i stumbled over the following implementation I found here http://cmdrclueless.com/blog/2009/07/06/activerecord-fork-exec-boom/

require 'fcntl'

keepers = [STDIN,STDOUT,STDERR]
ObjectSpace.each_object(IO) do |io|
  if not io.closed? and not keepers.include?(io)
    flags = io.fcntl(Fcntl::F_GETFD, 0)
    io.fcntl(Fnctl::F_SETFD, Fcntl::FD_CLOEXEC | flags)
  end
end

But I dont know the real difference between the closeonexec flag and calling the "ruby close".

@kelseyhightower

@stschulte Thanks for the link, I'm going to update this pull and try something like that.

@slippycheeze

@stschulte - for the record, yes, that actually works, and is correct. (This is, in fact, what Ruby does when you use popen, or backticks, or any number of similar ways to fork and capture output.)

Using 3 through 256 is pretty much arbitrary, but it is more correct - we want to close all file handles, not just those exposed at the Ruby level as IO objects. The arbitrary part is that, indeed, you can have more than 256 handles, but - Puppet pretty much never should, and MRI has no portable mechanism to obtain the maximum file handle number.

Finally, CLOEXEC is a (recent) Linux-ism, and isn't available on many platforms.

@slippycheeze

@stahnma - it should be completely irrelevant in terms of the possible file descriptor leak, since that is in a single process, not across fork. The other two reflect the same thing - something is causing AR to leak connections. This isn't that - it would shut down the connection incorrectly instead. Sorry.

@slippycheeze slippycheeze merged commit 0b13a37 into puppetlabs:2.7.x Apr 10, 2012
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment