sidekiq-throttled 0.11.0 → 0.15.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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +51 -0
  3. data/.rubocop/layout.yml +24 -0
  4. data/.rubocop/lint.yml +41 -0
  5. data/.rubocop/metrics.yml +4 -0
  6. data/.rubocop/performance.yml +25 -0
  7. data/.rubocop/rspec.yml +3 -0
  8. data/.rubocop/style.yml +84 -0
  9. data/.rubocop.yml +13 -68
  10. data/.rubocop_todo.yml +10 -35
  11. data/.travis.yml +2 -1
  12. data/Appraisals +16 -0
  13. data/CHANGES.md +53 -0
  14. data/Gemfile +6 -5
  15. data/LICENSE.md +2 -1
  16. data/README.md +49 -18
  17. data/gemfiles/sidekiq_5.0.gemfile +6 -5
  18. data/gemfiles/sidekiq_5.1.gemfile +6 -5
  19. data/gemfiles/sidekiq_5.2.gemfile +6 -5
  20. data/gemfiles/sidekiq_6.0.gemfile +31 -0
  21. data/gemfiles/sidekiq_6.1.gemfile +31 -0
  22. data/gemfiles/sidekiq_6.2.gemfile +31 -0
  23. data/gemfiles/sidekiq_6.3.gemfile +31 -0
  24. data/lib/sidekiq/throttled/communicator/callbacks.rb +8 -9
  25. data/lib/sidekiq/throttled/communicator/listener.rb +3 -3
  26. data/lib/sidekiq/throttled/expirable_list.rb +1 -1
  27. data/lib/sidekiq/throttled/fetch.rb +34 -20
  28. data/lib/sidekiq/throttled/queue_name.rb +1 -1
  29. data/lib/sidekiq/throttled/registry.rb +2 -5
  30. data/lib/sidekiq/throttled/strategy/concurrency.rb +5 -5
  31. data/lib/sidekiq/throttled/strategy/threshold.rb +6 -5
  32. data/lib/sidekiq/throttled/strategy.rb +15 -20
  33. data/lib/sidekiq/throttled/strategy_collection.rb +69 -0
  34. data/lib/sidekiq/throttled/utils.rb +1 -1
  35. data/lib/sidekiq/throttled/version.rb +1 -1
  36. data/lib/sidekiq/throttled/web/stats.rb +2 -6
  37. data/lib/sidekiq/throttled/web/summary_fix.rb +3 -2
  38. data/lib/sidekiq/throttled/web/throttled.html.erb +6 -2
  39. data/lib/sidekiq/throttled/web.rb +1 -1
  40. data/lib/sidekiq/throttled/worker.rb +2 -2
  41. data/lib/sidekiq/throttled.rb +14 -2
  42. data/sidekiq-throttled.gemspec +3 -1
  43. metadata +21 -9
@@ -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,18 +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}", {
90
- :key_suffix => key_suffix,
91
- **options
92
- })
93
- end
94
89
  end
95
90
  end
96
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.11.0"
6
+ VERSION = "0.15.0"
7
7
  end
8
8
  end
@@ -14,9 +14,7 @@ module Sidekiq
14
14
 
15
15
  # @param [Strategy::Concurrency, Strategy::Threshold] strategy
16
16
  def initialize(strategy)
17
- if strategy&.dynamic?
18
- raise ArgumentError, "Can't handle dynamic strategies"
19
- end
17
+ raise ArgumentError, "Can't handle dynamic strategies" if strategy&.dynamic?
20
18
 
21
19
  @strategy = strategy
22
20
  end
@@ -27,9 +25,7 @@ module Sidekiq
27
25
 
28
26
  html = humanize_integer(@strategy.limit) << " jobs"
29
27
 
30
- if @strategy.respond_to? :period
31
- html << " per " << humanize_duration(@strategy.period)
32
- end
28
+ html << " per " << humanize_duration(@strategy.period) if @strategy.respond_to?(:period)
33
29
 
34
30
  html << "<br />" << colorize_count(@strategy.count, @strategy.limit)
35
31
  end
@@ -4,14 +4,15 @@ 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
11
11
  attr_accessor :enabled
12
12
 
13
13
  def apply!(app)
14
- Sidekiq::WebAction.send(:prepend, SummaryFix)
14
+ Sidekiq::WebAction.prepend SummaryFix
15
+
15
16
  app.get("/throttled/summary_fix") do
16
17
  [200, HEADERS.dup, JAVASCRIPT.dup]
17
18
  end
@@ -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
@@ -77,7 +77,7 @@ module Sidekiq
77
77
  Registry.add(self, **kwargs)
78
78
  end
79
79
 
80
- # Adds current worker to preconfigured throtttling strtegy. Allows
80
+ # Adds current worker to preconfigured throttling strategy. Allows
81
81
  # sharing same pool for multiple workers.
82
82
  #
83
83
  # First of all we need to create shared throttling strategy:
@@ -85,7 +85,7 @@ module Sidekiq
85
85
  # # Create google_api throttling strategy
86
86
  # Sidekiq::Throttled::Registry.add(:google_api, {
87
87
  # :threshold => { :limit => 123, :period => 1.hour },
88
- # :concurrency => { :limit => 123 }
88
+ # :concurrency => { :limit => 10 }
89
89
  # })
90
90
  #
91
91
  # Now we can assign it to our workers:
