sidekiq-throttled 0.13.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +12 -16
  3. data/.rubocop.yml +14 -94
  4. data/.rubocop_todo.yml +42 -31
  5. data/Appraisals +14 -10
  6. data/CHANGES.md +65 -0
  7. data/Gemfile +7 -5
  8. data/LICENSE.md +2 -0
  9. data/README.md +42 -26
  10. data/Rakefile +1 -1
  11. data/gemfiles/sidekiq_6.0.gemfile +7 -5
  12. data/gemfiles/sidekiq_6.1.gemfile +7 -5
  13. data/gemfiles/{sidekiq_5.0.gemfile → sidekiq_6.2.gemfile} +8 -6
  14. data/gemfiles/{sidekiq_5.1.gemfile → sidekiq_6.3.gemfile} +8 -6
  15. data/gemfiles/{sidekiq_5.2.gemfile → sidekiq_6.4.gemfile} +8 -6
  16. data/gemfiles/sidekiq_6.5.gemfile +33 -0
  17. data/lib/sidekiq/throttled/communicator/callbacks.rb +8 -10
  18. data/lib/sidekiq/throttled/communicator/exception_handler.rb +25 -0
  19. data/lib/sidekiq/throttled/communicator/listener.rb +4 -4
  20. data/lib/sidekiq/throttled/communicator.rb +1 -1
  21. data/lib/sidekiq/throttled/expirable_list.rb +2 -5
  22. data/lib/sidekiq/throttled/fetch/unit_of_work.rb +7 -2
  23. data/lib/sidekiq/throttled/fetch.rb +5 -1
  24. data/lib/sidekiq/throttled/job.rb +128 -0
  25. data/lib/sidekiq/throttled/queue_name.rb +1 -1
  26. data/lib/sidekiq/throttled/registry.rb +0 -5
  27. data/lib/sidekiq/throttled/strategy.rb +15 -17
  28. data/lib/sidekiq/throttled/strategy_collection.rb +69 -0
  29. data/lib/sidekiq/throttled/utils.rb +1 -1
  30. data/lib/sidekiq/throttled/version.rb +1 -1
  31. data/lib/sidekiq/throttled/web/stats.rb +5 -4
  32. data/lib/sidekiq/throttled/web/summary_fix.rb +1 -1
  33. data/lib/sidekiq/throttled/web/throttled.html.erb +6 -2
  34. data/lib/sidekiq/throttled/web.rb +1 -1
  35. data/lib/sidekiq/throttled/worker.rb +6 -121
  36. data/lib/sidekiq/throttled.rb +5 -3
  37. data/rubocop/layout.yml +24 -0
  38. data/rubocop/lint.yml +41 -0
  39. data/rubocop/metrics.yml +4 -0
  40. data/rubocop/performance.yml +25 -0
  41. data/rubocop/rspec.yml +3 -0
  42. data/rubocop/style.yml +84 -0
  43. data/sidekiq-throttled.gemspec +8 -4
  44. metadata +27 -16
@@ -5,10 +5,7 @@ source "https://rubygems.org"
5
5
  gem "appraisal"
6
6
  gem "rake"
7
7
  gem "rspec"
8
- gem "rubocop", "~> 0.82.0", require: false
9
- gem "rubocop-performance", "~>1.5.2", require: false
10
- gem "rubocop-rspec", "~> 1.39.0", require: false
11
- gem "sidekiq", "~> 5.1.0"
8
+ gem "sidekiq", "~> 6.3.0"
12
9
 
13
10
  group :development do
14
11
  gem "byebug"
@@ -20,12 +17,17 @@ end
20
17
  group :test do
21
18
  gem "apparition"
22
19
  gem "capybara"
23
- gem "coveralls", require: false
24
20
  gem "puma"
25
21
  gem "rack-test"
26
- gem "simplecov"
27
22
  gem "sinatra"
28
23
  gem "timecop"
29
24
  end
30
25
 
