sidekiq-throttled 1.5.1 → 2.0.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/README.adoc +31 -9
- data/lib/sidekiq/throttled/config.rb +1 -1
- data/lib/sidekiq/throttled/job.rb +2 -2
- data/lib/sidekiq/throttled/registry.rb +2 -2
- data/lib/sidekiq/throttled/strategy/concurrency.rb +2 -0
- data/lib/sidekiq/throttled/strategy.rb +13 -6
- data/lib/sidekiq/throttled/strategy_collection.rb +1 -1
- data/lib/sidekiq/throttled/version.rb +1 -1
- data/lib/sidekiq/throttled/web.rb +17 -21
- data/web/views/index.erb +41 -0
- metadata +9 -9
- data/lib/sidekiq/throttled/web/throttled.html.erb +0 -35
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 85023f0d147ed5c41ac214b6f8c907ac4fd8c023271e0def9297659305449b16
|
4
|
+
data.tar.gz: 2f6b2c9c97210185dc91539328b85e108ee07b36770da7eaf87e27d3cff4138c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9fe961d74fd88be00803879e6a35197be285a80d7d7c289385bf6268a1c58c9e82a81c32ef0fb89070f8eccab6dc8ee963de2b2109695879be0c4b7d75bdc770
|
7
|
+
data.tar.gz: ee02663fa70432081fefbb756b5be9a72917ef3dcee1cf915a221d97c21f7ceb65b04ac0a54d5a3dba0709a484361e04423e3cf7a0750ef7127df5d0d1796f92
|
data/README.adoc
CHANGED
@@ -262,6 +262,33 @@ IMPORTANT: Don't forget to specify `:key_suffix` and make it return different
|
|
262
262
|
values if you are using dynamic limit/period options. Otherwise, you risk
|
263
263
|
getting into some trouble.
|
264
264
|
|
265
|
+
[source,ruby]
|
266
|
+
----
|
267
|
+
class MyJob
|
268
|
+
include Sidekiq::Job
|
269
|
+
include Sidekiq::Throttled::Job
|
270
|
+
|
271
|
+
sidekiq_options queue: :my_queue
|
272
|
+
|
273
|
+
sidekiq_throttle(
|
274
|
+
concurrency: { limit: 10 },
|
275
|
+
# Allow 500 jobs per minute, 5,000 per hour, and 50,000 per day:
|
276
|
+
threshold: [
|
277
|
+
{ limit: 500, period: 1.minute, key_suffix: "minutely" },
|
278
|
+
{ limit: 5_000, period: 1.hour, key_suffix: "hourly" },
|
279
|
+
{ limit: 50_000, period: 1.day, key_suffix: "daily" },
|
280
|
+
]
|
281
|
+
)
|
282
|
+
|
283
|
+
def perform(project_id, user_id)
|
284
|
+
# ...
|
285
|
+
end
|
286
|
+
end
|
287
|
+
----
|
288
|
+
|
289
|
+
NOTE: `key_suffix` does not have to be a proc/lambda, it can just be a
|
290
|
+
string value. This can come in handy to set throttle limits for different
|
291
|
+
ranges of time
|
265
292
|
|
266
293
|
=== Concurrency throttling fine-tuning
|
267
294
|
|
@@ -287,11 +314,9 @@ sidekiq_throttle(concurrency: { limit: 20, ttl: 1.hour.to_i })
|
|
287
314
|
|
288
315
|
This library aims to support and is tested against the following Ruby versions:
|
289
316
|
|
290
|
-
* Ruby 2.7.x
|
291
|
-
* Ruby 3.0.x
|
292
|
-
* Ruby 3.1.x
|
293
317
|
* Ruby 3.2.x
|
294
318
|
* Ruby 3.3.x
|
319
|
+
* Ruby 3.4.x
|
295
320
|
|
296
321
|
If something doesn't work on one of these versions, it's a bug.
|
297
322
|
|
@@ -311,15 +336,11 @@ dropped.
|
|
311
336
|
|
312
337
|
This library aims to support and work with following Sidekiq versions:
|
313
338
|
|
314
|
-
* Sidekiq
|
315
|
-
* Sidekiq 7.1.x
|
316
|
-
* Sidekiq 7.2.x
|
339
|
+
* Sidekiq 8.0.x
|
317
340
|
|
318
341
|
And the following Sidekiq Pro versions:
|
319
342
|
|
320
|
-
* Sidekiq Pro
|
321
|
-
* Sidekiq Pro 7.1.x
|
322
|
-
* Sidekiq Pro 7.2.x
|
343
|
+
* Sidekiq Pro 8.0.x
|
323
344
|
|
324
345
|
== Development
|
325
346
|
|
@@ -334,6 +355,7 @@ If you're working on Sidekiq-Pro support make sure that you have Sidekiq-Pro
|
|
334
355
|
license set either in the global config, or in `BUNDLE_GEMS\__CONTRIBSYS__COM`
|
335
356
|
environment variable.
|
336
357
|
|
358
|
+
|
337
359
|
== Contributing
|
338
360
|
|
339
361
|
* Fork sidekiq-throttled on GitHub
|
@@ -51,7 +51,7 @@ module Sidekiq
|
|
51
51
|
|
52
52
|
# @!attribute [w] default_requeue_options
|
53
53
|
def default_requeue_options=(options)
|
54
|
-
requeue_with = options.delete(:with)
|
54
|
+
requeue_with = options.delete(:with)&.to_sym || :enqueue
|
55
55
|
|
56
56
|
@default_requeue_options = options.merge({ with: requeue_with })
|
57
57
|
end
|
@@ -88,8 +88,8 @@ module Sidekiq
|
|
88
88
|
# @param [Hash] requeue What to do with jobs that are throttled
|
89
89
|
# @see Registry.add
|
90
90
|
# @return [void]
|
91
|
-
def sidekiq_throttle(**
|
92
|
-
Registry.add(self, **
|
91
|
+
def sidekiq_throttle(**)
|
92
|
+
Registry.add(self, **)
|
93
93
|
end
|
94
94
|
|
95
95
|
# Adds current worker to preconfigured throttling strategy. Allows
|
@@ -17,10 +17,10 @@ module Sidekiq
|
|
17
17
|
#
|
18
18
|
# @param (see Strategy#initialize)
|
19
19
|
# @return [Strategy]
|
20
|
-
def add(name, **
|
20
|
+
def add(name, **)
|
21
21
|
name = name.to_s
|
22
22
|
|
23
|
-
@strategies[name] = Strategy.new(name, **
|
23
|
+
@strategies[name] = Strategy.new(name, **)
|
24
24
|
end
|
25
25
|
|
26
26
|
# Adds alias for existing strategy.
|
@@ -58,6 +58,8 @@ module Sidekiq
|
|
58
58
|
return 0.0 if !job_limit || count(*job_args) < job_limit
|
59
59
|
|
60
60
|
oldest_jid_with_score = Sidekiq.redis { |redis| redis.zrange(key(job_args), 0, 0, withscores: true) }.first
|
61
|
+
return 0.0 unless oldest_jid_with_score
|
62
|
+
|
61
63
|
expiry_time = oldest_jid_with_score.last.to_f
|
62
64
|
expiry_time - Time.now.to_f
|
63
65
|
end
|
@@ -143,24 +143,27 @@ module Sidekiq
|
|
143
143
|
when NilClass
|
144
144
|
work.queue
|
145
145
|
when String, Symbol
|
146
|
-
requeue_to
|
146
|
+
requeue_to
|
147
147
|
else
|
148
148
|
raise ArgumentError, "Invalid argument for `to`"
|
149
149
|
end
|
150
150
|
|
151
151
|
target = work.queue if target.nil? || target.empty?
|
152
152
|
|
153
|
-
target.
|
153
|
+
target.to_s
|
154
154
|
end
|
155
155
|
|
156
156
|
# Push the job back to the head of the queue.
|
157
|
+
# The queue name is expected to include the "queue:" prefix, so we add it if it's missing.
|
157
158
|
def re_enqueue_throttled(work, target_queue)
|
159
|
+
target_queue = "queue:#{target_queue}" unless target_queue.start_with?("queue:")
|
160
|
+
|
158
161
|
case work.class.name
|
159
162
|
when "Sidekiq::Pro::SuperFetch::UnitOfWork"
|
160
163
|
# Calls SuperFetch UnitOfWork's requeue to remove the job from the
|
161
164
|
# temporary queue and push job back to the head of the target queue, so that
|
162
165
|
# the job won't be tried immediately after it was requeued (in most cases).
|
163
|
-
work.queue = target_queue
|
166
|
+
work.queue = target_queue
|
164
167
|
work.requeue
|
165
168
|
else
|
166
169
|
# This is the same operation Sidekiq performs upon `Sidekiq::Worker.perform_async` call.
|
@@ -168,10 +171,13 @@ module Sidekiq
|
|
168
171
|
end
|
169
172
|
end
|
170
173
|
|
174
|
+
# Reschedule the job to be executed later in the target queue.
|
175
|
+
# The queue name should NOT include the "queue:" prefix, so we remove it if it's present.
|
171
176
|
def reschedule_throttled(work, target_queue)
|
172
|
-
|
173
|
-
|
174
|
-
|
177
|
+
target_queue = target_queue.delete_prefix("queue:")
|
178
|
+
message = JSON.parse(work.job)
|
179
|
+
job_class = message.fetch("wrapped") { message.fetch("class") { return false } }
|
180
|
+
job_args = message["args"]
|
175
181
|
|
176
182
|
# Re-enqueue the job to the target queue at another time as a NEW unit of work
|
177
183
|
# AND THEN mark this work as done, so SuperFetch doesn't think this instance is orphaned
|
@@ -179,6 +185,7 @@ module Sidekiq
|
|
179
185
|
# but your job should be idempotent anyway, right?
|
180
186
|
# The job running twice was already a risk with SuperFetch anyway and this doesn't really increase that risk.
|
181
187
|
Sidekiq::Client.enqueue_to_in(target_queue, retry_in(work), Object.const_get(job_class), *job_args)
|
188
|
+
|
182
189
|
work.acknowledge
|
183
190
|
end
|
184
191
|
|
@@ -1,43 +1,39 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# stdlib
|
4
3
|
require "pathname"
|
5
4
|
|
6
|
-
# 3rd party
|
7
5
|
require "sidekiq"
|
8
6
|
require "sidekiq/web"
|
9
7
|
|
10
|
-
# internal
|
11
8
|
require_relative "./registry"
|
12
9
|
require_relative "./web/stats"
|
13
10
|
|
14
11
|
module Sidekiq
|
15
12
|
module Throttled
|
16
|
-
# Provides Sidekiq tab to monitor and reset throttled stats.
|
17
13
|
module Web
|
18
|
-
|
19
|
-
|
14
|
+
ROOT = Pathname.new(__dir__).join("../../../web").expand_path.realpath.freeze
|
15
|
+
VIEWS = ROOT.join("views").freeze
|
20
16
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
register_throttled_tab app
|
17
|
+
def self.registered(app)
|
18
|
+
app.get("/throttled") do
|
19
|
+
erb :index, views: VIEWS
|
25
20
|
end
|
26
21
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
app.get("/throttled") { erb THROTTLED_TPL.dup }
|
31
|
-
|
32
|
-
app.post("/throttled/:id/reset") do
|
33
|
-
Registry.get(params[:id], &:reset!)
|
34
|
-
redirect "#{root_path}throttled"
|
35
|
-
end
|
22
|
+
app.post("/throttled/:id/reset") do
|
23
|
+
Registry.get(route_params(:id), &:reset!)
|
24
|
+
redirect "#{root_path}throttled"
|
36
25
|
end
|
37
26
|
end
|
38
27
|
end
|
39
28
|
end
|
40
29
|
end
|
41
30
|
|
42
|
-
Sidekiq::Web.
|
43
|
-
|
31
|
+
Sidekiq::Web.configure do |config|
|
32
|
+
config.register_extension(
|
33
|
+
Sidekiq::Throttled::Web,
|
34
|
+
name: "throttled",
|
35
|
+
tab: %w[Throttled],
|
36
|
+
index: %w[throttled],
|
37
|
+
root_dir: Sidekiq::Throttled::Web::ROOT.to_s
|
38
|
+
)
|
39
|
+
end
|
data/web/views/index.erb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
<section>
|
2
|
+
<header>
|
3
|
+
<h1>Throttled</h1>
|
4
|
+
</header>
|
5
|
+
|
6
|
+
<div class="table_container">
|
7
|
+
<table class="throttled">
|
8
|
+
<thead>
|
9
|
+
<tr>
|
10
|
+
<th>Name</th>
|
11
|
+
<th>Concurrency</th>
|
12
|
+
<th>Threshold</th>
|
13
|
+
<th>Actions</th>
|
14
|
+
</tr>
|
15
|
+
</thead>
|
16
|
+
<tbody>
|
17
|
+
<% Sidekiq::Throttled::Registry.each_with_static_keys do |name, strategy| %>
|
18
|
+
<tr>
|
19
|
+
<td style="vertical-align:middle;"><%= name %></td>
|
20
|
+
<td style="vertical-align:middle;text-align:center;">
|
21
|
+
<% strategy.concurrency.each do |concurrency| %>
|
22
|
+
<%= Sidekiq::Throttled::Web::Stats.new(concurrency).to_html %>
|
23
|
+
<% end %>
|
24
|
+
</td>
|
25
|
+
<td style="vertical-align:middle;text-align:center;">
|
26
|
+
<% strategy.threshold.each do |threshold| %>
|
27
|
+
<%= Sidekiq::Throttled::Web::Stats.new(threshold).to_html %>
|
28
|
+
<% end %>
|
29
|
+
</td>
|
30
|
+
<td style="vertical-align:middle;text-align:center;">
|
31
|
+
<form action="<%= root_path %>throttled/<%= CGI.escape name %>/reset" method="post">
|
32
|
+
<%= csrf_tag %>
|
33
|
+
<button class="btn btn-danger" type="submit">Reset</button>
|
34
|
+
</form>
|
35
|
+
</td>
|
36
|
+
</tr>
|
37
|
+
<% end %>
|
38
|
+
</tbody>
|
39
|
+
</table>
|
40
|
+
</div>
|
41
|
+
</section>
|
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:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alexey Zapparov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2025-06-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -44,14 +44,14 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
47
|
+
version: '8.0'
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
54
|
+
version: '8.0'
|
55
55
|
description:
|
56
56
|
email:
|
57
57
|
- alexey@zapparov.com
|
@@ -83,16 +83,16 @@ files:
|
|
83
83
|
- lib/sidekiq/throttled/version.rb
|
84
84
|
- lib/sidekiq/throttled/web.rb
|
85
85
|
- lib/sidekiq/throttled/web/stats.rb
|
86
|
-
- lib/sidekiq/throttled/web/throttled.html.erb
|
87
86
|
- lib/sidekiq/throttled/worker.rb
|
87
|
+
- web/views/index.erb
|
88
88
|
homepage: https://github.com/ixti/sidekiq-throttled
|
89
89
|
licenses:
|
90
90
|
- MIT
|
91
91
|
metadata:
|
92
92
|
homepage_uri: https://github.com/ixti/sidekiq-throttled
|
93
|
-
source_code_uri: https://github.com/ixti/sidekiq-throttled/tree/
|
93
|
+
source_code_uri: https://github.com/ixti/sidekiq-throttled/tree/v2.0.0
|
94
94
|
bug_tracker_uri: https://github.com/ixti/sidekiq-throttled/issues
|
95
|
-
changelog_uri: https://github.com/ixti/sidekiq-throttled/blob/
|
95
|
+
changelog_uri: https://github.com/ixti/sidekiq-throttled/blob/v2.0.0/CHANGES.md
|
96
96
|
rubygems_mfa_required: 'true'
|
97
97
|
post_install_message:
|
98
98
|
rdoc_options: []
|
@@ -102,14 +102,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
102
102
|
requirements:
|
103
103
|
- - ">="
|
104
104
|
- !ruby/object:Gem::Version
|
105
|
-
version: '2
|
105
|
+
version: '3.2'
|
106
106
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
108
|
- - ">="
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '0'
|
111
111
|
requirements: []
|
112
|
-
rubygems_version: 3.4.
|
112
|
+
rubygems_version: 3.4.19
|
113
113
|
signing_key:
|
114
114
|
specification_version: 4
|
115
115
|
summary: Concurrency and rate-limit throttling for Sidekiq
|
@@ -1,35 +0,0 @@
|
|
1
|
-
<h3>Throttled</h3>
|
2
|
-
|
3
|
-
<div class="table_container">
|
4
|
-
<table class="table table-hover table-bordered table-striped table-white">
|
5
|
-
<thead>
|
6
|
-
<tr>
|
7
|
-
<th>Name</th>
|
8
|
-
<th style="text-align:center;">Concurrency</th>
|
9
|
-
<th style="text-align:center;">Threshold</th>
|
10
|
-
<th style="text-align:center;">Actions</th>
|
11
|
-
</tr>
|
12
|
-
</thead>
|
13
|
-
<% Sidekiq::Throttled::Registry.each_with_static_keys do |name, strategy| %>
|
14
|
-
<tr>
|
15
|
-
<td style="vertical-align:middle;"><%= name %></td>
|
16
|
-
<td style="vertical-align:middle;text-align:center;">
|
17
|
-
<% strategy.concurrency.each do |concurrency| %>
|
18
|
-
<%= Sidekiq::Throttled::Web::Stats.new(concurrency).to_html %>
|
19
|
-
<% end %>
|
20
|
-
</td>
|
21
|
-
<td style="vertical-align:middle;text-align:center;">
|
22
|
-
<% strategy.threshold.each do |threshold| %>
|
23
|
-
<%= Sidekiq::Throttled::Web::Stats.new(threshold).to_html %>
|
24
|
-
<% end %>
|
25
|
-
</td>
|
26
|
-
<td style="vertical-align:middle;text-align:center;">
|
27
|
-
<form action="<%= root_path %>throttled/<%= CGI.escape name %>/reset" method="post">
|
28
|
-
<%= csrf_tag %>
|
29
|
-
<button class="btn btn-danger" type="submit">Reset</button>
|
30
|
-
</form>
|
31
|
-
</td>
|
32
|
-
</tr>
|
33
|
-
<% end %>
|
34
|
-
</table>
|
35
|
-
</div>
|