sidekiq 6.0.7 → 6.4.2
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 +189 -2
- data/LICENSE +3 -3
- data/README.md +11 -10
- data/bin/sidekiq +8 -3
- data/bin/sidekiqload +57 -65
- data/bin/sidekiqmon +1 -1
- 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 +164 -116
- data/lib/sidekiq/cli.rb +49 -15
- data/lib/sidekiq/client.rb +51 -70
- data/lib/sidekiq/delay.rb +2 -0
- data/lib/sidekiq/extensions/action_mailer.rb +3 -2
- data/lib/sidekiq/extensions/active_record.rb +4 -3
- data/lib/sidekiq/extensions/class_methods.rb +5 -4
- data/lib/sidekiq/extensions/generic_proxy.rb +4 -2
- data/lib/sidekiq/fetch.rb +32 -23
- data/lib/sidekiq/job.rb +13 -0
- data/lib/sidekiq/job_logger.rb +16 -28
- data/lib/sidekiq/job_retry.rb +32 -33
- data/lib/sidekiq/job_util.rb +67 -0
- data/lib/sidekiq/launcher.rb +113 -54
- data/lib/sidekiq/logger.rb +11 -20
- data/lib/sidekiq/manager.rb +16 -18
- data/lib/sidekiq/middleware/chain.rb +10 -8
- data/lib/sidekiq/middleware/current_attributes.rb +57 -0
- data/lib/sidekiq/middleware/i18n.rb +4 -4
- data/lib/sidekiq/monitor.rb +1 -1
- data/lib/sidekiq/paginator.rb +8 -8
- data/lib/sidekiq/processor.rb +31 -31
- data/lib/sidekiq/rails.rb +36 -20
- data/lib/sidekiq/redis_connection.rb +16 -15
- data/lib/sidekiq/scheduled.rb +51 -16
- data/lib/sidekiq/sd_notify.rb +1 -1
- data/lib/sidekiq/testing/inline.rb +4 -4
- data/lib/sidekiq/testing.rb +38 -39
- data/lib/sidekiq/util.rb +41 -0
- data/lib/sidekiq/version.rb +1 -1
- data/lib/sidekiq/web/action.rb +2 -2
- data/lib/sidekiq/web/application.rb +21 -12
- data/lib/sidekiq/web/csrf_protection.rb +180 -0
- data/lib/sidekiq/web/helpers.rb +39 -33
- data/lib/sidekiq/web/router.rb +5 -2
- data/lib/sidekiq/web.rb +36 -72
- data/lib/sidekiq/worker.rb +135 -16
- data/lib/sidekiq.rb +33 -17
- data/sidekiq.gemspec +11 -4
- data/web/assets/images/apple-touch-icon.png +0 -0
- data/web/assets/javascripts/application.js +113 -65
- data/web/assets/javascripts/dashboard.js +51 -51
- data/web/assets/stylesheets/application-dark.css +64 -43
- data/web/assets/stylesheets/application-rtl.css +0 -4
- data/web/assets/stylesheets/application.css +42 -239
- data/web/locales/ar.yml +8 -2
- data/web/locales/en.yml +4 -1
- data/web/locales/es.yml +18 -2
- data/web/locales/fr.yml +8 -1
- data/web/locales/ja.yml +3 -0
- data/web/locales/lt.yml +1 -1
- data/web/locales/pl.yml +4 -4
- data/web/locales/ru.yml +4 -0
- data/web/views/_footer.erb +1 -1
- data/web/views/_job_info.erb +1 -1
- data/web/views/_poll_link.erb +2 -5
- data/web/views/_summary.erb +7 -7
- data/web/views/busy.erb +51 -20
- data/web/views/dashboard.erb +22 -14
- data/web/views/dead.erb +1 -1
- data/web/views/layout.erb +2 -1
- data/web/views/morgue.erb +6 -6
- data/web/views/queue.erb +11 -11
- data/web/views/queues.erb +4 -4
- data/web/views/retries.erb +7 -7
- data/web/views/retry.erb +1 -1
- data/web/views/scheduled.erb +1 -1
- metadata +24 -49
- data/.circleci/config.yml +0 -60
- data/.github/contributing.md +0 -32
- data/.github/issue_template.md +0 -11
- data/.gitignore +0 -13
- data/.standard.yml +0 -20
- data/3.0-Upgrade.md +0 -70
- data/4.0-Upgrade.md +0 -53
- data/5.0-Upgrade.md +0 -56
- data/6.0-Upgrade.md +0 -72
- data/COMM-LICENSE +0 -97
- data/Ent-2.0-Upgrade.md +0 -37
- data/Ent-Changes.md +0 -256
- data/Gemfile +0 -24
- data/Gemfile.lock +0 -208
- data/Pro-2.0-Upgrade.md +0 -138
- data/Pro-3.0-Upgrade.md +0 -44
- data/Pro-4.0-Upgrade.md +0 -35
- data/Pro-5.0-Upgrade.md +0 -25
- data/Pro-Changes.md +0 -782
- data/Rakefile +0 -10
- data/code_of_conduct.md +0 -50
- data/lib/generators/sidekiq/worker_generator.rb +0 -57
data/lib/sidekiq/worker.rb
CHANGED
@@ -9,6 +9,7 @@ module Sidekiq
|
|
9
9
|
#
|
10
10
|
# class HardWorker
|
11
11
|
# include Sidekiq::Worker
|
12
|
+
# sidekiq_options queue: 'critical', retry: 5
|
12
13
|
#
|
13
14
|
# def perform(*args)
|
14
15
|
# # do some work
|
@@ -20,6 +21,26 @@ module Sidekiq
|
|
20
21
|
# HardWorker.perform_async(1, 2, 3)
|
21
22
|
#
|
22
23
|
# Note that perform_async is a class method, perform is an instance method.
|
24
|
+
#
|
25
|
+
# Sidekiq::Worker also includes several APIs to provide compatibility with
|
26
|
+
# ActiveJob.
|
27
|
+
#
|
28
|
+
# class SomeWorker
|
29
|
+
# include Sidekiq::Worker
|
30
|
+
# queue_as :critical
|
31
|
+
#
|
32
|
+
# def perform(...)
|
33
|
+
# end
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# SomeWorker.set(wait_until: 1.hour).perform_async(123)
|
37
|
+
#
|
38
|
+
# Note that arguments passed to the job must still obey Sidekiq's
|
39
|
+
# best practice for simple, JSON-native data types. Sidekiq will not
|
40
|
+
# implement ActiveJob's more complex argument serialization. For
|
41
|
+
# this reason, we don't implement `perform_later` as our call semantics
|
42
|
+
# are very different.
|
43
|
+
#
|
23
44
|
module Worker
|
24
45
|
##
|
25
46
|
# The Options module is extracted so we can include it in ActiveJob::Base
|
@@ -61,7 +82,7 @@ module Sidekiq
|
|
61
82
|
end
|
62
83
|
|
63
84
|
def get_sidekiq_options # :nodoc:
|
64
|
-
self.sidekiq_options_hash ||= Sidekiq.
|
85
|
+
self.sidekiq_options_hash ||= Sidekiq.default_job_options
|
65
86
|
end
|
66
87
|
|
67
88
|
def sidekiq_class_attribute(*attrs)
|
@@ -150,33 +171,97 @@ module Sidekiq
|
|
150
171
|
# SomeWorker.set(queue: 'foo').perform_async(....)
|
151
172
|
#
|
152
173
|
class Setter
|
174
|
+
include Sidekiq::JobUtil
|
175
|
+
|
153
176
|
def initialize(klass, opts)
|
154
177
|
@klass = klass
|
155
|
-
|
178
|
+
# NB: the internal hash always has stringified keys
|
179
|
+
@opts = opts.transform_keys(&:to_s)
|
180
|
+
|
181
|
+
# ActiveJob compatibility
|
182
|
+
interval = @opts.delete("wait_until") || @opts.delete("wait")
|
183
|
+
at(interval) if interval
|
156
184
|
end
|
157
185
|
|
158
186
|
def set(options)
|
159
|
-
|
187
|
+
hash = options.transform_keys(&:to_s)
|
188
|
+
interval = hash.delete("wait_until") || @opts.delete("wait")
|
189
|
+
@opts.merge!(hash)
|
190
|
+
at(interval) if interval
|
160
191
|
self
|
161
192
|
end
|
162
193
|
|
163
194
|
def perform_async(*args)
|
164
|
-
@
|
195
|
+
if @opts["sync"] == true
|
196
|
+
perform_inline(*args)
|
197
|
+
else
|
198
|
+
@klass.client_push(@opts.merge("args" => args, "class" => @klass))
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
# Explicit inline execution of a job. Returns nil if the job did not
|
203
|
+
# execute, true otherwise.
|
204
|
+
def perform_inline(*args)
|
205
|
+
raw = @opts.merge("args" => args, "class" => @klass)
|
206
|
+
|
207
|
+
# validate and normalize payload
|
208
|
+
item = normalize_item(raw)
|
209
|
+
queue = item["queue"]
|
210
|
+
|
211
|
+
# run client-side middleware
|
212
|
+
result = Sidekiq.client_middleware.invoke(item["class"], item, queue, Sidekiq.redis_pool) do
|
213
|
+
item
|
214
|
+
end
|
215
|
+
return nil unless result
|
216
|
+
|
217
|
+
# round-trip the payload via JSON
|
218
|
+
msg = Sidekiq.load_json(Sidekiq.dump_json(item))
|
219
|
+
|
220
|
+
# prepare the job instance
|
221
|
+
klass = msg["class"].constantize
|
222
|
+
job = klass.new
|
223
|
+
job.jid = msg["jid"]
|
224
|
+
job.bid = msg["bid"] if job.respond_to?(:bid)
|
225
|
+
|
226
|
+
# run the job through server-side middleware
|
227
|
+
result = Sidekiq.server_middleware.invoke(job, msg, msg["queue"]) do
|
228
|
+
# perform it
|
229
|
+
job.perform(*msg["args"])
|
230
|
+
true
|
231
|
+
end
|
232
|
+
return nil unless result
|
233
|
+
# jobs do not return a result. they should store any
|
234
|
+
# modified state.
|
235
|
+
true
|
236
|
+
end
|
237
|
+
alias_method :perform_sync, :perform_inline
|
238
|
+
|
239
|
+
def perform_bulk(args, batch_size: 1_000)
|
240
|
+
client = @klass.build_client
|
241
|
+
result = args.each_slice(batch_size).flat_map do |slice|
|
242
|
+
client.push_bulk(@opts.merge("class" => @klass, "args" => slice))
|
243
|
+
end
|
244
|
+
|
245
|
+
result.is_a?(Enumerator::Lazy) ? result.force : result
|
165
246
|
end
|
166
247
|
|
167
248
|
# +interval+ must be a timestamp, numeric or something that acts
|
168
249
|
# numeric (like an activesupport time interval).
|
169
250
|
def perform_in(interval, *args)
|
251
|
+
at(interval).perform_async(*args)
|
252
|
+
end
|
253
|
+
alias_method :perform_at, :perform_in
|
254
|
+
|
255
|
+
private
|
256
|
+
|
257
|
+
def at(interval)
|
170
258
|
int = interval.to_f
|
171
259
|
now = Time.now.to_f
|
172
260
|
ts = (int < 1_000_000_000 ? now + int : int)
|
173
|
-
|
174
|
-
payload = @opts.merge("class" => @klass, "args" => args)
|
175
261
|
# Optimization to enqueue something now that is scheduled to go out now or in the past
|
176
|
-
|
177
|
-
|
262
|
+
@opts["at"] = ts if ts > now
|
263
|
+
self
|
178
264
|
end
|
179
|
-
alias_method :perform_at, :perform_in
|
180
265
|
end
|
181
266
|
|
182
267
|
module ClassMethods
|
@@ -192,12 +277,46 @@ module Sidekiq
|
|
192
277
|
raise ArgumentError, "Do not call .delay_until on a Sidekiq::Worker class, call .perform_at"
|
193
278
|
end
|
194
279
|
|
280
|
+
def queue_as(q)
|
281
|
+
sidekiq_options("queue" => q.to_s)
|
282
|
+
end
|
283
|
+
|
195
284
|
def set(options)
|
196
285
|
Setter.new(self, options)
|
197
286
|
end
|
198
287
|
|
199
288
|
def perform_async(*args)
|
200
|
-
|
289
|
+
Setter.new(self, {}).perform_async(*args)
|
290
|
+
end
|
291
|
+
|
292
|
+
# Inline execution of job's perform method after passing through Sidekiq.client_middleware and Sidekiq.server_middleware
|
293
|
+
def perform_inline(*args)
|
294
|
+
Setter.new(self, {}).perform_inline(*args)
|
295
|
+
end
|
296
|
+
alias_method :perform_sync, :perform_inline
|
297
|
+
|
298
|
+
##
|
299
|
+
# Push a large number of jobs to Redis, while limiting the batch of
|
300
|
+
# each job payload to 1,000. This method helps cut down on the number
|
301
|
+
# of round trips to Redis, which can increase the performance of enqueueing
|
302
|
+
# large numbers of jobs.
|
303
|
+
#
|
304
|
+
# +items+ must be an Array of Arrays.
|
305
|
+
#
|
306
|
+
# For finer-grained control, use `Sidekiq::Client.push_bulk` directly.
|
307
|
+
#
|
308
|
+
# Example (3 Redis round trips):
|
309
|
+
#
|
310
|
+
# SomeWorker.perform_async(1)
|
311
|
+
# SomeWorker.perform_async(2)
|
312
|
+
# SomeWorker.perform_async(3)
|
313
|
+
#
|
314
|
+
# Would instead become (1 Redis round trip):
|
315
|
+
#
|
316
|
+
# SomeWorker.perform_bulk([[1], [2], [3]])
|
317
|
+
#
|
318
|
+
def perform_bulk(*args, **kwargs)
|
319
|
+
Setter.new(self, {}).perform_bulk(*args, **kwargs)
|
201
320
|
end
|
202
321
|
|
203
322
|
# +interval+ must be a timestamp, numeric or something that acts
|
@@ -234,13 +353,13 @@ module Sidekiq
|
|
234
353
|
end
|
235
354
|
|
236
355
|
def client_push(item) # :nodoc:
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
item[key.to_s] = item.delete(key)
|
241
|
-
end
|
356
|
+
raise ArgumentError, "Job payloads should contain no Symbols: #{item}" if item.any? { |k, v| k.is_a?(::Symbol) }
|
357
|
+
build_client.push(item)
|
358
|
+
end
|
242
359
|
|
243
|
-
|
360
|
+
def build_client # :nodoc:
|
361
|
+
pool = Thread.current[:sidekiq_via_pool] || get_sidekiq_options["pool"] || Sidekiq.redis_pool
|
362
|
+
Sidekiq::Client.new(pool)
|
244
363
|
end
|
245
364
|
end
|
246
365
|
end
|
data/lib/sidekiq.rb
CHANGED
@@ -6,6 +6,7 @@ fail "Sidekiq #{Sidekiq::VERSION} does not support Ruby versions below 2.5.0." i
|
|
6
6
|
require "sidekiq/logger"
|
7
7
|
require "sidekiq/client"
|
8
8
|
require "sidekiq/worker"
|
9
|
+
require "sidekiq/job"
|
9
10
|
require "sidekiq/redis_connection"
|
10
11
|
require "sidekiq/delay"
|
11
12
|
|
@@ -20,10 +21,12 @@ module Sidekiq
|
|
20
21
|
labels: [],
|
21
22
|
concurrency: 10,
|
22
23
|
require: ".",
|
24
|
+
strict: true,
|
23
25
|
environment: nil,
|
24
26
|
timeout: 25,
|
25
27
|
poll_interval_average: nil,
|
26
28
|
average_scheduled_poll_interval: 5,
|
29
|
+
on_complex_arguments: :warn,
|
27
30
|
error_handlers: [],
|
28
31
|
death_handlers: [],
|
29
32
|
lifecycle_events: {
|
@@ -37,11 +40,6 @@ module Sidekiq
|
|
37
40
|
reloader: proc { |&block| block.call }
|
38
41
|
}
|
39
42
|
|
40
|
-
DEFAULT_WORKER_OPTIONS = {
|
41
|
-
"retry" => true,
|
42
|
-
"queue" => "default"
|
43
|
-
}
|
44
|
-
|
45
43
|
FAKE_INFO = {
|
46
44
|
"redis_version" => "9.9.9",
|
47
45
|
"uptime_in_days" => "9999",
|
@@ -95,10 +93,13 @@ module Sidekiq
|
|
95
93
|
retryable = true
|
96
94
|
begin
|
97
95
|
yield conn
|
98
|
-
rescue Redis::
|
96
|
+
rescue Redis::BaseError => ex
|
99
97
|
# 2550 Failover can cause the server to become a replica, need
|
100
98
|
# to disconnect and reopen the socket to get back to the primary.
|
101
|
-
if
|
99
|
+
# 4495 Use the same logic if we have a "Not enough replicas" error from the primary
|
100
|
+
# 4985 Use the same logic when a blocking command is force-unblocked
|
101
|
+
# The same retry logic is also used in client.rb
|
102
|
+
if retryable && ex.message =~ /READONLY|NOREPLICAS|UNBLOCKED/
|
102
103
|
conn.disconnect!
|
103
104
|
retryable = false
|
104
105
|
retry
|
@@ -152,13 +153,20 @@ module Sidekiq
|
|
152
153
|
Middleware::Chain.new
|
153
154
|
end
|
154
155
|
|
155
|
-
def self.default_worker_options=(hash)
|
156
|
-
|
157
|
-
|
156
|
+
def self.default_worker_options=(hash) # deprecated
|
157
|
+
@default_job_options = default_job_options.merge(hash.transform_keys(&:to_s))
|
158
|
+
end
|
159
|
+
|
160
|
+
def self.default_job_options=(hash)
|
161
|
+
@default_job_options = default_job_options.merge(hash.transform_keys(&:to_s))
|
162
|
+
end
|
163
|
+
|
164
|
+
def self.default_worker_options # deprecated
|
165
|
+
@default_job_options ||= {"retry" => true, "queue" => "default"}
|
158
166
|
end
|
159
167
|
|
160
|
-
def self.
|
161
|
-
|
168
|
+
def self.default_job_options
|
169
|
+
@default_job_options ||= {"retry" => true, "queue" => "default"}
|
162
170
|
end
|
163
171
|
|
164
172
|
##
|
@@ -196,12 +204,12 @@ module Sidekiq
|
|
196
204
|
end
|
197
205
|
|
198
206
|
def self.logger
|
199
|
-
@logger ||= Sidekiq::Logger.new(
|
207
|
+
@logger ||= Sidekiq::Logger.new($stdout, level: :info)
|
200
208
|
end
|
201
209
|
|
202
210
|
def self.logger=(logger)
|
203
211
|
if logger.nil?
|
204
|
-
self.logger.
|
212
|
+
self.logger.fatal!
|
205
213
|
return self.logger
|
206
214
|
end
|
207
215
|
|
@@ -214,6 +222,10 @@ module Sidekiq
|
|
214
222
|
defined?(Sidekiq::Pro)
|
215
223
|
end
|
216
224
|
|
225
|
+
def self.ent?
|
226
|
+
defined?(Sidekiq::Enterprise)
|
227
|
+
end
|
228
|
+
|
217
229
|
# How frequently Redis should be checked by a random Sidekiq process for
|
218
230
|
# scheduled and retriable jobs. Each individual process will take turns by
|
219
231
|
# waiting some multiple of this value.
|
@@ -248,12 +260,16 @@ module Sidekiq
|
|
248
260
|
options[:lifecycle_events][event] << block
|
249
261
|
end
|
250
262
|
|
251
|
-
|
263
|
+
def self.strict_args!(mode = :raise)
|
264
|
+
options[:on_complex_arguments] = mode
|
265
|
+
end
|
266
|
+
|
267
|
+
# We are shutting down Sidekiq but what about threads that
|
252
268
|
# are working on some long job? This error is
|
253
|
-
# raised in
|
269
|
+
# raised in jobs that have not finished within the hard
|
254
270
|
# timeout limit. This is needed to rollback db transactions,
|
255
271
|
# otherwise Ruby's Thread#kill will commit. See #377.
|
256
|
-
# DO NOT RESCUE THIS ERROR IN YOUR
|
272
|
+
# DO NOT RESCUE THIS ERROR IN YOUR JOBS
|
257
273
|
class Shutdown < Interrupt; end
|
258
274
|
end
|
259
275
|
|
data/sidekiq.gemspec
CHANGED
@@ -5,17 +5,24 @@ Gem::Specification.new do |gem|
|
|
5
5
|
gem.email = ["mperham@gmail.com"]
|
6
6
|
gem.summary = "Simple, efficient background processing for Ruby"
|
7
7
|
gem.description = "Simple, efficient background processing for Ruby."
|
8
|
-
gem.homepage = "
|
8
|
+
gem.homepage = "https://sidekiq.org"
|
9
9
|
gem.license = "LGPL-3.0"
|
10
10
|
|
11
11
|
gem.executables = ["sidekiq", "sidekiqmon"]
|
12
|
-
gem.files = `git ls-files | grep -
|
12
|
+
gem.files = ["sidekiq.gemspec", "README.md", "Changes.md", "LICENSE"] + `git ls-files | grep -E '^(bin|lib|web)'`.split("\n")
|
13
13
|
gem.name = "sidekiq"
|
14
14
|
gem.version = Sidekiq::VERSION
|
15
15
|
gem.required_ruby_version = ">= 2.5.0"
|
16
16
|
|
17
|
-
gem.
|
17
|
+
gem.metadata = {
|
18
|
+
"homepage_uri" => "https://sidekiq.org",
|
19
|
+
"bug_tracker_uri" => "https://github.com/mperham/sidekiq/issues",
|
20
|
+
"documentation_uri" => "https://github.com/mperham/sidekiq/wiki",
|
21
|
+
"changelog_uri" => "https://github.com/mperham/sidekiq/blob/main/Changes.md",
|
22
|
+
"source_code_uri" => "https://github.com/mperham/sidekiq"
|
23
|
+
}
|
24
|
+
|
25
|
+
gem.add_dependency "redis", ">= 4.2.0"
|
18
26
|
gem.add_dependency "connection_pool", ">= 2.2.2"
|
19
27
|
gem.add_dependency "rack", "~> 2.0"
|
20
|
-
gem.add_dependency "rack-protection", ">= 2.0.0"
|
21
28
|
end
|
Binary file
|