26
+ group :lint do
27
+ gem "rubocop", require: false
28
+ gem "rubocop-performance", require: false
29
+ gem "rubocop-rake", require: false
30
+ gem "rubocop-rspec", require: false
31
+ end
32
+
31
33
  gemspec path: "../"
@@ -5,10 +5,7 @@ source "https://rubygems.org"
5
5
  gem "appraisal"
6
6
  gem "rake"
7
7
  gem "rspec"
8
- gem "rubocop", "~> 0.82.0", require: false
9
- gem "rubocop-performance", "~>1.5.2", require: false
10
- gem "rubocop-rspec", "~> 1.39.0", require: false
11
- gem "sidekiq", "~> 5.2.0"
8
+ gem "sidekiq", "~> 6.4.0"
12
9
 
13
10
  group :development do
14
11
  gem "byebug"
@@ -20,12 +17,17 @@ end
20
17
  group :test do
21
18
  gem "apparition"
22
19
  gem "capybara"
23
- gem "coveralls", require: false
24
20
  gem "puma"
25
21
  gem "rack-test"
26
- gem "simplecov"
27
22
  gem "sinatra"
28
23
  gem "timecop"
29
24
  end
30
25
 
26
+ group :lint do
27
+ gem "rubocop", require: false
28
+ gem "rubocop-performance", require: false
29
+ gem "rubocop-rake", require: false
30
+ gem "rubocop-rspec", require: false
31
+ end
32
+
31
33
  gemspec path: "../"
@@ -0,0 +1,33 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "appraisal"
6
+ gem "rake"
7
+ gem "rspec"
8
+ gem "sidekiq", "~> 6.5.0"
9
+
10
+ group :development do
11
+ gem "byebug"
12
+ gem "guard", require: false
13
+ gem "guard-rspec", require: false
14
+ gem "guard-rubocop", require: false
15
+ end
16
+
17
+ group :test do
18
+ gem "apparition"
19
+ gem "capybara"
20
+ gem "puma"
21
+ gem "rack-test"
22
+ gem "sinatra"
23
+ gem "timecop"
24
+ end
25
+
26
+ group :lint do
27
+ gem "rubocop", require: false
28
+ gem "rubocop-performance", require: false
29
+ gem "rubocop-rake", require: false
30
+ gem "rubocop-rspec", require: false
31
+ end
32
+
33
+ gemspec path: "../"
@@ -2,7 +2,7 @@
2
2
 
3
3
  require "fiber"
4
4
 
5
- require "sidekiq/exception_handler"
5
+ require "sidekiq/throttled/communicator/exception_handler"
6
6
 
7
7
  module Sidekiq
8
8
  module Throttled
@@ -55,17 +55,15 @@ module Sidekiq
55
55
  # @return [void]
56
56
  def run(event, payload = nil)
57
57
  @mutex.synchronize do
58
- Fiber.new do
58
+ fiber = Fiber.new do
59
59
  @handlers[event.to_s].each do |callback|
60
- begin
61
- callback.call(payload)
62
- rescue => e
63
- handle_exception(e, {
64
- :context => "sidekiq:throttled"
65
- })
66
- end
60
+ callback.call(payload)
61
+ rescue => e
62
+ handle_exception(e, :context => "sidekiq:throttled")
67
63
  end
68
- end.resume
64
+ end
65
+
66
+ fiber.resume
69
67
  end
70
68
  end
71
69
  end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "sidekiq"
4
+ require "sidekiq/version"
5
+
6
+ module Sidekiq
7
+ module Throttled
8
+ class Communicator
9
+ if Sidekiq::VERSION >= "6.5.0"
10
+ module ExceptionHandler
11
+ def handle_exception(*args)
12
+ Sidekiq.handle_exception(*args)
13
+ end
14
+ end
15
+
16
+ # NOTE: `Sidekiq.default_error_handler` is private API
17
+ Sidekiq.error_handlers << Sidekiq.method(:default_error_handler)
18
+ else
19
+ require "sidekiq/exception_handler"
20
+
21
+ ExceptionHandler = ::Sidekiq::ExceptionHandler
22
+ end
23
+ end
24
+ end
25
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "sidekiq/exception_handler"
3
+ require "sidekiq/throttled/communicator/exception_handler"
4
4
 
