How to implement simple queue limiting/throttling for Sidekiq

The sidekiq-rate-limiter Archive gem allows rate-limiting Sidekiq jobs and works like a charm. However, it needs to be integrated on a per-worker basis.

If you want to limit a whole queue instead, and if your requirements are simple enough, you can do it via a Sidekiq middleware Archive yourself.

Here is an example that limits concurrency of the "mailers" queue to 1. It uses a database mutex via the with_advisory_lock Archive gem but you could easily use something else.

class SidekiqMailersThrottle
  def call(worker, job_options, queue)
    if queue == 'mailers'
      ApplicationRecord.with_advisory_lock('mailers-throttle') do
        yield
      end
    else
      yield
    end
  end
end

Sidekiq.configure_server do |config|
  config.server_middleware do |chain|
    chain.add SidekiqMailersThrottle
  end
end

Some hints:

  • Using this approach, workers will be blocked while jobs are waiting for the mutex to free up. The sidekiq-rate-limiter gem re-queues blocked jobs so that other jobs can be worked off in the meantime (which is smart). Depending on your use case, blocking workers might or might not be an issue.
  • To increase concurrency, you might do something as simple as naming your lock "mailers-throttle-#{rand(4)}" (not perfect, but might be good enough).
  • For rate-limiting (e.g. only 1 job per 5 seconds) you could name it "mailers-throttle-#{(Time.now.to_i / 5.0).to_i}".

Also note that Sidekiq allows configuring queue weights Archive . This is a different approach and will not throttle a queue, but instead allow some queues to be picked up more often than others.
Finally, it might be an option to spawn a separate Sidekiq process to work off only your a specific queue, and limit its worker count.

Arne Hartherz almost 2 years ago
This website uses short-lived cookies to improve usability.
Accept or learn more