@@ -61,8 +61,7 @@ module Sidekiq
61
61
  QueuesPauser.instance.setup!
62
62
 
63
63
  Sidekiq.configure_server do |config|
64
- require "sidekiq/throttled/fetch"
65
- Sidekiq.options[:fetch] = Sidekiq::Throttled::Fetch
64
+ setup_strategy!
66
65
 
67
66
  require "sidekiq/throttled/middleware"
68
67
  config.server_middleware do |chain|
@@ -93,6 +92,19 @@ module Sidekiq
93
92
 
94
93
  private
95
94
 
95
+ # @return [void]
96
+ def setup_strategy!
97
+ require "sidekiq/throttled/fetch"
98
+
99
+ # https://github.com/mperham/sidekiq/commit/fce05c9d4b4c0411c982078a4cf3a63f20f739bc
100
+ Sidekiq.options[:fetch] =
101
+ if Gem::Version.new(Sidekiq::VERSION) < Gem::Version.new("6.1.0")
102
+ Sidekiq::Throttled::Fetch
103
+ else
104
+ Sidekiq::Throttled::Fetch.new(Sidekiq.options)
105
+ end
106
+ end
107
+
96
108
  # Tries to preload constant by it's name once.
97
109
  #
98
110
  # Somehow, sometimes, some classes are not eager loaded upon Rails init,
@@ -24,9 +24,11 @@ Gem::Specification.new do |spec|
24
24
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
25
25
  spec.require_paths = ["lib"]
26
26
 
27
+ spec.required_ruby_version = ">= 2.6"
28
+
27
29
  spec.add_runtime_dependency "concurrent-ruby"
28
30
  spec.add_runtime_dependency "redis-prescription"
29
31
  spec.add_runtime_dependency "sidekiq"
30
32
 
31
- spec.add_development_dependency "bundler", "~> 2.0"
33
+ spec.add_development_dependency "bundler", ">= 2.0"
32
34
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sidekiq-throttled
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.0
4
+ version: 0.15.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexey V Zapparov
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-08-23 00:00:00.000000000 Z
11
+ date: 2021-12-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -56,14 +56,14 @@ dependencies:
56
56
  name: bundler
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - "~>"
59
+ - - ">="
60
60
  - !ruby/object:Gem::Version
61
61
  version: '2.0'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - "~>"
66
+ - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '2.0'
69
69
  description: Concurrency and threshold throttling for Sidekiq.
@@ -74,9 +74,16 @@ extensions: []
74
74
  extra_rdoc_files: []
75
75
  files:
76
76
  - ".coveralls.yml"
77
+ - ".github/workflows/ci.yml"
77
78
  - ".gitignore"
78
79
  - ".rspec"
79
80
  - ".rubocop.yml"
81
+ - ".rubocop/layout.yml"
82
+ - ".rubocop/lint.yml"
83
+ - ".rubocop/metrics.yml"
84
+ - ".rubocop/performance.yml"
85
+ - ".rubocop/rspec.yml"
86
+ - ".rubocop/style.yml"
80
87
  - ".rubocop_todo.yml"
81
88
  - ".travis.yml"
82
89
  - ".yardopts"
@@ -90,6 +97,10 @@ files:
90
97
  - gemfiles/sidekiq_5.0.gemfile
91
98
  - gemfiles/sidekiq_5.1.gemfile
92
99
  - gemfiles/sidekiq_5.2.gemfile
100
+ - gemfiles/sidekiq_6.0.gemfile
101
+ - gemfiles/sidekiq_6.1.gemfile
102
+ - gemfiles/sidekiq_6.2.gemfile
103
+ - gemfiles/sidekiq_6.3.gemfile
93
104
  - lib/sidekiq/throttled.rb
94
105
  - lib/sidekiq/throttled/communicator.rb
95
106
  - lib/sidekiq/throttled/communicator/callbacks.rb
@@ -110,6 +121,7 @@ files:
110
121
  - lib/sidekiq/throttled/strategy/concurrency.rb
111
122
  - lib/sidekiq/throttled/strategy/threshold.lua
112
123
  - lib/sidekiq/throttled/strategy/threshold.rb
124
+ - lib/sidekiq/throttled/strategy_collection.rb
113
125
  - lib/sidekiq/throttled/testing.rb
114
126
  - lib/sidekiq/throttled/utils.rb
115
127
  - lib/sidekiq/throttled/version.rb
@@ -125,7 +137,7 @@ homepage: https://github.com/sensortower/sidekiq-throttled
125
137
  licenses:
126
138
  - MIT
127
139
  metadata: {}
128
- post_install_message:
140
+ post_install_message:
129
141
  rdoc_options: []
130
142
  require_paths:
131
143
  - lib
@@ -133,15 +145,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
133
145
  requirements:
134
146
  - - ">="
135
147
  - !ruby/object:Gem::Version
136
- version: '0'
148
+ version: '2.6'
137
149
  required_rubygems_version: !ruby/object:Gem::Requirement
138
150
  requirements:
139
151
  - - ">="
140
152
  - !ruby/object:Gem::Version
141
153
  version: '0'
142
154
  requirements: []
143
- rubygems_version: 3.0.3
144
- signing_key:
155
+ rubygems_version: 3.2.22
156
+ signing_key:
145
157
  specification_version: 4
146
158
  summary: Concurrency and threshold throttling for Sidekiq.
147
159
  test_files: []