5
5
  module Sidekiq
6
6
  module Throttled
@@ -62,7 +62,7 @@ module Sidekiq
62
62
  # - `Exception` is recorded to the log and re-raised.
63
63
  #
64
64
  # @return [void]
65
- def listen
65
+ def listen # rubocop:disable Metrics/MethodLength
66
66
  subscribe
67
67
  rescue Sidekiq::Shutdown
68
68
  @terminated = true
@@ -88,7 +88,7 @@ module Sidekiq
88
88
  # @see http://redis.io/commands/subscribe
89
89
  # @see Callbacks#run
90
90
  # @return [void]
91
- def subscribe
91
+ def subscribe # rubocop:disable Metrics/MethodLength
92
92
  Sidekiq.redis do |conn|
93
93
  conn.subscribe @channel do |on|
94
94
  on.subscribe do
@@ -97,7 +97,7 @@ module Sidekiq
97
97
  end
98
98
 
99
99
  on.message do |_channel, data|
100
- message, payload = Marshal.load(data)
100
+ message, payload = Marshal.load(data) # rubocop:disable Security/MarshalLoad:
101
101
  @callbacks.run("message:#{message}", payload)
102
102
  end
103
103
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  require "singleton"
4
4
 
5
- require "sidekiq/exception_handler"
5
+ require "sidekiq/throttled/communicator/exception_handler"
6
6
  require "sidekiq/throttled/communicator/listener"
7
7
  require "sidekiq/throttled/communicator/callbacks"
8
8
 
@@ -2,8 +2,6 @@
2
2
 
3
3
  require "monitor"
4
4
 
5
- require "concurrent/utility/monotonic_time"
6
-
7
5
  module Sidekiq
8
6
  module Throttled
9
7
  # List that tracks when elements were added and enumerates over those not
@@ -24,7 +22,6 @@ module Sidekiq
24
22
  # It does not deduplicates elements. Eviction happens only upon elements
25
23
  # retrieval (see {#each}).
26
24
  #
27
- # @see http://ruby-concurrency.github.io/concurrent-ruby/Concurrent.html#monotonic_time-class_method
28
25
  # @see https://ruby-doc.org/core/Process.html#method-c-clock_gettime
29
26
  # @see https://linux.die.net/man/3/clock_gettime
30
27
  #
@@ -44,7 +41,7 @@ module Sidekiq
44
41
  # @params element [Object]
45
42
  # @return [ExpirableList] self
46
43
  def <<(element)
47
- @mon.synchronize { @arr << [Concurrent.monotonic_time, element] }
44
+ @mon.synchronize { @arr << [::Process.clock_gettime(::Process::CLOCK_MONOTONIC), element] }
48
45
  self
49
46
  end
50
47
 
@@ -58,7 +55,7 @@ module Sidekiq
58
55
  return to_enum __method__ unless block_given?
59
56
 
60
57
  @mon.synchronize do
61
- horizon = Concurrent.monotonic_time - @ttl
58
+ horizon = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC) - @ttl
62
59
 
63
60
  # drop all elements older than horizon
64
61
  @arr.shift while @arr[0] && @arr[0][0] < horizon
@@ -49,9 +49,14 @@ module Sidekiq
49
49
  # process was terminated. It is a reverse of whatever fetcher was
50
50
  # doing to pull the job out of queue.
51
51
  #
52
+ # @param [Redis] pipelined connection for requeing via Redis#pipelined
52
53
  # @return [void]
53
- def requeue
54
- Sidekiq.redis { |conn| conn.rpush(QueueName.expand(queue_name), job) }
54
+ def requeue(pipeline = nil)
55
+ if pipeline
56
+ pipeline.rpush(QueueName.expand(queue_name), job)
57
+ else
58
+ Sidekiq.redis { |conn| conn.rpush(QueueName.expand(queue_name), job) }
59
+ end
55
60
  end
