forkbreak / fork_break

Fork with breakpoints, for testing multiprocess behaviour.
MIT License
61 stars 6 forks source link

Parent process database reconnection error #4

Closed calonso closed 11 years ago

calonso commented 11 years ago

Hi Petter.

I write a test case just like those in your examples

it '...' do
  p1, p2 = 2.times.map do |i|
    ForkBreak::Process.new do |bps|
    ActiveRecord::Base.connection.reconnect!

    obj = A.new
    original = obj.method(:model_method)
    A.any_instance.stub(:model_method) do |*args|
      bps << :before_method
      original.call(*args)
      bps << :after_method
    end

    obj.model_method
  end

  p1.run_until(:after_method).wait

  p2.run_until(:before_method).wait
  p2.run_until(:after_method) && sleep(0.1)

  p1.finish && sleep(0.1)
  p2.finish.wait

  # Here is where the exception comes!!
  ActiveRecord::Base.connection.reconnect! 
end

And the arised exception is

PG::Error: server closed the connection unexpectedly
This probably means the server terminated abnormally
before or while processing the request.

Please help!!

Thanks

calonso commented 11 years ago

Any info on this?

remen commented 11 years ago

Sorry, I've been busy with work and family. I'll see if I have time to look at it today or tomorrow. From the error message I would say that it has something to do with postgresql not being happy with reestablishing a connection after the fork.

calonso commented 11 years ago

Hi Petter. How is it going? I'm really looking forward to be able to use your gem to test my project. Could you have a look at it? Thank you

calonso commented 11 years ago

Hi Petter.

I found something. I got a fully running test by disconnecting parent process database connection before forking. In each forked process I run establish_connection, and in the last line of each I disconnect it's database connection too. Afterwards, instead of reconnecting the parent, I run establish_connection too and everything seems to be working fine.

Summarizing, what I think I'm doing is disconnect parent process database connection before forking. Establishing a new databse connection for each of the forked processes and disconnecting it just before finishing it. Finally establish a new connection for the parent process to check the results.

remen commented 11 years ago

Hey calonso,

I finally had some time to look at it... Sorry for the long wait.

Yes, you are indeed right! When using postgresql, we need to disconnect the parent connection before forking. More information here: https://devcenter.heroku.com/articles/forked-pg-connections

I don't think you need to disconnect at the end of the children processes since they will terminate anyway. But it can't hurt.

So, to summarize; the error is in the blog post example, not the gem itself. Updating the blog post now.

Many thanks for finding the issue and solution!

pedrocarrico commented 10 years ago

This also happens with MySQL.

I was getting some random "MySQL server has gone away" errors throughout my test suite so after reading the mysql documentation (http://dev.mysql.com/doc/refman/5.0/en/gone-away.html): "You can also encounter this error with applications that fork child processes, all of which try to use the same connection to the MySQL server. This can be avoided by using a separate connection for each child process."

And so I also can confirm that the solution for MySQL is the same.

Cheers!

remen commented 10 years ago

Thanks for the update @pedrocarrico!