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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +51 -0
- data/.rubocop/layout.yml +24 -0
- data/.rubocop/lint.yml +41 -0
- data/.rubocop/metrics.yml +4 -0
- data/.rubocop/performance.yml +25 -0
- data/.rubocop/rspec.yml +3 -0
- data/.rubocop/style.yml +84 -0
- data/.rubocop.yml +13 -68
- data/.rubocop_todo.yml +10 -35
- data/.travis.yml +2 -1
- data/Appraisals +16 -0
- data/CHANGES.md +53 -0
- data/Gemfile +6 -5
- data/LICENSE.md +2 -1
- data/README.md +49 -18
- data/gemfiles/sidekiq_5.0.gemfile +6 -5
- data/gemfiles/sidekiq_5.1.gemfile +6 -5
- data/gemfiles/sidekiq_5.2.gemfile +6 -5
- data/gemfiles/sidekiq_6.0.gemfile +31 -0
- data/gemfiles/sidekiq_6.1.gemfile +31 -0
- data/gemfiles/sidekiq_6.2.gemfile +31 -0
- data/gemfiles/sidekiq_6.3.gemfile +31 -0
- data/lib/sidekiq/throttled/communicator/callbacks.rb +8 -9
- data/lib/sidekiq/throttled/communicator/listener.rb +3 -3
- data/lib/sidekiq/throttled/expirable_list.rb +1 -1
- data/lib/sidekiq/throttled/fetch.rb +34 -20
- data/lib/sidekiq/throttled/queue_name.rb +1 -1
- data/lib/sidekiq/throttled/registry.rb +2 -5
- data/lib/sidekiq/throttled/strategy/concurrency.rb +5 -5
- data/lib/sidekiq/throttled/strategy/threshold.rb +6 -5
- data/lib/sidekiq/throttled/strategy.rb +15 -20
- data/lib/sidekiq/throttled/strategy_collection.rb +69 -0
- data/lib/sidekiq/throttled/utils.rb +1 -1
- data/lib/sidekiq/throttled/version.rb +1 -1
- data/lib/sidekiq/throttled/web/stats.rb +2 -6
- data/lib/sidekiq/throttled/web/summary_fix.rb +3 -2
- data/lib/sidekiq/throttled/web/throttled.html.erb +6 -2
- data/lib/sidekiq/throttled/web.rb +1 -1
- data/lib/sidekiq/throttled/worker.rb +2 -2
- data/lib/sidekiq/throttled.rb +14 -2
- data/sidekiq-throttled.gemspec +3 -1
- 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
|
-
|
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
|
-
|
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(
|
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
|
@@ -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?
|
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(
|
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.
|
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
|
-
|
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
|
-
|
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">
|
@@ -77,7 +77,7 @@ module Sidekiq
|
|
77
77
|
Registry.add(self, **kwargs)
|
78
78
|
end
|
79
79
|
|
80
|
-
# Adds current worker to preconfigured
|
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 =>
|
88
|
+
# :concurrency => { :limit => 10 }
|
89
89
|
# })
|
90
90
|
#
|
91
91
|
# Now we can assign it to our workers:
|
data/lib/sidekiq/throttled.rb
CHANGED
@@ -61,8 +61,7 @@ module Sidekiq
|
|
61
61
|
QueuesPauser.instance.setup!
|
62
62
|
|
63
63
|
Sidekiq.configure_server do |config|
|
64
|
-
|
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,
|
data/sidekiq-throttled.gemspec
CHANGED
@@ -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", "
|
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.
|
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:
|
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: '
|
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.
|
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: []
|