56
61
 
57
62
  # Pushes job back to the head of the queue, so that job won't be tried
@@ -22,7 +22,11 @@ module Sidekiq
22
22
  return if units.empty?
23
23
 
24
24
  Sidekiq.logger.debug { "Re-queueing terminated jobs" }
25
- Sidekiq.redis { |conn| conn.pipelined { units.each(&:requeue) } }
25
+ Sidekiq.redis do |conn|
26
+ conn.pipelined do |pipeline|
27
+ units.each { |unit| unit.requeue(pipeline) }
28
+ end
29
+ end
26
30
  Sidekiq.logger.info("Pushed #{units.size} jobs back to Redis")
27
31
  rescue => e
28
32
  Sidekiq.logger.warn("Failed to requeue #{units.size} jobs: #{e}")
@@ -0,0 +1,128 @@
1
+ # frozen_string_literal: true
2
+
3
+ # internal
4
+ require "sidekiq/throttled/registry"
5
+
6
+ module Sidekiq
7
+ module Throttled
8
+ # Adds helpers to your worker classes
9
+ #
10
+ # @example Usage
11
+ #
12
+ # class MyWorker
13
+ # include Sidekiq::Worker
14
+ # include Sidekiq::Throttled::Worker
15
+ #
16
+ # sidkiq_options :queue => :my_queue
17
+ # sidekiq_throttle :threshold => { :limit => 123, :period => 1.hour }
18
+ #
19
+ # def perform
20
+ # # ...
21
+ # end
22
+ # end
23
+ #
24
+ # @see ClassMethods
25
+ module Job
26
+ # Extends worker class with {ClassMethods}.
27
+ #
28
+ # @note Using `included` hook with extending worker with {ClassMethods}
29
+ # in order to make API inline with `include Sidekiq::Worker`.
30
+ #
31
+ # @private
32
+ def self.included(worker)
33
+ worker.send(:extend, ClassMethods)
34
+ end
35
+
36
+ # Helper methods added to the singleton class of destination
37
+ module ClassMethods
38
+ # Registers some strategy for the worker.
39
+ #
40
+ # @example Allow max 123 MyWorker jobs per hour
41
+ #
42
+ # class MyWorker
43
+ # include Sidekiq::Worker
44
+ # include Sidekiq::Throttled::Worker
45
+ #
46
+ # sidekiq_throttle({
47
+ # :threshold => { :limit => 123, :period => 1.hour }
48
+ # })
49
+ # end
50
+ #
51
+ # @example Allow max 10 concurrently running MyWorker jobs
52
+ #
53
+ # class MyWorker
54
+ # include Sidekiq::Worker
55
+ # include Sidekiq::Throttled::Worker
56
+ #
57
+ # sidekiq_throttle({
58
+ # :concurrency => { :limit => 10 }
59
+ # })
60
+ # end
61
+ #
62
+ # @example Allow max 10 concurrent MyWorker jobs and max 123 per hour
63
+ #
64
+ # class MyWorker
65
+ # include Sidekiq::Worker
66
+ # include Sidekiq::Throttled::Worker
67
+ #
68
+ # sidekiq_throttle({
69
+ # :threshold => { :limit => 123, :period => 1.hour },
70
+ # :concurrency => { :limit => 10 }
71
+ # })
72
+ # end
73
+ #
74
+ # @see Registry.add
75
+ # @return [void]
76
+ def sidekiq_throttle(**kwargs)
77
+ Registry.add(self, **kwargs)
78
+ end
79
+
80
+ # Adds current worker to preconfigured throttling strategy. Allows
81
+ # sharing same pool for multiple workers.
82
+ #
83
+ # First of all we need to create shared throttling strategy:
84
+ #
85
+ # # Create google_api throttling strategy
86
+ # Sidekiq::Throttled::Registry.add(:google_api, {
87
+ # :threshold => { :limit => 123, :period => 1.hour },
88
+ # :concurrency => { :limit => 10 }
89
+ # })
90
+ #
91
+ # Now we can assign it to our workers:
92
+ #
93
+ # class FetchProfileJob
94
+ # include Sidekiq::Worker
95
+ # include Sidekiq::Throttled::Worker
96
+ #
97
+ # sidekiq_throttle_as :google_api
98
+ # end
99
+ #
100
+ # class FetchCommentsJob
101
+ # include Sidekiq::Worker
102
+ # include Sidekiq::Throttled::Worker
103
+ #
104
+ # sidekiq_throttle_as :google_api
105
+ # end
106
+ #
107
+ # With the above configuration we ensure that there are maximum 10
108
+ # concurrently running jobs of FetchProfileJob or FetchCommentsJob
109
+ # allowed. And only 123 jobs of those are executed per hour.
110
+ #
111
+ # In other words, it will allow:
112
+ #
113
+ # - only `X` concurrent `FetchProfileJob`s
114
+ # - max `XX` `FetchProfileJob` per hour
115
+ # - only `Y` concurrent `FetchCommentsJob`s
116
+ # - max `YY` `FetchCommentsJob` per hour
117
+ #
118
+ # Where `(X + Y) == 10` and `(XX + YY) == 123`
119
+ #
120
+ # @see Registry.add_alias
121
+ # @return [void]
122
+ def sidekiq_throttle_as(name)
123
+ Registry.add_alias(self, name)
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end
@@ -7,7 +7,7 @@ module Sidekiq
7
7
  # @private
