How to use a different ActiveRecord connection pool between Unicorn and Sidekiq?

Première publication : 2014-06-02

I work on a Rails (4.1) application, sitting behind Unicorn and backed by a couple of Sidekiq processes.

A quick reminder : Unicorn is an application server (for Rack-compatible Ruby applications) based on the multi-process master-workers model, and Sidekiq is a background processing based on the multi-threaded model.

The jobs we put in the Sidekiq queue are also multi-threaded and need to access the database behind ActiveRecord in parallel. It forces us to have a bigger connection pool than usual : 20 instead of the default 5.

But the main part of the application, which run solely inside Unicorn, doesn’t need to use that much connections. In fact we have 16 Unicorn workers, so the bigger the pool is the more connections are opened.

Even if they are mainly idle, it’s still a waste of resource and it make the supervision of resources consumption more difficult.

Here how I’ve managed to have a separate pool size for Sidekiq and Unicorn.

Unicorn

A typical Unicorn configuration contains something like this

before_fork do |server, worker|
  # the following is recommended for Rails + "preload_app true"
  # as there's no need for the master process to hold a connection
  if defined?(ActiveRecord::Base)
    ActiveRecord::Base.connection.disconnect!
  end
end

after_fork do |server, worker|
  if defined?(ActiveRecord::Base)
    ActiveRecord::Base.establish_connection
  end
end

Before forking, the master process close all its connections, and after forking, every worker re-open their connections.

ActiveRecord::Base.establish_connection can be called with a connection “name” if there is something by that name in the config/database.yml file.

Let’s create such a configuration, by overriding only what’s necessary :

production: &production
  adapter: mysql2
  host: localhost
  port: 3306
  database: database_name
  username: my_app
  password: password
  encoding: utf8
  reconnect: true
  pool: 5

production_unicorn:
  <<: *production
  username: my_app_unicorn
  pool: 5

You can see that I’ve kept the same pool setting’s default value. That way It’s easier to have a Unicorn setting different from the default setting, used when I run Rake tasks…

I’ve also changed the username because I want to differentiate them when looking at the database opened connections. It’s completely optional.

Then we nee to configure Unicorn to use that new configuration :

after_fork do |server, worker|
  if defined?(ActiveRecord::Base)
    spec = "#{Rails.env}_unicorn"
    if Rails.application.config.database_configuration.key?(spec)
      ActiveRecord::Base.establish_connection(spec.to_sym)
    else
      ActiveRecord::Base.establish_connection
    end
  end
end

Sidekiq

For Sidekiq it’s quite similar. We begin by adding another override in the config/database.yml file :

production_sidekiq:
  <<: *production
  username: my_app_sidekiq
  pool: 20

NB : I set Sidekiq up in the config/initializers/sidekiq.rb file, but you can also put this in many other places. Consult Sidekiq’s documentation for more details.

Let’s use our custom connection inside Sidekiq :

Sidekiq.configure_server do |config|
  if defined?(ActiveRecord::Base)
    spec = "#{Rails.env}_sidekiq"
    if Rails.application.config.database_configuration.key?(spec)
      ActiveRecord::Base.establish_connection(spec.to_sym)
    else
      ActiveRecord::Base.establish_connection
    end
  end
end