sidekiq-throttled 0.11.0 → 0.15.0

Sign up to get free protection for your applications and to get access to all the features.
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: []