8
8
  module QueueName
9
9
  # RegExp used to stip out any redisr-namespace prefixes with `queue:`.
10
- QUEUE_NAME_PREFIX_RE = /.*queue:/.freeze
10
+ QUEUE_NAME_PREFIX_RE = %r{.*queue:}.freeze
11
11
  private_constant :QUEUE_NAME_PREFIX_RE
12
12
 
13
13
  class << self
@@ -18,20 +18,16 @@ module Sidekiq
18
18
 
19
19
  # Adds strategy to the registry.
20
20
  #
21
- # @note prints a warning to STDERR upon duplicate strategy name
22
21
  # @param (see Strategy#initialize)
23
22
  # @return [Strategy]
24
23
  def add(name, **kwargs)
25
24
  name = name.to_s
26
25
 
27
- warn "Duplicate strategy name: #{name}" if @strategies[name]
28
-
29
26
  @strategies[name] = Strategy.new(name, **kwargs)
30
27
  end
31
28
 
32
29
  # Adds alias for existing strategy.
33
30
  #
34
- # @note prints a warning to STDERR upon duplicate strategy name
35
31
  # @param (#to_s) new_name
36
32
  # @param (#to_s) old_name
37
33
  # @raise [RuntimeError] if no strategy found with `old_name`
@@ -40,7 +36,6 @@ module Sidekiq
40
36
  new_name = new_name.to_s
41
37
  old_name = old_name.to_s
42
38
 
43
- warn "Duplicate strategy name: #{new_name}" if @strategies[new_name]
44
39
  raise "Strategy not found: #{old_name}" unless @strategies[old_name]
45
40
 
46
41
  @aliases[new_name] = @strategies[old_name]
@@ -2,6 +2,7 @@
2
2
 
3
3
  # internal
4
4
  require "sidekiq/throttled/errors"
5
+ require "sidekiq/throttled/strategy_collection"
5
6
  require "sidekiq/throttled/strategy/concurrency"
6
7
  require "sidekiq/throttled/strategy/threshold"
7
8
 
@@ -30,15 +31,20 @@ module Sidekiq
30
31
  # See keyword args of {Strategy::Threshold#initialize} for details.
31
32
  # @param [#call] key_suffix Dynamic key suffix generator.
