ixti / sidekiq-throttled

Concurrency and rate-limit throttling for Sidekiq
MIT License
710 stars 76 forks source link

Throttle the Queue instead of the Class #44

Closed pacMakaveli closed 4 years ago

pacMakaveli commented 6 years ago

Hi,

So far, in development, the gem has been working great! Great! However, once I shipped the changes in production I noticed I'm pretty much reaching my API limits within minutes. I initially thought the gem throttles the queue and it applies it on all classes that use the said queue.

For example, I have 5 Workers that are using the queue.

module Network::XBOX
  class GameSyncJob
    include Sidekiq::Worker
    include Sidekiq::Symbols
    include Sidekiq::Throttled::Worker

    sidekiq_options queue: :xbox_api, retry: 1, backtrace: false
    sidekiq_throttle({
      concurrency: { limit: 15 },
      threshold: {
        limit: 975,
        period: 1.hour
      }
    })

    def perform(args, options = {})
      ...
    end
  end
end
module Network::XBOX
  class IdentitySyncJob
    include Sidekiq::Worker
    include Sidekiq::Symbols
    include Sidekiq::Throttled::Worker

    sidekiq_options queue: :xbox_api, retry: 1, backtrace: false
    sidekiq_throttle({
      concurrency: { limit: 15 },
      threshold: {
        limit: 975,
        period: 1.hour
      }
    })

    def perform(args, options = {})
      ...
    end
  end
end

Which gives the following:

screen shot 2018-03-06 at 8 57 50 am

As you can see, instead of allowing ONLY 975/hour, it allows 975/hour/class .

Question: Is there a way to limit just the queue and apply the said limit to all of its classes?

Thanks!

ixti commented 6 years ago

Right now there's no way to throttle by queue - it's on my radar, but not built-in yet. But still you can do what you want with "shared" throttling strategies. Somewhere in your bootstrapping code (initializers if you are running on rails):

Sidekiq::Throttled::Registry.add(:xbox_api, {
  :concurrency => { :limit => 15 },
  :threshold => { :limit => 975, :period => 1.hour }
})

Then in your jobs you will be able to:

module Network::XBOX
  class GameSyncJob
    include Sidekiq::Worker
    include Sidekiq::Symbols
    include Sidekiq::Throttled::Worker

    sidekiq_options queue: :xbox_api, retry: 1, backtrace: false
    sidekiq_throttle_as :xbox_api

    def perform(args, options = {})
      ...
    end
  end
end

module Network::XBOX
  class IdentitySyncJob
    include Sidekiq::Worker
    include Sidekiq::Symbols
    include Sidekiq::Throttled::Worker

    sidekiq_options queue: :xbox_api, retry: 1, backtrace: false
    sidekiq_throttle_as :xbox_api

    def perform(args, options = {})
      ...
    end
  end
end
pacMakaveli commented 6 years ago

Thanks for the help! I'll give it a go tomorrow, when I deploy some new changes and see if that does the trick.

westonplatter commented 6 years ago

I found this really helpful. @ixti, you cool with adding this to the readme? If so, I'd be happy to add it.

ixti commented 6 years ago

@westonplatter sure, my friend!

Systho commented 6 years ago

Does this Sidekiq::Throttled::Registry.add(:xbox_api, {....}) trick works for jobs enqueued in different queues ?

More precisely, can I make an rule for :acme_api and share it between jobs enqueued both in :high_priority an :low_priority ?

ixti commented 6 years ago

@Systho Yes it will work. All jobs with sidekiq_throttle_as :acme_api will share same throttling bucket regardless of where they were enqueued to.