Advanced Options

Mike Perham edited this page Mar 1, 2017 · 105 revisions

The Sidekiq Configuration File

The Sidekiq configuration file is a YAML file that Sidekiq server uses to configure itself, by default located at config/sidekiq.yml. It is only necessary to create the file if you need to set advanced options, such as concurrency pool size, named queues, PID file location, etc. Here is an example configuration file:

---
:concurrency: 5
:pidfile: tmp/pids/sidekiq.pid
staging:
  :concurrency: 10
production:
  :concurrency: 20
:queues:
  - default
  - [myqueue, 2]

Note the use of environment-specific subsections. These values will override top-level values. If you don't use the default location, use the -C flag to tell Sidekiq where the file is:

sidekiq -C config/myapp_sidekiq.yml

Options passed on the command line will also override options specified in the config file.

Queues

By default, sidekiq uses a single queue called "default" in Redis. If you want to use multiple queues, you can either specify them as arguments to the sidekiq command, or set them in the Sidekiq configuration file. Each queue can be configured with an optional weight. A queue with a weight of 2 will be checked twice as often as a queue with a weight of 1:

As arguments...

sidekiq -q critical,2 -q default

In the configuration file...

# ...
:queues:
  - [critical, 2]
  - default

If you want queues always processed in a specific order, just declare them in order without weights:

As arguments...

sidekiq -q critical -q default -q low

In the configuration file...

# ...
:queues:
  - critical
  - default
  - low

This means that any job in the default queue will be processed only when the critical queue is empty.

You can get random queue priorities by declaring each queue with a weight of 1, so each queue has an equal chance of being processed:

# ...
:queues:
  - ["foo", 1]
  - ["bar", 1]
  - ["xyzzy", 1]

You can specify a queue to use for a given worker by declaring it:

class ImportantWorker
  include Sidekiq::Worker
  sidekiq_options queue: 'critical'

  def perform(*important_args)
    puts "Doing critical work"
  end
end

I don't recommend having more than a handful of queues. Lots of queues makes for a more complex system and Sidekiq Pro cannot reliably handle multiple queues without polling. M Sidekiq Pro processes polling N queues means O(M*N) operations per second slamming Redis.

Reserved Queues

If you'd like to "reserve" a queue so it only handles certain jobs, the easiest way is to run two sidekiq runners each handling different queues:

sidekiq -q critical # Only handles jobs on the "critical" queue
sidekiq -q default -q low # Handles the other jobs

Workers

Sidekiq offers a number of options for worker behavior.

  • queue : use a named queue for this Worker, default 'default'
  • retry : enable the RetryJobs middleware for this Worker, default true. Alternatively, you can specify the max. number of times a job is retried (ie. :retry => 3)
  • backtrace : whether to save any error backtrace in the retry payload to display in web UI, can be true, false or an integer number of lines to save, default false. Be careful, backtraces are big and can take up a lot of space in Redis if you have a large number of retries.
class HardWorker
  include Sidekiq::Worker
  sidekiq_options :queue => :crawler, :retry => false, :backtrace => true
  
  def perform(name, count)
  end
end

Default worker options can be set using Sidekiq.default_worker_options=:

Sidekiq.default_worker_options = { 'backtrace' => true }

Concurrency

You can tune the amount of concurrency in your sidekiq process. By default, one sidekiq process creates 25 threads. If that's crushing your machine with I/O, you can adjust it down:

sidekiq -c 10

Don't set the concurrency higher than 50. I've seen stability issues with concurrency of 100, for example. Note that ActiveRecord has a connection pool which needs to be properly configured in config/database.yml to work well with heavy concurrency. Set the pool setting to something close or equal to the number of threads:

production:
  adapter: mysql2
  database: foo_production
  pool: 25

Heroku

See this great writeup on StackOverflow on how to get Sidekiq working in Heroku.

Rails 4.1 and newer

As of Rails 4.1, the config/database.yml file will be merged with the DATABASE_URL environment variable. With that change, Heroku stopped generating a custom config/database.yml file for you. Use the config/database.yml file for custom configuration.

Rails 4.0 and older

If you're running on Heroku, you can't rely on the config/database.yml as that platform relies on the DATABASE_URL environment variable to determine the database connection configuration. Heroku overwrites the database.yml during slug compilation so that it reads from DATABASE_URL.

See Heroku Concurrency and DB Connections for Active Record connection pool configuration on Heroku.

Rails 3.2 and newer

As of Rails 3.2, ActiveRecord's initialization code will prefer a DATABASE_URL over the database.yml file. (See Issue #503 for a more complete explanation.) You can set a custom connection pool size for the Sidekiq server process via:

Sidekiq.configure_server do |config|
  database_url = ENV['DATABASE_URL']
  if database_url
    ENV['DATABASE_URL'] = "#{database_url}?pool=25"
    ActiveRecord::Base.establish_connection
    # Note that as of Rails 4.1 the `establish_connection` method requires
    # the database_url be passed in as an argument. Like this:
    # ActiveRecord::Base.establish_connection(ENV['DATABASE_URL'])
  end
end

When we detect a DATABASE_URL environment variable we tack the new pool size onto it (this only affects the current process's environment variable). Then we force ActiveRecord::Base to re-establish a new connection with the database using the new pool size.

Connection Pooling

sidekiq includes the connection_pool gem which your Workers can use. With a connection pool, you can share a limited number of I/O connections among a larger number of threads.

class HardWorker
  include Sidekiq::Worker

  MEMCACHED_POOL = ConnectionPool.new(:size => 10, :timeout => 3) { Dalli::Client.new }
  def perform(args)
    MEMCACHED_POOL.with do |dalli|
      dalli.set('foo', 'bar')
    end
  end
end

This ensures that even if you have lots of concurrency, you'll only have 10 connections open to memcached per Sidekiq process.

Previous: Error Handling Next: Scheduled Jobs