32
33
  # @param [#call] observer Process called after throttled.
33
- def initialize(
34
- name,
35
- concurrency: nil, threshold: nil, key_suffix: nil, observer: nil
36
- )
37
- @observer = observer
38
- @concurrency = make_strategy(Concurrency, name, key_suffix, concurrency)
39
- @threshold = make_strategy(Threshold, name, key_suffix, threshold)
34
+ def initialize(name, concurrency: nil, threshold: nil, key_suffix: nil, observer: nil) # rubocop:disable Metrics/MethodLength
35
+ @observer = observer
40
36
 
41
- return if @concurrency || @threshold
37
+ @concurrency = StrategyCollection.new(concurrency,
38
+ :strategy => Concurrency,
39
+ :name => name,
40
+ :key_suffix => key_suffix)
41
+
42
+ @threshold = StrategyCollection.new(threshold,
43
+ :strategy => Threshold,
44
+ :name => name,
45
+ :key_suffix => key_suffix)
46
+
47
+ return if @concurrency.any? || @threshold.any?
42
48
 
43
49
  raise ArgumentError, "Neither :concurrency nor :threshold given"
44
50
  end
@@ -60,6 +66,7 @@ module Sidekiq
60
66
 
61
67
  if @threshold&.throttled?(*job_args)
62
68
  @observer&.call(:threshold, *job_args)
69
+
63
70
  finalize!(jid, *job_args)
64
71
  return true
65
72
  end
@@ -79,15 +86,6 @@ module Sidekiq
79
86
  @concurrency&.reset!
80
87
  @threshold&.reset!
81
88
  end
82
-
83
- private
84
-
85
- # @return [Base, nil]
86
- def make_strategy(strategy, name, key_suffix, options)
87
- return unless options
88
-
89
- strategy.new("throttled:#{name}", :key_suffix => key_suffix, **options)
90
- end
91
89
  end
92
90
  end
93
91
  end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ # internal
4
+ module Sidekiq
5
+ module Throttled
6
+ # Collection which transparently group several meta-strategies of one kind
7
+ #
8
+ # @private
9
+ class StrategyCollection
10
+ include Enumerable
11
+
12
+ attr_reader :strategies
13
+
14
+ # @param [Hash, Array, nil] strategies Concurrency or Threshold options
15
+ # or array of options.
16
+ # See keyword args of {Strategy::Concurrency#initialize} for details.
17
+ # See keyword args of {Strategy::Threshold#initialize} for details.
18
+ # @param [Class] strategy class of strategy: Concurrency or Threshold
19
+ # @param [#to_s] name
20
+ # @param [#call] key_suffix Dynamic key suffix generator.
21
+ def initialize(strategies, strategy:, name:, key_suffix:)
22
+ strategies = (strategies.is_a?(Hash) ? [strategies] : Array(strategies))
23
+ @strategies = strategies.map do |options|
24
+ make_strategy(strategy, name, key_suffix, options)
25
+ end
26
+ end
27
+
28
+ # @param [#call] block
29
+ # Iterates each strategy in collection
30
+ def each(&block)
31
+ @strategies.each(&block)
32
+ end
33
+
34
+ # @return [Boolean] whenever any strategy in collection has dynamic config
35
+ def dynamic?
36
+ any?(&:dynamic?)
37
+ end
38
+
39
+ # @return [Boolean] whenever job is throttled or not
40
+ # by any strategy in collection.
41
+ def throttled?(*args)
42
+ any? { |s| s.throttled?(*args) }
43
+ end
44
+
45
+ # Marks job as being processed.
46
+ # @return [void]
47
+ def finalize!(*args)
48
+ each { |c| c.finalize!(*args) }
49
+ end
50
+
51
+ # Resets count of jobs of all avaliable strategies
52
+ # @return [void]
53
+ def reset!
54
+ each(&:reset!)
55
+ end
56
+
57
+ private
58
+
59
+ # @return [Base, nil]
60
+ def make_strategy(strategy, name, key_suffix, options)
61
+ return unless options
62
+
63
+ strategy.new("throttled:#{name}",
64
+ :key_suffix => key_suffix,
65
+ **options)
66
+ end
67
+ end
68
+ end
69
+ end
@@ -9,7 +9,7 @@ module Sidekiq
9
9
  # @param name [#to_s] Constant name
