Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unicorn! and PostgreSQL

I am in the middle of upgrading an old Rails and PostgreSQL app from 2.1 to Rails 3 (via a successful intermediate 2.3.11 step).

Finally, when I got all the Rspecs running smoothly, I installed Mongrel 1.2.0.pre2 and got it up and running - all OK. Even the JMeter test case with 100 concurrent users passed OK.

But when I tried the same with Unicorn! server, it works fine when I am using it myself, but when I get it under load with JMeter, it starts giving 100% errors from the moment, where JMeter tries to POST a login. At the same time when I check some specific page, it gives me weird errors like this:

undefined method `eq' for nil:NilClass

At the terminal I get the following output (but only a few times):

message type 0x43 arrived from server while idle
message type 0x5a arrived from server while idle
WARNING:  there is already a transaction in progress

EDIT: this seems to be an issue with shared PGconnect object, so I removed some of previous text

I found that PostgreSQL docs specify that PGconn object must not be shared between threads making concurrent requests.

Is Unicorn! or AR mixing requests from different threads in the same PGconn object?

Database.yml:

production:
  adapter: postgresql
  encoding: unicode
  database: ***
  username: ***
  password: ***
  host: 127.0.0.1

I even tried to add this to no avail:

  allow_concurrency: true

unicorn.conf:

worker_processes 8
working_directory "/full/path/to/app"
listen '/tmp/app.sock', :backlog => 512
timeout 30
pid "/full/path/to/app/tmp/pids/puppetmaster_unicorn.pid"

preload_app true
  if GC.respond_to?(:copy_on_write_friendly=)
  GC.copy_on_write_friendly = true
end

Specs:

  • Rails 3.0.6 (because of deployment env)
  • Unicorn! 4.1.1
  • Nginx 1.0.5
  • pg gem 0.11.0
  • PostgreSQL 9.0.4
  • Mac OS X 10.7.2

If Rails version should be the culprit, of course I can upgrade to the latest. I just started off with what the service provider has.

like image 557
Laas Avatar asked Nov 24 '11 21:11

Laas


1 Answers

It's critical not to share database connections between processes. When you run Rails with preload_app Rails will establish the AR connection(s) before the unicorn master forks the workers. The example unicorn.conf.rb has a recommendation to disconnect the AR connection in the before_fork hook. AR will automatically reestablish a fresh connection as each worker requires it post-fork.

excerpt from http://unicorn.bogomips.org/examples/unicorn.conf.rb:

before_fork do |server, worker|
  # the following is highly recomended for Rails + "preload_app true"
  # as there's no need for the master process to hold a connection
  defined?(ActiveRecord::Base) and
    ActiveRecord::Base.connection.disconnect!
  # ...
end
like image 99
dbenhur Avatar answered Nov 14 '22 04:11

dbenhur