When you come to a .fork in a Process, take it.
A foible of Ruby forked processes is that children have copies of the parent process's objects. In the case of the ActiveRecord::Base.connection_pool, this means that usually when you try to start new database connections within a forked process, you won't.
When a complex database migration requires a lot of horsepower and raw SQL executes, rather than allowing ActiveRecord to take its course, you will likely benefit from forking the task. But you may also want to ensure that your database connections are unique to each process.
Much of the advice out there focuses on how important it is to teardown the existing connection before forking, have each process create its own connection, and then reset the old connection at the end. And this is good as far as it goes, but it's not the whole picture. If you simply remove the connection, and then establish a new connection in each process, the connection_pool is going to look the same to each process and thus the "new" connection each time will actually be the same one:
ActiveRecord::Base.remove_connection
4.times do
Process.fork do
ActiveRecord::Base.establish_connection
puts connection
ActiveRecord::Base.remove_connection
end
end
Process.waitall
ActiveRecord::Base.establish_connection
outputs:
#<ActiveRecord::ConnectionAdapters::MysqlAdapter:0x335e154> #<ActiveRecord::ConnectionAdapters::MysqlAdapter:0x335e154> #<ActiveRecord::ConnectionAdapters::MysqlAdapter:0x335e154> #<ActiveRecord::ConnectionAdapters::MysqlAdapter:0x335e154>
Not what we want. Now, there are, most likely, fancier and better ways of doing this, and it seems a little bit sketchy to have to roll one's own connection pool ... but I like things that work and are easy, so:
ActiveRecord::Base.remove_connection
pool = []
4.times do |i|
pool.push(ActiveRecord::Base.establish_connection)
Process.fork do
connection = pool[i].connection
puts connection
ActiveRecord::Base.remove_connection
end
end
Process.waitall
ActiveRecord::Base.establish_connection
And now we get:
#<ActiveRecord::ConnectionAdapters::MysqlAdapter:0x335ea28> #<ActiveRecord::ConnectionAdapters::MysqlAdapter:0x3351ddc> #<ActiveRecord::ConnectionAdapters::MysqlAdapter:0x3345140> #<ActiveRecord::ConnectionAdapters::MysqlAdapter:0x33384f4>
Perfect. And, yes, those connection objects are perfectly good and you can go ahead and call connection.execute, connection.select_value, etc. exactly as you normally would.
Comments