10
10
  # @return [Object, nil] Resolved constant or nil if failed.
11
11
  def constantize(name)
12
- name.to_s.sub(/^::/, "").split("::").inject(Object, &:const_get)
12
+ name.to_s.sub(%r{^::}, "").split("::").inject(Object, &:const_get)
13
13
  rescue NameError
14
14
  Sidekiq.logger.warn { "Failed to constantize: #{name}" }
15
15
  nil
@@ -3,6 +3,6 @@
3
3
  module Sidekiq
4
4
  module Throttled
5
5
  # Gem version
6
- VERSION = "0.13.0"
6
+ VERSION = "0.16.0"
7
7
  end
8
8
  end
@@ -37,7 +37,8 @@ module Sidekiq
37
37
  percentile = 100.00 * int / max
38
38
  lvl = if 80 <= percentile then "danger"
39
39
  elsif 60 <= percentile then "warning"
40
- else "success"
40
+ else
41
+ "success"
41
42
  end
42
43
 
43
44
  %(<span class="label label-#{lvl}">#{int}</span>)
@@ -61,10 +62,10 @@ module Sidekiq
61
62
 
62
63
  # @return [String]
63
64
  def humanize_integer(int)
64
- digits = int.to_s.split ""
65
- str = digits.shift(digits.count % 3).join("")
65
+ digits = int.to_s.chars
66
+ str = digits.shift(digits.count % 3).join
66
67
 
67
- str << " " << digits.shift(3).join("") while digits.count.positive?
68
+ str << " " << digits.shift(3).join while digits.count.positive?
68
69
 
69
70
  str.strip
70
71
  end
@@ -4,7 +4,7 @@ module Sidekiq
4
4
  module Throttled
5
5
  module Web
6
6
  module SummaryFix
7
- JAVASCRIPT = [File.read(__FILE__.sub(/\.rb$/, ".js")).freeze].freeze
7
+ JAVASCRIPT = [File.read(File.expand_path("summary_fix.js", __dir__)).freeze].freeze
8
8
  HEADERS = { "Content-Type" => "application/javascript" }.freeze
9
9
 
10
10
  class << self
@@ -14,10 +14,14 @@
14
14
  <tr>
15
15
  <td style="vertical-align:middle;"><%= name %></td>
16
16
  <td style="vertical-align:middle;text-align:center;">
17
- <%= Sidekiq::Throttled::Web::Stats.new(strategy.concurrency).to_html %>
17
+ <% strategy.concurrency.each do |concurrency| %>
18
+ <%= Sidekiq::Throttled::Web::Stats.new(concurrency).to_html %>
19
+ <% end %>
18
20
  </td>
19
21
  <td style="vertical-align:middle;text-align:center;">
20
- <%= Sidekiq::Throttled::Web::Stats.new(strategy.threshold).to_html %>
22
+ <% strategy.threshold.each do |threshold| %>
23
+ <%= Sidekiq::Throttled::Web::Stats.new(threshold).to_html %>
24
+ <% end %>
21
25
  </td>
22
26
  <td style="vertical-align:middle;text-align:center;">
23
27
  <form action="<%= root_path %>throttled/<%= CGI.escape name %>/reset" method="post">
@@ -57,7 +57,7 @@ module Sidekiq
57
57
  end
58
58
 
59
59
  # rubocop:disable Metrics/AbcSize
60
- def register_enhanced_queues_tab(app)
60
+ def register_enhanced_queues_tab(app) # rubocop:disable Metrics/MethodLength
61
61
  pauser = QueuesPauser.instance
62
62
 
63
63
  app.get("/enhanced-queues") do