Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fork, Ruby, ActiveRecord and File Descriptors on Fork

I understand that when we fork a process the child process inherits a copy of the parents open file descriptors and offsets. According to the man pages this refers to the same file descriptors used by the parent. Based on that theory in the following program


puts "Process #{Process.pid}"

file = File.open('sample', 'w')

forked_pid = fork do
    sleep(10)
    puts "Writing to file now..."
    file.puts("Hello World. #{Time.now}")       
end




file.puts("Welcome to winter of my discontent #{Time.now}")
file.close
file = nil

Question 1: Shouldn't the forked process which is sleeping for 10 seconds lose its file descriptor and not be able to write to the file as the parent process completes and closes the file and exits.
Question 2: But for whatever reason if this works then how does ActiveRecord lose its connection in this scenario. It only works if I set :reconnect => true on ActiveRecord connect can it actually connect, which means its losing connection.


require "rubygems"
require "redis"
require 'active_record'
require 'mysql2'

connection = ActiveRecord::Base.establish_connection({
    :adapter =&gt 'mysql2',
    :username =&gt 'root_user',
    :password =&gt 'Pi',
    :host =&gt 'localhost',
    :database => 'list_development', 
    :socket =&gt '/var/lib/mysql/mysql.sock'

    })

class User &lt ActiveRecord::Base   
end

u = User.first

puts u.inspect

fork do
    sleep 3
    puts "*" * 50
    puts User.first.inspect
    puts "*" * 50

end

puts User.first.inspect

However, the same is not true with Redis (v2.4.8) which does not lose connection on a fork, again. Does the it try to reconnect internally on a fork?

If thats the case then why isn't the write file program not throwing an error.

Could somebody explain whats going on here. Thanks

like image 619
Sid Avatar asked Oct 26 '12 15:10

Sid


People also ask

What happens to file descriptors when you fork?

When a fork() is performed, the child receives duplicates of all of the parent's file descriptors. These duplicates are made in the manner of dup(), which means that corresponding descriptors in the parent and the child refer to the same open file description.

Does fork share file descriptors?

File descriptors are generally unique to each process, but they can be shared by child processes created with a fork subroutine or copied by the fcntl, dup, and dup2 subroutines.

What happens within a parent process when a child closes a file descriptor inherited across a fork?

When a child is forked then it inherits parent's file descriptors, if child closes the file descriptor what will happen ? It inherits a copy of the file descriptor. So closing the descriptor in the child will close it for the child, but not the parent, and vice versa.


1 Answers

If you close a file descriptor in one process it stays valid in the other process, this is why your file example works fine.

The mysql case is different because it's a socket with another process at the end. When you call close on the mysql adapter (or when the adapter gets garbage collected when ruby exits) it actually sends a "QUIT" command to the server saying that you're disconnecting, so the server tears down its side of the socket. In general you really don't want to share a mysql connection between two processes - you'll get weird errors depending on whether the two processes are trying to use the socket at the same time.

If closing a redis connection just closes the socket (as opposed to sending a "I'm going away " message to the server) then the child connection should continue to work because the socket won't actually have been closed

like image 81
Frederick Cheung Avatar answered Oct 28 '22 21:10

Frederick Cheung