sidekiq 6.2.2 → 6.4.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of sidekiq might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/Changes.md +69 -1
- data/LICENSE +3 -3
- data/README.md +3 -3
- data/lib/generators/sidekiq/job_generator.rb +57 -0
- data/lib/generators/sidekiq/templates/{worker.rb.erb → job.rb.erb} +2 -2
- data/lib/generators/sidekiq/templates/{worker_spec.rb.erb → job_spec.rb.erb} +1 -1
- data/lib/generators/sidekiq/templates/{worker_test.rb.erb → job_test.rb.erb} +1 -1
- data/lib/sidekiq/api.rb +7 -4
- data/lib/sidekiq/cli.rb +13 -3
- data/lib/sidekiq/client.rb +5 -39
- data/lib/sidekiq/delay.rb +2 -0
- data/lib/sidekiq/extensions/action_mailer.rb +2 -2
- data/lib/sidekiq/extensions/active_record.rb +2 -2
- data/lib/sidekiq/extensions/class_methods.rb +2 -2
- data/lib/sidekiq/extensions/generic_proxy.rb +2 -2
- data/lib/sidekiq/fetch.rb +4 -3
- data/lib/sidekiq/job.rb +8 -3
- data/lib/sidekiq/job_retry.rb +6 -4
- data/lib/sidekiq/job_util.rb +65 -0
- data/lib/sidekiq/launcher.rb +5 -1
- data/lib/sidekiq/manager.rb +7 -9
- data/lib/sidekiq/middleware/current_attributes.rb +57 -0
- data/lib/sidekiq/rails.rb +11 -0
- data/lib/sidekiq/redis_connection.rb +4 -6
- data/lib/sidekiq/scheduled.rb +44 -15
- data/lib/sidekiq/util.rb +13 -0
- data/lib/sidekiq/version.rb +1 -1
- data/lib/sidekiq/web/application.rb +7 -4
- data/lib/sidekiq/web/helpers.rb +1 -12
- data/lib/sidekiq/worker.rb +127 -7
- data/lib/sidekiq.rb +8 -1
- data/sidekiq.gemspec +1 -1
- data/web/assets/javascripts/application.js +82 -61
- data/web/assets/javascripts/dashboard.js +51 -51
- data/web/assets/stylesheets/application-dark.css +19 -23
- data/web/assets/stylesheets/application-rtl.css +0 -4
- data/web/assets/stylesheets/application.css +10 -108
- data/web/locales/en.yml +1 -1
- data/web/views/_footer.erb +1 -1
- data/web/views/_poll_link.erb +2 -5
- data/web/views/_summary.erb +7 -7
- data/web/views/dashboard.erb +8 -8
- data/web/views/layout.erb +1 -1
- data/web/views/queue.erb +10 -10
- data/web/views/queues.erb +1 -1
- metadata +9 -7
- data/lib/generators/sidekiq/worker_generator.rb +0 -57
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9622b2851203b0c5a80695ab7801ca77e15dc63a641ae79132cda9a2fcbe0cc6
|
4
|
+
data.tar.gz: dd943a02d2cf910f51866d02254f3a7845cb159735bd54d223dd7127a1fbd2fa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 65bcd542866d8699ecf5958d81a15fd322cd1c51dfc1dbc6a8b15402b70862510c134e2b0ed9f9dbcdbb4dea3a59c1d12878ff926145e503079a59896f279ff3
|
7
|
+
data.tar.gz: 36143e85dc7fd4611f8a43fe39788dc590725ac63a827fbc174916da2d59fdadb3fb64fa0d819c4ef90694f6f292ea15f7fb58fb29f368c0a06c5b90f1b4bca7
|
data/Changes.md
CHANGED
@@ -1,6 +1,74 @@
|
|
1
1
|
# Sidekiq Changes
|
2
2
|
|
3
|
-
[Sidekiq Changes](https://github.com/mperham/sidekiq/blob/
|
3
|
+
[Sidekiq Changes](https://github.com/mperham/sidekiq/blob/main/Changes.md) | [Sidekiq Pro Changes](https://github.com/mperham/sidekiq/blob/main/Pro-Changes.md) | [Sidekiq Enterprise Changes](https://github.com/mperham/sidekiq/blob/main/Ent-Changes.md)
|
4
|
+
|
5
|
+
6.4.0
|
6
|
+
---------
|
7
|
+
|
8
|
+
- **SECURITY**: Validate input to avoid possible DoS in Web UI.
|
9
|
+
- Add **strict argument checking** [#5071]
|
10
|
+
Sidekiq will now log a warning if JSON-unsafe arguments are passed to `perform_async`.
|
11
|
+
Add `Sidekiq.strict_args!(false)` to your initializer to disable this warning.
|
12
|
+
This warning will switch to an exception in Sidekiq 7.0.
|
13
|
+
- Note that Delayed Extensions will be removed in Sidekiq 7.0 [#5076]
|
14
|
+
- Add `perform_{inline,sync}` in Sidekiq::Job to run a job synchronously [#5061, hasan-ally]
|
15
|
+
```ruby
|
16
|
+
SomeJob.perform_async(args...)
|
17
|
+
SomeJob.perform_sync(args...)
|
18
|
+
SomeJob.perform_inline(args...)
|
19
|
+
```
|
20
|
+
You can also dynamically redirect a job to run synchronously:
|
21
|
+
```ruby
|
22
|
+
SomeJob.set("sync": true).perform_async(args...) # will run via perform_inline
|
23
|
+
```
|
24
|
+
- Replace Sidekiq::Worker `app/workers` generator with Sidekiq::Job `app/sidekiq` generator [#5055]
|
25
|
+
```
|
26
|
+
bin/rails generate sidekiq:job ProcessOrderJob
|
27
|
+
```
|
28
|
+
- Fix job retries losing CurrentAttributes [#5090]
|
29
|
+
- Tweak shutdown to give long-running threads time to cleanup [#5095]
|
30
|
+
- Add keyword arguments support in extensions
|
31
|
+
|
32
|
+
6.3.1
|
33
|
+
---------
|
34
|
+
|
35
|
+
- Fix keyword arguments error with CurrentAttributes on Ruby 3.0 [#5048]
|
36
|
+
|
37
|
+
6.3.0
|
38
|
+
---------
|
39
|
+
|
40
|
+
- **BREAK**: The Web UI has been refactored to remove jQuery. Any UI extensions
|
41
|
+
which use jQuery will break.
|
42
|
+
- **FEATURE**: Sidekiq.logger has been enhanced so any `Rails.logger`
|
43
|
+
output in jobs now shows up in the Sidekiq console. Remove any logger
|
44
|
+
hacks in your initializer and see if it Just Works™ now. [#5021]
|
45
|
+
- **FEATURE**: Add `Sidekiq::Job` alias for `Sidekiq::Worker`, to better
|
46
|
+
reflect industry standard terminology. You can now do this:
|
47
|
+
```ruby
|
48
|
+
class MyJob
|
49
|
+
include Sidekiq::Job
|
50
|
+
sidekiq_options ...
|
51
|
+
def perform(args)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
```
|
55
|
+
- **FEATURE**: Support for serializing ActiveSupport::CurrentAttributes into each job. [#4982]
|
56
|
+
```ruby
|
57
|
+
# config/initializers/sidekiq.rb
|
58
|
+
require "sidekiq/middleware/current_attributes"
|
59
|
+
Sidekiq::CurrentAttributes.persist(Myapp::Current) # Your AS::CurrentAttributes singleton
|
60
|
+
```
|
61
|
+
- **FEATURE**: Add `Sidekiq::Worker.perform_bulk` for enqueuing jobs in bulk,
|
62
|
+
similar to `Sidekiq::Client.push_bulk` [#5042]
|
63
|
+
```ruby
|
64
|
+
MyJob.perform_bulk([[1], [2], [3]])
|
65
|
+
```
|
66
|
+
- Implement `queue_as`, `wait` and `wait_until` for ActiveJob compatibility [#5003]
|
67
|
+
- Scheduler now uses Lua to reduce Redis load and network roundtrips [#5044]
|
68
|
+
- Retry Redis operation if we get an `UNBLOCKED` Redis error [#4985]
|
69
|
+
- Run existing signal traps, if any, before running Sidekiq's trap [#4991]
|
70
|
+
- Fix fetch bug when using weighted queues which caused Sidekiq to stop
|
71
|
+
processing queues randomly [#5031]
|
4
72
|
|
5
73
|
6.2.2
|
6
74
|
---------
|
data/LICENSE
CHANGED
@@ -4,6 +4,6 @@ Sidekiq is an Open Source project licensed under the terms of
|
|
4
4
|
the LGPLv3 license. Please see <http://www.gnu.org/licenses/lgpl-3.0.html>
|
5
5
|
for license text.
|
6
6
|
|
7
|
-
Sidekiq Pro
|
8
|
-
|
9
|
-
|
7
|
+
Sidekiq Pro and Sidekiq Enterprise have a commercial-friendly license.
|
8
|
+
You can find the commercial license in COMM-LICENSE.txt.
|
9
|
+
Please see https://sidekiq.org for purchasing options.
|
data/README.md
CHANGED
@@ -43,10 +43,10 @@ Getting Started
|
|
43
43
|
-----------------
|
44
44
|
|
45
45
|
See the [Getting Started wiki page](https://github.com/mperham/sidekiq/wiki/Getting-Started) and follow the simple setup process.
|
46
|
-
You can watch [this
|
46
|
+
You can watch [this YouTube playlist](https://www.youtube.com/playlist?list=PLjeHh2LSCFrWGT5uVjUuFKAcrcj5kSai1) to learn all about
|
47
47
|
Sidekiq and see its features in action. Here's the Web UI:
|
48
48
|
|
49
|
-
![Web UI](https://github.com/mperham/sidekiq/raw/
|
49
|
+
![Web UI](https://github.com/mperham/sidekiq/raw/main/examples/web-ui.png)
|
50
50
|
|
51
51
|
|
52
52
|
Want to Upgrade?
|
@@ -84,7 +84,7 @@ See the [Sidekiq support page](https://sidekiq.org/support.html) for details.
|
|
84
84
|
License
|
85
85
|
-----------------
|
86
86
|
|
87
|
-
Please see [LICENSE](https://github.com/mperham/sidekiq/blob/
|
87
|
+
Please see [LICENSE](https://github.com/mperham/sidekiq/blob/main/LICENSE) for licensing details.
|
88
88
|
|
89
89
|
|
90
90
|
Author
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require "rails/generators/named_base"
|
2
|
+
|
3
|
+
module Sidekiq
|
4
|
+
module Generators # :nodoc:
|
5
|
+
class JobGenerator < ::Rails::Generators::NamedBase # :nodoc:
|
6
|
+
desc "This generator creates a Sidekiq Job in app/sidekiq and a corresponding test"
|
7
|
+
|
8
|
+
check_class_collision suffix: "Job"
|
9
|
+
|
10
|
+
def self.default_generator_root
|
11
|
+
File.dirname(__FILE__)
|
12
|
+
end
|
13
|
+
|
14
|
+
def create_job_file
|
15
|
+
template "job.rb.erb", File.join("app/sidekiq", class_path, "#{file_name}_job.rb")
|
16
|
+
end
|
17
|
+
|
18
|
+
def create_test_file
|
19
|
+
return unless test_framework
|
20
|
+
|
21
|
+
if test_framework == :rspec
|
22
|
+
create_job_spec
|
23
|
+
else
|
24
|
+
create_job_test
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def create_job_spec
|
31
|
+
template_file = File.join(
|
32
|
+
"spec/sidekiq",
|
33
|
+
class_path,
|
34
|
+
"#{file_name}_job_spec.rb"
|
35
|
+
)
|
36
|
+
template "job_spec.rb.erb", template_file
|
37
|
+
end
|
38
|
+
|
39
|
+
def create_job_test
|
40
|
+
template_file = File.join(
|
41
|
+
"test/sidekiq",
|
42
|
+
class_path,
|
43
|
+
"#{file_name}_job_test.rb"
|
44
|
+
)
|
45
|
+
template "job_test.rb.erb", template_file
|
46
|
+
end
|
47
|
+
|
48
|
+
def file_name
|
49
|
+
@_file_name ||= super.sub(/_?job\z/i, "")
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_framework
|
53
|
+
::Rails.application.config.generators.options[:rails][:test_framework]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/lib/sidekiq/api.rb
CHANGED
@@ -113,6 +113,7 @@ module Sidekiq
|
|
113
113
|
|
114
114
|
@stats[:workers_size] = workers_size
|
115
115
|
@stats[:enqueued] = enqueued
|
116
|
+
@stats
|
116
117
|
end
|
117
118
|
|
118
119
|
def fetch_stats!
|
@@ -160,6 +161,8 @@ module Sidekiq
|
|
160
161
|
|
161
162
|
class History
|
162
163
|
def initialize(days_previous, start_date = nil)
|
164
|
+
# we only store five years of data in Redis
|
165
|
+
raise ArgumentError if days_previous < 1 || days_previous > (5 * 365)
|
163
166
|
@days_previous = days_previous
|
164
167
|
@start_date = start_date || Time.now.utc.to_date
|
165
168
|
end
|
@@ -818,10 +821,10 @@ module Sidekiq
|
|
818
821
|
|
819
822
|
hash = Sidekiq.load_json(info)
|
820
823
|
yield Process.new(hash.merge("busy" => busy.to_i,
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
|
824
|
+
"beat" => at_s.to_f,
|
825
|
+
"quiet" => quiet,
|
826
|
+
"rss" => rss.to_i,
|
827
|
+
"rtt_us" => rtt.to_i))
|
825
828
|
end
|
826
829
|
end
|
827
830
|
|
data/lib/sidekiq/cli.rb
CHANGED
@@ -46,7 +46,15 @@ module Sidekiq
|
|
46
46
|
# USR1 and USR2 don't work on the JVM
|
47
47
|
sigs << "USR2" if Sidekiq.pro? && !jruby?
|
48
48
|
sigs.each do |sig|
|
49
|
-
trap
|
49
|
+
old_handler = Signal.trap(sig) do
|
50
|
+
if old_handler.respond_to?(:call)
|
51
|
+
begin
|
52
|
+
old_handler.call
|
53
|
+
rescue Exception => exc
|
54
|
+
# signal handlers can't use Logger so puts only
|
55
|
+
puts ["Error in #{sig} handler", exc].inspect
|
56
|
+
end
|
57
|
+
end
|
50
58
|
self_write.puts(sig)
|
51
59
|
end
|
52
60
|
rescue ArgumentError
|
@@ -372,7 +380,9 @@ module Sidekiq
|
|
372
380
|
end
|
373
381
|
|
374
382
|
def parse_config(path)
|
375
|
-
|
383
|
+
erb = ERB.new(File.read(path))
|
384
|
+
erb.filename = File.expand_path(path)
|
385
|
+
opts = YAML.load(erb.result) || {}
|
376
386
|
|
377
387
|
if opts.respond_to? :deep_symbolize_keys!
|
378
388
|
opts.deep_symbolize_keys!
|
@@ -396,7 +406,7 @@ module Sidekiq
|
|
396
406
|
opts[:queues] ||= []
|
397
407
|
opts[:strict] = true if opts[:strict].nil?
|
398
408
|
raise ArgumentError, "queues: #{queue} cannot be defined twice" if opts[:queues].include?(queue)
|
399
|
-
[weight.to_i, 1].max.times { opts[:queues] << queue }
|
409
|
+
[weight.to_i, 1].max.times { opts[:queues] << queue.to_s }
|
400
410
|
opts[:strict] = false if weight.to_i > 0
|
401
411
|
end
|
402
412
|
|
data/lib/sidekiq/client.rb
CHANGED
@@ -2,9 +2,12 @@
|
|
2
2
|
|
3
3
|
require "securerandom"
|
4
4
|
require "sidekiq/middleware/chain"
|
5
|
+
require "sidekiq/job_util"
|
5
6
|
|
6
7
|
module Sidekiq
|
7
8
|
class Client
|
9
|
+
include Sidekiq::JobUtil
|
10
|
+
|
8
11
|
##
|
9
12
|
# Define client-side middleware:
|
10
13
|
#
|
@@ -95,7 +98,7 @@ module Sidekiq
|
|
95
98
|
return [] if args.empty? # no jobs to push
|
96
99
|
|
97
100
|
at = items.delete("at")
|
98
|
-
raise ArgumentError, "Job 'at' must be a Numeric or an Array of Numeric timestamps" if at && (Array(at).empty? || !Array(at).all?(Numeric))
|
101
|
+
raise ArgumentError, "Job 'at' must be a Numeric or an Array of Numeric timestamps" if at && (Array(at).empty? || !Array(at).all? { |entry| entry.is_a?(Numeric) })
|
99
102
|
raise ArgumentError, "Job 'at' Array must have same size as 'args' Array" if at.is_a?(Array) && at.size != args.size
|
100
103
|
|
101
104
|
normed = normalize_item(items)
|
@@ -186,7 +189,7 @@ module Sidekiq
|
|
186
189
|
|
187
190
|
def raw_push(payloads)
|
188
191
|
@redis_pool.with do |conn|
|
189
|
-
conn.
|
192
|
+
conn.pipelined do
|
190
193
|
atomic_push(conn, payloads)
|
191
194
|
end
|
192
195
|
end
|
@@ -218,42 +221,5 @@ module Sidekiq
|
|
218
221
|
item
|
219
222
|
end
|
220
223
|
end
|
221
|
-
|
222
|
-
def validate(item)
|
223
|
-
raise(ArgumentError, "Job must be a Hash with 'class' and 'args' keys: `#{item}`") unless item.is_a?(Hash) && item.key?("class") && item.key?("args")
|
224
|
-
raise(ArgumentError, "Job args must be an Array: `#{item}`") unless item["args"].is_a?(Array)
|
225
|
-
raise(ArgumentError, "Job class must be either a Class or String representation of the class name: `#{item}`") unless item["class"].is_a?(Class) || item["class"].is_a?(String)
|
226
|
-
raise(ArgumentError, "Job 'at' must be a Numeric timestamp: `#{item}`") if item.key?("at") && !item["at"].is_a?(Numeric)
|
227
|
-
raise(ArgumentError, "Job tags must be an Array: `#{item}`") if item["tags"] && !item["tags"].is_a?(Array)
|
228
|
-
end
|
229
|
-
|
230
|
-
def normalize_item(item)
|
231
|
-
validate(item)
|
232
|
-
# raise(ArgumentError, "Arguments must be native JSON types, see https://github.com/mperham/sidekiq/wiki/Best-Practices") unless JSON.load(JSON.dump(item['args'])) == item['args']
|
233
|
-
|
234
|
-
# merge in the default sidekiq_options for the item's class and/or wrapped element
|
235
|
-
# this allows ActiveJobs to control sidekiq_options too.
|
236
|
-
defaults = normalized_hash(item["class"])
|
237
|
-
defaults = defaults.merge(item["wrapped"].get_sidekiq_options) if item["wrapped"].respond_to?("get_sidekiq_options")
|
238
|
-
item = defaults.merge(item)
|
239
|
-
|
240
|
-
raise(ArgumentError, "Job must include a valid queue name") if item["queue"].nil? || item["queue"] == ""
|
241
|
-
|
242
|
-
item["class"] = item["class"].to_s
|
243
|
-
item["queue"] = item["queue"].to_s
|
244
|
-
item["jid"] ||= SecureRandom.hex(12)
|
245
|
-
item["created_at"] ||= Time.now.to_f
|
246
|
-
|
247
|
-
item
|
248
|
-
end
|
249
|
-
|
250
|
-
def normalized_hash(item_class)
|
251
|
-
if item_class.is_a?(Class)
|
252
|
-
raise(ArgumentError, "Message must include a Sidekiq::Worker class, not class name: #{item_class.ancestors.inspect}") unless item_class.respond_to?("get_sidekiq_options")
|
253
|
-
item_class.get_sidekiq_options
|
254
|
-
else
|
255
|
-
Sidekiq.default_worker_options
|
256
|
-
end
|
257
|
-
end
|
258
224
|
end
|
259
225
|
end
|
data/lib/sidekiq/delay.rb
CHANGED
@@ -3,6 +3,8 @@
|
|
3
3
|
module Sidekiq
|
4
4
|
module Extensions
|
5
5
|
def self.enable_delay!
|
6
|
+
Sidekiq.logger.error "Sidekiq's Delayed Extensions will be removed in Sidekiq 7.0. #{caller(1..1).first}"
|
7
|
+
|
6
8
|
if defined?(::ActiveSupport)
|
7
9
|
require "sidekiq/extensions/active_record"
|
8
10
|
require "sidekiq/extensions/action_mailer"
|
@@ -16,8 +16,8 @@ module Sidekiq
|
|
16
16
|
include Sidekiq::Worker
|
17
17
|
|
18
18
|
def perform(yml)
|
19
|
-
(target, method_name, args) = YAML.load(yml)
|
20
|
-
msg = target.public_send(method_name, *args)
|
19
|
+
(target, method_name, args, kwargs) = YAML.load(yml)
|
20
|
+
msg = kwargs.empty? ? target.public_send(method_name, *args) : target.public_send(method_name, *args, **kwargs)
|
21
21
|
# The email method can return nil, which causes ActionMailer to return
|
22
22
|
# an undeliverable empty message.
|
23
23
|
if msg
|
@@ -18,8 +18,8 @@ module Sidekiq
|
|
18
18
|
include Sidekiq::Worker
|
19
19
|
|
20
20
|
def perform(yml)
|
21
|
-
(target, method_name, args) = YAML.load(yml)
|
22
|
-
target.__send__(method_name, *args)
|
21
|
+
(target, method_name, args, kwargs) = YAML.load(yml)
|
22
|
+
kwargs.empty? ? target.__send__(method_name, *args) : target.__send__(method_name, *args, **kwargs)
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
@@ -16,8 +16,8 @@ module Sidekiq
|
|
16
16
|
include Sidekiq::Worker
|
17
17
|
|
18
18
|
def perform(yml)
|
19
|
-
(target, method_name, args) = YAML.load(yml)
|
20
|
-
target.__send__(method_name, *args)
|
19
|
+
(target, method_name, args, kwargs) = YAML.load(yml)
|
20
|
+
kwargs.empty? ? target.__send__(method_name, *args) : target.__send__(method_name, *args, **kwargs)
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
@@ -13,13 +13,13 @@ module Sidekiq
|
|
13
13
|
@opts = options
|
14
14
|
end
|
15
15
|
|
16
|
-
def method_missing(name, *args)
|
16
|
+
def method_missing(name, *args, **kwargs)
|
17
17
|
# Sidekiq has a limitation in that its message must be JSON.
|
18
18
|
# JSON can't round trip real Ruby objects so we use YAML to
|
19
19
|
# serialize the objects to a String. The YAML will be converted
|
20
20
|
# to JSON and then deserialized on the other side back into a
|
21
21
|
# Ruby object.
|
22
|
-
obj = [@target, name, args]
|
22
|
+
obj = [@target, name, args, kwargs]
|
23
23
|
marshalled = ::YAML.dump(obj)
|
24
24
|
if marshalled.size > SIZE_LIMIT
|
25
25
|
::Sidekiq.logger.warn { "#{@target}.#{name} job argument is #{marshalled.bytesize} bytes, you should refactor it to reduce the size" }
|
data/lib/sidekiq/fetch.rb
CHANGED
data/lib/sidekiq/job.rb
CHANGED
@@ -1,8 +1,13 @@
|
|
1
1
|
require "sidekiq/worker"
|
2
2
|
|
3
3
|
module Sidekiq
|
4
|
-
# Sidekiq::Job is a new alias for Sidekiq::Worker
|
5
|
-
#
|
6
|
-
#
|
4
|
+
# Sidekiq::Job is a new alias for Sidekiq::Worker as of Sidekiq 6.3.0.
|
5
|
+
# Use `include Sidekiq::Job` rather than `include Sidekiq::Worker`.
|
6
|
+
#
|
7
|
+
# The term "worker" is too generic and overly confusing, used in several
|
8
|
+
# different contexts meaning different things. Many people call a Sidekiq
|
9
|
+
# process a "worker". Some people call the thread that executes jobs a
|
10
|
+
# "worker". This change brings Sidekiq closer to ActiveJob where your job
|
11
|
+
# classes extend ApplicationJob.
|
7
12
|
Job = Worker
|
8
13
|
end
|
data/lib/sidekiq/job_retry.rb
CHANGED
@@ -34,9 +34,10 @@ module Sidekiq
|
|
34
34
|
# The job will be retried this number of times before giving up. (If simply
|
35
35
|
# 'true', Sidekiq retries 25 times)
|
36
36
|
#
|
37
|
-
#
|
37
|
+
# Relevant options for job retries:
|
38
38
|
#
|
39
|
-
# * 'queue' - the queue
|
39
|
+
# * 'queue' - the queue for the initial job
|
40
|
+
# * 'retry_queue' - if job retries should be pushed to a different (e.g. lower priority) queue
|
40
41
|
# * 'retry_count' - number of times we've retried so far.
|
41
42
|
# * 'error_message' - the message from the exception
|
42
43
|
# * 'error_class' - the exception class
|
@@ -52,11 +53,12 @@ module Sidekiq
|
|
52
53
|
#
|
53
54
|
# Sidekiq.options[:max_retries] = 7
|
54
55
|
#
|
55
|
-
# or limit the number of retries for a particular worker
|
56
|
+
# or limit the number of retries for a particular worker and send retries to
|
57
|
+
# a low priority queue with:
|
56
58
|
#
|
57
59
|
# class MyWorker
|
58
60
|
# include Sidekiq::Worker
|
59
|
-
# sidekiq_options :
|
61
|
+
# sidekiq_options retry: 10, retry_queue: 'low'
|
60
62
|
# end
|
61
63
|
#
|
62
64
|
class JobRetry
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require "securerandom"
|
2
|
+
require "time"
|
3
|
+
|
4
|
+
module Sidekiq
|
5
|
+
module JobUtil
|
6
|
+
# These functions encapsulate various job utilities.
|
7
|
+
# They must be simple and free from side effects.
|
8
|
+
|
9
|
+
def validate(item)
|
10
|
+
raise(ArgumentError, "Job must be a Hash with 'class' and 'args' keys: `#{item}`") unless item.is_a?(Hash) && item.key?("class") && item.key?("args")
|
11
|
+
raise(ArgumentError, "Job args must be an Array: `#{item}`") unless item["args"].is_a?(Array)
|
12
|
+
raise(ArgumentError, "Job class must be either a Class or String representation of the class name: `#{item}`") unless item["class"].is_a?(Class) || item["class"].is_a?(String)
|
13
|
+
raise(ArgumentError, "Job 'at' must be a Numeric timestamp: `#{item}`") if item.key?("at") && !item["at"].is_a?(Numeric)
|
14
|
+
raise(ArgumentError, "Job tags must be an Array: `#{item}`") if item["tags"] && !item["tags"].is_a?(Array)
|
15
|
+
|
16
|
+
if Sidekiq.options[:on_complex_arguments] == :raise
|
17
|
+
msg = <<~EOM
|
18
|
+
Job arguments to #{item["class"]} must be native JSON types, see https://github.com/mperham/sidekiq/wiki/Best-Practices.
|
19
|
+
To disable this error, remove `Sidekiq.strict_args!` from your initializer.
|
20
|
+
EOM
|
21
|
+
raise(ArgumentError, msg) unless json_safe?(item)
|
22
|
+
elsif Sidekiq.options[:on_complex_arguments] == :warn
|
23
|
+
Sidekiq.logger.warn <<~EOM unless json_safe?(item)
|
24
|
+
Job arguments to #{item["class"]} do not serialize to JSON safely. This will raise an error in
|
25
|
+
Sidekiq 7.0. See https://github.com/mperham/sidekiq/wiki/Best-Practices or raise an error today
|
26
|
+
by calling `Sidekiq.strict_args!` during Sidekiq initialization.
|
27
|
+
EOM
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def normalize_item(item)
|
32
|
+
validate(item)
|
33
|
+
|
34
|
+
# merge in the default sidekiq_options for the item's class and/or wrapped element
|
35
|
+
# this allows ActiveJobs to control sidekiq_options too.
|
36
|
+
defaults = normalized_hash(item["class"])
|
37
|
+
defaults = defaults.merge(item["wrapped"].get_sidekiq_options) if item["wrapped"].respond_to?(:get_sidekiq_options)
|
38
|
+
item = defaults.merge(item)
|
39
|
+
|
40
|
+
raise(ArgumentError, "Job must include a valid queue name") if item["queue"].nil? || item["queue"] == ""
|
41
|
+
|
42
|
+
item["class"] = item["class"].to_s
|
43
|
+
item["queue"] = item["queue"].to_s
|
44
|
+
item["jid"] ||= SecureRandom.hex(12)
|
45
|
+
item["created_at"] ||= Time.now.to_f
|
46
|
+
|
47
|
+
item
|
48
|
+
end
|
49
|
+
|
50
|
+
def normalized_hash(item_class)
|
51
|
+
if item_class.is_a?(Class)
|
52
|
+
raise(ArgumentError, "Message must include a Sidekiq::Worker class, not class name: #{item_class.ancestors.inspect}") unless item_class.respond_to?(:get_sidekiq_options)
|
53
|
+
item_class.get_sidekiq_options
|
54
|
+
else
|
55
|
+
Sidekiq.default_worker_options
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def json_safe?(item)
|
62
|
+
JSON.parse(JSON.dump(item["args"])) == item["args"]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
data/lib/sidekiq/launcher.rb
CHANGED
@@ -69,10 +69,12 @@ module Sidekiq
|
|
69
69
|
|
70
70
|
private unless $TESTING
|
71
71
|
|
72
|
+
BEAT_PAUSE = 5
|
73
|
+
|
72
74
|
def start_heartbeat
|
73
75
|
loop do
|
74
76
|
heartbeat
|
75
|
-
sleep
|
77
|
+
sleep BEAT_PAUSE
|
76
78
|
end
|
77
79
|
Sidekiq.logger.info("Heartbeat stopping...")
|
78
80
|
end
|
@@ -211,6 +213,8 @@ module Sidekiq
|
|
211
213
|
Your Redis network connection is performing extremely poorly.
|
212
214
|
Last RTT readings were #{RTT_READINGS.buffer.inspect}, ideally these should be < 1000.
|
213
215
|
Ensure Redis is running in the same AZ or datacenter as Sidekiq.
|
216
|
+
If these values are close to 100,000, that means your Sidekiq process may be
|
217
|
+
CPU overloaded; see https://github.com/mperham/sidekiq/discussions/5039
|
214
218
|
EOM
|
215
219
|
RTT_READINGS.reset
|
216
220
|
end
|
data/lib/sidekiq/manager.rb
CHANGED
@@ -55,9 +55,6 @@ module Sidekiq
|
|
55
55
|
fire_event(:quiet, reverse: true)
|
56
56
|
end
|
57
57
|
|
58
|
-
# hack for quicker development / testing environment #2774
|
59
|
-
PAUSE_TIME = $stdout.tty? ? 0.1 : 0.5
|
60
|
-
|
61
58
|
def stop(deadline)
|
62
59
|
quiet
|
63
60
|
fire_event(:shutdown, reverse: true)
|
@@ -69,12 +66,7 @@ module Sidekiq
|
|
69
66
|
return if @workers.empty?
|
70
67
|
|
71
68
|
logger.info { "Pausing to allow workers to finish..." }
|
72
|
-
|
73
|
-
while remaining > PAUSE_TIME
|
74
|
-
return if @workers.empty?
|
75
|
-
sleep PAUSE_TIME
|
76
|
-
remaining = deadline - ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
77
|
-
end
|
69
|
+
wait_for(deadline) { @workers.empty? }
|
78
70
|
return if @workers.empty?
|
79
71
|
|
80
72
|
hard_shutdown
|
@@ -130,6 +122,12 @@ module Sidekiq
|
|
130
122
|
cleanup.each do |processor|
|
131
123
|
processor.kill
|
132
124
|
end
|
125
|
+
|
126
|
+
# when this method returns, we immediately call `exit` which may not give
|
127
|
+
# the remaining threads time to run `ensure` blocks, etc. We pause here up
|
128
|
+
# to 3 seconds to give threads a minimal amount of time to run `ensure` blocks.
|
129
|
+
deadline = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC) + 3
|
130
|
+
wait_for(deadline) { @workers.empty? }
|
133
131
|
end
|
134
132
|
end
|
135
133
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require "active_support/current_attributes"
|
2
|
+
|
3
|
+
module Sidekiq
|
4
|
+
##
|
5
|
+
# Automatically save and load any current attributes in the execution context
|
6
|
+
# so context attributes "flow" from Rails actions into any associated jobs.
|
7
|
+
# This can be useful for multi-tenancy, i18n locale, timezone, any implicit
|
8
|
+
# per-request attribute. See +ActiveSupport::CurrentAttributes+.
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
#
|
12
|
+
# # in your initializer
|
13
|
+
# require "sidekiq/middleware/current_attributes"
|
14
|
+
# Sidekiq::CurrentAttributes.persist(Myapp::Current)
|
15
|
+
#
|
16
|
+
module CurrentAttributes
|
17
|
+
class Save
|
18
|
+
def initialize(cattr)
|
19
|
+
@klass = cattr
|
20
|
+
end
|
21
|
+
|
22
|
+
def call(_, job, _, _)
|
23
|
+
attrs = @klass.attributes
|
24
|
+
if job.has_key?("cattr")
|
25
|
+
job["cattr"].merge!(attrs)
|
26
|
+
else
|
27
|
+
job["cattr"] = attrs
|
28
|
+
end
|
29
|
+
yield
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class Load
|
34
|
+
def initialize(cattr)
|
35
|
+
@klass = cattr
|
36
|
+
end
|
37
|
+
|
38
|
+
def call(_, job, _, &block)
|
39
|
+
if job.has_key?("cattr")
|
40
|
+
@klass.set(job["cattr"], &block)
|
41
|
+
else
|
42
|
+
yield
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.persist(klass)
|
48
|
+
Sidekiq.configure_client do |config|
|
49
|
+
config.client_middleware.add Save, klass
|
50
|
+
end
|
51
|
+
Sidekiq.configure_server do |config|
|
52
|
+
config.client_middleware.add Save, klass
|
53
|
+
config.server_middleware.add Load, klass
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|