sidekiq 5.0.1 → 5.2.9
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 +5 -5
- data/.circleci/config.yml +61 -0
- data/.github/issue_template.md +3 -1
- data/.gitignore +2 -0
- data/.travis.yml +6 -13
- data/COMM-LICENSE +11 -9
- data/Changes.md +136 -1
- data/Ent-Changes.md +46 -3
- data/Gemfile +14 -20
- data/LICENSE +1 -1
- data/Pro-4.0-Upgrade.md +35 -0
- data/Pro-Changes.md +125 -0
- data/README.md +5 -3
- data/Rakefile +2 -5
- data/bin/sidekiqctl +13 -92
- data/bin/sidekiqload +2 -2
- data/lib/sidekiq.rb +24 -15
- data/lib/sidekiq/api.rb +83 -37
- data/lib/sidekiq/cli.rb +106 -76
- data/lib/sidekiq/client.rb +36 -33
- data/lib/sidekiq/ctl.rb +221 -0
- data/lib/sidekiq/delay.rb +23 -2
- data/lib/sidekiq/exception_handler.rb +2 -4
- data/lib/sidekiq/fetch.rb +1 -1
- data/lib/sidekiq/job_logger.rb +4 -3
- data/lib/sidekiq/job_retry.rb +51 -24
- data/lib/sidekiq/launcher.rb +18 -12
- data/lib/sidekiq/logging.rb +9 -5
- data/lib/sidekiq/manager.rb +5 -6
- data/lib/sidekiq/middleware/server/active_record.rb +2 -1
- data/lib/sidekiq/processor.rb +85 -48
- data/lib/sidekiq/rails.rb +7 -0
- data/lib/sidekiq/redis_connection.rb +40 -4
- data/lib/sidekiq/scheduled.rb +35 -8
- data/lib/sidekiq/testing.rb +4 -4
- data/lib/sidekiq/util.rb +5 -1
- data/lib/sidekiq/version.rb +1 -1
- data/lib/sidekiq/web.rb +4 -4
- data/lib/sidekiq/web/action.rb +2 -2
- data/lib/sidekiq/web/application.rb +24 -2
- data/lib/sidekiq/web/helpers.rb +18 -8
- data/lib/sidekiq/web/router.rb +10 -10
- data/lib/sidekiq/worker.rb +39 -22
- data/sidekiq.gemspec +6 -17
- data/web/assets/javascripts/application.js +0 -0
- data/web/assets/javascripts/dashboard.js +15 -5
- data/web/assets/stylesheets/application.css +35 -2
- data/web/assets/stylesheets/bootstrap.css +2 -2
- data/web/locales/ar.yml +1 -0
- data/web/locales/en.yml +2 -0
- data/web/locales/es.yml +4 -3
- data/web/locales/ja.yml +5 -3
- data/web/views/_footer.erb +3 -0
- data/web/views/_nav.erb +3 -17
- data/web/views/layout.erb +1 -1
- data/web/views/queue.erb +1 -0
- data/web/views/queues.erb +2 -0
- data/web/views/retries.erb +4 -0
- metadata +20 -156
data/lib/sidekiq/cli.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
# encoding: utf-8
|
2
1
|
# frozen_string_literal: true
|
3
2
|
$stdout.sync = true
|
4
3
|
|
@@ -10,6 +9,7 @@ require 'fileutils'
|
|
10
9
|
|
11
10
|
require 'sidekiq'
|
12
11
|
require 'sidekiq/util'
|
12
|
+
require 'sidekiq/launcher'
|
13
13
|
|
14
14
|
module Sidekiq
|
15
15
|
class CLI
|
@@ -17,30 +17,20 @@ module Sidekiq
|
|
17
17
|
include Singleton unless $TESTING
|
18
18
|
|
19
19
|
PROCTITLES = [
|
20
|
-
proc { 'sidekiq'
|
20
|
+
proc { 'sidekiq' },
|
21
21
|
proc { Sidekiq::VERSION },
|
22
22
|
proc { |me, data| data['tag'] },
|
23
23
|
proc { |me, data| "[#{Processor::WORKER_STATE.size} of #{data['concurrency']} busy]" },
|
24
24
|
proc { |me, data| "stopping" if me.stopping? },
|
25
25
|
]
|
26
26
|
|
27
|
-
# Used for CLI testing
|
28
|
-
attr_accessor :code
|
29
27
|
attr_accessor :launcher
|
30
28
|
attr_accessor :environment
|
31
29
|
|
32
|
-
def
|
33
|
-
@code = nil
|
34
|
-
end
|
35
|
-
|
36
|
-
def parse(args=ARGV)
|
37
|
-
@code = nil
|
38
|
-
|
30
|
+
def parse(args = ARGV)
|
39
31
|
setup_options(args)
|
40
32
|
initialize_logger
|
41
33
|
validate!
|
42
|
-
daemonize
|
43
|
-
write_pid
|
44
34
|
end
|
45
35
|
|
46
36
|
def jruby?
|
@@ -51,8 +41,10 @@ module Sidekiq
|
|
51
41
|
# global process state irreversibly. PRs which improve the
|
52
42
|
# test coverage of Sidekiq::CLI are welcomed.
|
53
43
|
def run
|
44
|
+
daemonize if options[:daemon]
|
45
|
+
write_pid
|
54
46
|
boot_system
|
55
|
-
print_banner
|
47
|
+
print_banner if environment == 'development' && $stdout.tty?
|
56
48
|
|
57
49
|
self_read, self_write = IO.pipe
|
58
50
|
sigs = %w(INT TERM TTIN TSTP)
|
@@ -65,7 +57,7 @@ module Sidekiq
|
|
65
57
|
sigs.each do |sig|
|
66
58
|
begin
|
67
59
|
trap sig do
|
68
|
-
self_write.
|
60
|
+
self_write.write("#{sig}\n")
|
69
61
|
end
|
70
62
|
rescue ArgumentError
|
71
63
|
puts "Signal #{sig} not supported"
|
@@ -80,25 +72,35 @@ module Sidekiq
|
|
80
72
|
# fire startup and start multithreading.
|
81
73
|
ver = Sidekiq.redis_info['redis_version']
|
82
74
|
raise "You are using Redis v#{ver}, Sidekiq requires Redis v2.8.0 or greater" if ver < '2.8'
|
75
|
+
logger.warn "Sidekiq 6.0 will require Redis 4.0+, you are using Redis v#{ver}" if ver < '4'
|
76
|
+
|
77
|
+
# Since the user can pass us a connection pool explicitly in the initializer, we
|
78
|
+
# need to verify the size is large enough or else Sidekiq's performance is dramatically slowed.
|
79
|
+
cursize = Sidekiq.redis_pool.size
|
80
|
+
needed = Sidekiq.options[:concurrency] + 2
|
81
|
+
raise "Your pool of #{cursize} Redis connections is too small, please increase the size to at least #{needed}" if cursize < needed
|
83
82
|
|
84
83
|
# cache process identity
|
85
|
-
|
84
|
+
Sidekiq.options[:identity] = identity
|
86
85
|
|
87
86
|
# Touch middleware so it isn't lazy loaded by multiple threads, #3043
|
88
87
|
Sidekiq.server_middleware
|
89
88
|
|
90
89
|
# Before this point, the process is initializing with just the main thread.
|
91
90
|
# Starting here the process will now have multiple threads running.
|
92
|
-
fire_event(:startup)
|
91
|
+
fire_event(:startup, reverse: false, reraise: true)
|
93
92
|
|
94
93
|
logger.debug { "Client Middleware: #{Sidekiq.client_middleware.map(&:klass).join(', ')}" }
|
95
94
|
logger.debug { "Server Middleware: #{Sidekiq.server_middleware.map(&:klass).join(', ')}" }
|
96
95
|
|
96
|
+
launch(self_read)
|
97
|
+
end
|
98
|
+
|
99
|
+
def launch(self_read)
|
97
100
|
if !options[:daemon]
|
98
101
|
logger.info 'Starting processing, hit Ctrl-C to stop'
|
99
102
|
end
|
100
103
|
|
101
|
-
require 'sidekiq/launcher'
|
102
104
|
@launcher = Sidekiq::Launcher.new(options)
|
103
105
|
|
104
106
|
begin
|
@@ -135,60 +137,60 @@ module Sidekiq
|
|
135
137
|
}
|
136
138
|
end
|
137
139
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
when 'TERM'
|
146
|
-
# Heroku sends TERM and then waits 10 seconds for process to exit.
|
147
|
-
raise Interrupt
|
148
|
-
when 'USR1'
|
140
|
+
SIGNAL_HANDLERS = {
|
141
|
+
# Ctrl-C in terminal
|
142
|
+
'INT' => ->(cli) { raise Interrupt },
|
143
|
+
# TERM is the signal that Sidekiq must exit.
|
144
|
+
# Heroku sends TERM and then waits 30 seconds for process to exit.
|
145
|
+
'TERM' => ->(cli) { raise Interrupt },
|
146
|
+
'USR1' => ->(cli) {
|
149
147
|
Sidekiq.logger.info "Received USR1, no longer accepting new work"
|
150
|
-
launcher.quiet
|
151
|
-
|
152
|
-
|
148
|
+
cli.launcher.quiet
|
149
|
+
},
|
150
|
+
'TSTP' => ->(cli) {
|
153
151
|
Sidekiq.logger.info "Received TSTP, no longer accepting new work"
|
154
|
-
launcher.quiet
|
155
|
-
|
152
|
+
cli.launcher.quiet
|
153
|
+
},
|
154
|
+
'USR2' => ->(cli) {
|
156
155
|
if Sidekiq.options[:logfile]
|
157
156
|
Sidekiq.logger.info "Received USR2, reopening log file"
|
158
157
|
Sidekiq::Logging.reopen_logs
|
159
158
|
end
|
160
|
-
|
159
|
+
},
|
160
|
+
'TTIN' => ->(cli) {
|
161
161
|
Thread.list.each do |thread|
|
162
|
-
Sidekiq.logger.warn "Thread TID-#{thread.object_id.to_s(36)} #{thread['sidekiq_label']}"
|
162
|
+
Sidekiq.logger.warn "Thread TID-#{(thread.object_id ^ ::Process.pid).to_s(36)} #{thread['sidekiq_label']}"
|
163
163
|
if thread.backtrace
|
164
164
|
Sidekiq.logger.warn thread.backtrace.join("\n")
|
165
165
|
else
|
166
166
|
Sidekiq.logger.warn "<no backtrace available>"
|
167
167
|
end
|
168
168
|
end
|
169
|
+
},
|
170
|
+
}
|
171
|
+
|
172
|
+
def handle_signal(sig)
|
173
|
+
Sidekiq.logger.debug "Got #{sig} signal"
|
174
|
+
handy = SIGNAL_HANDLERS[sig]
|
175
|
+
if handy
|
176
|
+
handy.call(self)
|
177
|
+
else
|
178
|
+
Sidekiq.logger.info { "No signal handler for #{sig}" }
|
169
179
|
end
|
170
180
|
end
|
171
181
|
|
172
182
|
private
|
173
183
|
|
174
184
|
def print_banner
|
175
|
-
#
|
176
|
-
|
177
|
-
|
178
|
-
puts Sidekiq::CLI.banner
|
179
|
-
puts "\e[0m"
|
180
|
-
end
|
185
|
+
puts "\e[#{31}m"
|
186
|
+
puts Sidekiq::CLI.banner
|
187
|
+
puts "\e[0m"
|
181
188
|
end
|
182
189
|
|
183
190
|
def daemonize
|
184
|
-
return unless options[:daemon]
|
185
|
-
|
186
191
|
raise ArgumentError, "You really should set a logfile if you're going to daemonize" unless options[:logfile]
|
187
|
-
files_to_reopen = []
|
188
|
-
ObjectSpace.each_object(File) do |file|
|
189
|
-
files_to_reopen << file unless file.closed?
|
190
|
-
end
|
191
192
|
|
193
|
+
files_to_reopen = ObjectSpace.each_object(File).reject { |f| f.closed? }
|
192
194
|
::Process.daemon(true, true)
|
193
195
|
|
194
196
|
files_to_reopen.each do |file|
|
@@ -214,19 +216,50 @@ module Sidekiq
|
|
214
216
|
@environment = cli_env || ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development'
|
215
217
|
end
|
216
218
|
|
219
|
+
def symbolize_keys_deep!(hash)
|
220
|
+
hash.keys.each do |k|
|
221
|
+
symkey = k.respond_to?(:to_sym) ? k.to_sym : k
|
222
|
+
hash[symkey] = hash.delete k
|
223
|
+
symbolize_keys_deep! hash[symkey] if hash[symkey].kind_of? Hash
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
217
227
|
alias_method :die, :exit
|
218
228
|
alias_method :☠, :exit
|
219
229
|
|
220
230
|
def setup_options(args)
|
231
|
+
# parse CLI options
|
221
232
|
opts = parse_options(args)
|
233
|
+
|
222
234
|
set_environment opts[:environment]
|
223
235
|
|
224
|
-
|
225
|
-
|
236
|
+
# check config file presence
|
237
|
+
if opts[:config_file]
|
238
|
+
if opts[:config_file] && !File.exist?(opts[:config_file])
|
239
|
+
raise ArgumentError, "No such file #{opts[:config_file]}"
|
240
|
+
end
|
241
|
+
else
|
242
|
+
config_dir = if File.directory?(opts[:require].to_s)
|
243
|
+
File.join(opts[:require], 'config')
|
244
|
+
else
|
245
|
+
File.join(options[:require], 'config')
|
246
|
+
end
|
247
|
+
|
248
|
+
%w[sidekiq.yml sidekiq.yml.erb].each do |config_file|
|
249
|
+
path = File.join(config_dir, config_file)
|
250
|
+
opts[:config_file] ||= path if File.exist?(path)
|
251
|
+
end
|
252
|
+
end
|
226
253
|
|
254
|
+
# parse config file options
|
255
|
+
opts = parse_config(opts[:config_file]).merge(opts) if opts[:config_file]
|
256
|
+
|
257
|
+
# set defaults
|
258
|
+
opts[:queues] = Array(opts[:queues]) << 'default' if opts[:queues].nil? || opts[:queues].empty?
|
227
259
|
opts[:strict] = true if opts[:strict].nil?
|
228
|
-
opts[:concurrency] = Integer(ENV["RAILS_MAX_THREADS"]) if
|
260
|
+
opts[:concurrency] = Integer(ENV["RAILS_MAX_THREADS"]) if opts[:concurrency].nil? && ENV["RAILS_MAX_THREADS"]
|
229
261
|
|
262
|
+
# merge with defaults
|
230
263
|
options.merge!(opts)
|
231
264
|
end
|
232
265
|
|
@@ -237,8 +270,6 @@ module Sidekiq
|
|
237
270
|
def boot_system
|
238
271
|
ENV['RACK_ENV'] = ENV['RAILS_ENV'] = environment
|
239
272
|
|
240
|
-
raise ArgumentError, "#{options[:require]} does not exist" unless File.exist?(options[:require])
|
241
|
-
|
242
273
|
if File.directory?(options[:require])
|
243
274
|
require 'rails'
|
244
275
|
if ::Rails::VERSION::MAJOR < 4
|
@@ -258,10 +289,7 @@ module Sidekiq
|
|
258
289
|
end
|
259
290
|
options[:tag] ||= default_tag
|
260
291
|
else
|
261
|
-
|
262
|
-
"./#{options[:require]} or /path/to/#{options[:require]}"
|
263
|
-
|
264
|
-
require(options[:require]) || raise(ArgumentError, not_required_message)
|
292
|
+
require options[:require]
|
265
293
|
end
|
266
294
|
end
|
267
295
|
|
@@ -277,12 +305,10 @@ module Sidekiq
|
|
277
305
|
end
|
278
306
|
|
279
307
|
def validate!
|
280
|
-
options[:queues] << 'default' if options[:queues].empty?
|
281
|
-
|
282
308
|
if !File.exist?(options[:require]) ||
|
283
309
|
(File.directory?(options[:require]) && !File.exist?("#{options[:require]}/config/application.rb"))
|
284
310
|
logger.info "=================================================================="
|
285
|
-
logger.info " Please point sidekiq to a Rails
|
311
|
+
logger.info " Please point sidekiq to a Rails 4/5 application or a Ruby file "
|
286
312
|
logger.info " to load your worker classes with -r [DIR|FILE]."
|
287
313
|
logger.info "=================================================================="
|
288
314
|
logger.info @parser
|
@@ -304,6 +330,7 @@ module Sidekiq
|
|
304
330
|
|
305
331
|
o.on '-d', '--daemon', "Daemonize process" do |arg|
|
306
332
|
opts[:daemon] = arg
|
333
|
+
puts "WARNING: Daemonization mode will be removed in Sidekiq 6.0, see #4045. Please use a proper process supervisor to start and manage your services"
|
307
334
|
end
|
308
335
|
|
309
336
|
o.on '-e', '--environment ENV', "Application environment" do |arg|
|
@@ -314,6 +341,8 @@ module Sidekiq
|
|
314
341
|
opts[:tag] = arg
|
315
342
|
end
|
316
343
|
|
344
|
+
# this index remains here for backwards compatibility but none of the Sidekiq
|
345
|
+
# family use this value anymore. it was used by Pro's original reliable_fetch.
|
317
346
|
o.on '-i', '--index INT', "unique process index on this machine" do |arg|
|
318
347
|
opts[:index] = Integer(arg.match(/\d+/)[0])
|
319
348
|
end
|
@@ -341,10 +370,12 @@ module Sidekiq
|
|
341
370
|
|
342
371
|
o.on '-L', '--logfile PATH', "path to writable logfile" do |arg|
|
343
372
|
opts[:logfile] = arg
|
373
|
+
puts "WARNING: Logfile redirection will be removed in Sidekiq 6.0, see #4045. Sidekiq will only log to STDOUT"
|
344
374
|
end
|
345
375
|
|
346
376
|
o.on '-P', '--pidfile PATH', "path to pidfile" do |arg|
|
347
377
|
opts[:pidfile] = arg
|
378
|
+
puts "WARNING: PID file creation will be removed in Sidekiq 6.0, see #4045. Please use a proper process supervisor to start and manage your services"
|
348
379
|
end
|
349
380
|
|
350
381
|
o.on '-V', '--version', "Print version and exit" do |arg|
|
@@ -358,11 +389,8 @@ module Sidekiq
|
|
358
389
|
logger.info @parser
|
359
390
|
die 1
|
360
391
|
end
|
361
|
-
@parser.parse!(argv)
|
362
392
|
|
363
|
-
|
364
|
-
opts[:config_file] ||= filename if File.exist?(filename)
|
365
|
-
end
|
393
|
+
@parser.parse!(argv)
|
366
394
|
|
367
395
|
opts
|
368
396
|
end
|
@@ -382,16 +410,18 @@ module Sidekiq
|
|
382
410
|
end
|
383
411
|
end
|
384
412
|
|
385
|
-
def parse_config(
|
386
|
-
opts = {}
|
387
|
-
|
388
|
-
|
389
|
-
opts
|
390
|
-
parse_queues(opts, opts.delete(:queues) || [])
|
413
|
+
def parse_config(path)
|
414
|
+
opts = YAML.load(ERB.new(File.read(path)).result) || {}
|
415
|
+
|
416
|
+
if opts.respond_to? :deep_symbolize_keys!
|
417
|
+
opts.deep_symbolize_keys!
|
391
418
|
else
|
392
|
-
|
393
|
-
# can be deployed by cap with just the defaults.
|
419
|
+
symbolize_keys_deep!(opts)
|
394
420
|
end
|
421
|
+
|
422
|
+
opts = opts.merge(opts.delete(environment.to_sym) || {})
|
423
|
+
parse_queues(opts, opts.delete(:queues) || [])
|
424
|
+
|
395
425
|
ns = opts.delete(:namespace)
|
396
426
|
if ns
|
397
427
|
# logger hasn't been initialized yet, puts is all we have.
|
@@ -405,10 +435,10 @@ module Sidekiq
|
|
405
435
|
queues_and_weights.each { |queue_and_weight| parse_queue(opts, *queue_and_weight) }
|
406
436
|
end
|
407
437
|
|
408
|
-
def parse_queue(opts,
|
409
|
-
[
|
410
|
-
|
411
|
-
|
438
|
+
def parse_queue(opts, queue, weight = nil)
|
439
|
+
opts[:queues] ||= []
|
440
|
+
raise ArgumentError, "queues: #{queue} cannot be defined twice" if opts[:queues].include?(queue)
|
441
|
+
[weight.to_i, 1].max.times { opts[:queues] << queue }
|
412
442
|
opts[:strict] = false if weight.to_i > 0
|
413
443
|
end
|
414
444
|
end
|
data/lib/sidekiq/client.rb
CHANGED
@@ -51,7 +51,10 @@ module Sidekiq
|
|
51
51
|
# at - timestamp to schedule the job (optional), must be Numeric (e.g. Time.now.to_f)
|
52
52
|
# retry - whether to retry this job if it fails, default true or an integer number of retries
|
53
53
|
# backtrace - whether to save any error backtrace, default false
|
54
|
-
#
|
54
|
+
#
|
55
|
+
# If class is set to the class name, the jobs' options will be based on Sidekiq's default
|
56
|
+
# worker options. Otherwise, they will be based on the job class's options.
|
57
|
+
#
|
55
58
|
# Any options valid for a worker class's sidekiq_options are also available here.
|
56
59
|
#
|
57
60
|
# All options must be strings, not symbols. NB: because we are serializing to JSON, all
|
@@ -65,18 +68,19 @@ module Sidekiq
|
|
65
68
|
#
|
66
69
|
def push(item)
|
67
70
|
normed = normalize_item(item)
|
68
|
-
payload = process_single(item['class'
|
71
|
+
payload = process_single(item['class'], normed)
|
69
72
|
|
70
73
|
if payload
|
71
74
|
raw_push([payload])
|
72
|
-
payload['jid'
|
75
|
+
payload['jid']
|
73
76
|
end
|
74
77
|
end
|
75
78
|
|
76
79
|
##
|
77
|
-
# Push a large number of jobs to Redis.
|
78
|
-
#
|
79
|
-
#
|
80
|
+
# Push a large number of jobs to Redis. This method cuts out the redis
|
81
|
+
# network round trip latency. I wouldn't recommend pushing more than
|
82
|
+
# 1000 per call but YMMV based on network quality, size of job args, etc.
|
83
|
+
# A large number of jobs can cause a bit of Redis command processing latency.
|
80
84
|
#
|
81
85
|
# Takes the same arguments as #push except that args is expected to be
|
82
86
|
# an Array of Arrays. All other keys are duplicated for each job. Each job
|
@@ -86,19 +90,19 @@ module Sidekiq
|
|
86
90
|
# Returns an array of the of pushed jobs' jids. The number of jobs pushed can be less
|
87
91
|
# than the number given if the middleware stopped processing for one or more jobs.
|
88
92
|
def push_bulk(items)
|
89
|
-
arg = items['args'
|
93
|
+
arg = items['args'].first
|
90
94
|
return [] unless arg # no jobs to push
|
91
95
|
raise ArgumentError, "Bulk arguments must be an Array of Arrays: [[1], [2]]" if !arg.is_a?(Array)
|
92
96
|
|
93
97
|
normed = normalize_item(items)
|
94
|
-
payloads = items['args'
|
95
|
-
copy = normed.merge('args'
|
96
|
-
result = process_single(items['class'
|
98
|
+
payloads = items['args'].map do |args|
|
99
|
+
copy = normed.merge('args' => args, 'jid' => SecureRandom.hex(12), 'enqueued_at' => Time.now.to_f)
|
100
|
+
result = process_single(items['class'], copy)
|
97
101
|
result ? result : nil
|
98
102
|
end.compact
|
99
103
|
|
100
104
|
raw_push(payloads) if !payloads.empty?
|
101
|
-
payloads.collect { |payload| payload['jid'
|
105
|
+
payloads.collect { |payload| payload['jid'] }
|
102
106
|
end
|
103
107
|
|
104
108
|
# Allows sharding of jobs across any number of Redis instances. All jobs
|
@@ -116,11 +120,10 @@ module Sidekiq
|
|
116
120
|
def self.via(pool)
|
117
121
|
raise ArgumentError, "No pool given" if pool.nil?
|
118
122
|
current_sidekiq_pool = Thread.current[:sidekiq_via_pool]
|
119
|
-
raise RuntimeError, "Sidekiq::Client.via is not re-entrant" if current_sidekiq_pool && current_sidekiq_pool != pool
|
120
123
|
Thread.current[:sidekiq_via_pool] = pool
|
121
124
|
yield
|
122
125
|
ensure
|
123
|
-
Thread.current[:sidekiq_via_pool] =
|
126
|
+
Thread.current[:sidekiq_via_pool] = current_sidekiq_pool
|
124
127
|
end
|
125
128
|
|
126
129
|
class << self
|
@@ -142,14 +145,14 @@ module Sidekiq
|
|
142
145
|
# Messages are enqueued to the 'default' queue.
|
143
146
|
#
|
144
147
|
def enqueue(klass, *args)
|
145
|
-
klass.client_push('class'
|
148
|
+
klass.client_push('class' => klass, 'args' => args)
|
146
149
|
end
|
147
150
|
|
148
151
|
# Example usage:
|
149
152
|
# Sidekiq::Client.enqueue_to(:queue_name, MyWorker, 'foo', 1, :bat => 'bar')
|
150
153
|
#
|
151
154
|
def enqueue_to(queue, klass, *args)
|
152
|
-
klass.client_push('queue'
|
155
|
+
klass.client_push('queue' => queue, 'class' => klass, 'args' => args)
|
153
156
|
end
|
154
157
|
|
155
158
|
# Example usage:
|
@@ -160,8 +163,8 @@ module Sidekiq
|
|
160
163
|
now = Time.now.to_f
|
161
164
|
ts = (int < 1_000_000_000 ? now + int : int)
|
162
165
|
|
163
|
-
item = { 'class'
|
164
|
-
item.delete('at'
|
166
|
+
item = { 'class' => klass, 'args' => args, 'at' => ts, 'queue' => queue }
|
167
|
+
item.delete('at') if ts <= now
|
165
168
|
|
166
169
|
klass.client_push(item)
|
167
170
|
end
|
@@ -186,25 +189,25 @@ module Sidekiq
|
|
186
189
|
end
|
187
190
|
|
188
191
|
def atomic_push(conn, payloads)
|
189
|
-
if payloads.first['at'
|
190
|
-
conn.zadd('schedule'
|
191
|
-
at = hash.delete('at'
|
192
|
+
if payloads.first['at']
|
193
|
+
conn.zadd('schedule', payloads.map do |hash|
|
194
|
+
at = hash.delete('at').to_s
|
192
195
|
[at, Sidekiq.dump_json(hash)]
|
193
196
|
end)
|
194
197
|
else
|
195
|
-
q = payloads.first['queue'
|
198
|
+
q = payloads.first['queue']
|
196
199
|
now = Time.now.to_f
|
197
200
|
to_push = payloads.map do |entry|
|
198
|
-
entry['enqueued_at'
|
201
|
+
entry['enqueued_at'] = now
|
199
202
|
Sidekiq.dump_json(entry)
|
200
203
|
end
|
201
|
-
conn.sadd('queues'
|
204
|
+
conn.sadd('queues', q)
|
202
205
|
conn.lpush("queue:#{q}", to_push)
|
203
206
|
end
|
204
207
|
end
|
205
208
|
|
206
209
|
def process_single(worker_class, item)
|
207
|
-
queue = item['queue'
|
210
|
+
queue = item['queue']
|
208
211
|
|
209
212
|
middleware.invoke(worker_class, item, queue, @redis_pool) do
|
210
213
|
item
|
@@ -212,25 +215,25 @@ module Sidekiq
|
|
212
215
|
end
|
213
216
|
|
214
217
|
def normalize_item(item)
|
215
|
-
raise(ArgumentError, "Job must be a Hash with 'class' and 'args' keys: { 'class' => SomeWorker, 'args' => ['bob', 1, :foo => 'bar'] }") unless item.is_a?(Hash) && item.has_key?('class'
|
218
|
+
raise(ArgumentError, "Job must be a Hash with 'class' and 'args' keys: { 'class' => SomeWorker, 'args' => ['bob', 1, :foo => 'bar'] }") unless item.is_a?(Hash) && item.has_key?('class') && item.has_key?('args')
|
216
219
|
raise(ArgumentError, "Job args must be an Array") unless item['args'].is_a?(Array)
|
217
|
-
raise(ArgumentError, "Job class must be either a Class or String representation of the class name") unless item['class'
|
218
|
-
raise(ArgumentError, "Job 'at' must be a Numeric timestamp") if item.has_key?('at'
|
220
|
+
raise(ArgumentError, "Job class must be either a Class or String representation of the class name") unless item['class'].is_a?(Class) || item['class'].is_a?(String)
|
221
|
+
raise(ArgumentError, "Job 'at' must be a Numeric timestamp") if item.has_key?('at') && !item['at'].is_a?(Numeric)
|
219
222
|
#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']
|
220
223
|
|
221
|
-
normalized_hash(item['class'
|
224
|
+
normalized_hash(item['class'])
|
222
225
|
.each{ |key, value| item[key] = value if item[key].nil? }
|
223
226
|
|
224
|
-
item['class'
|
225
|
-
item['queue'
|
226
|
-
item['jid'
|
227
|
-
item['created_at'
|
227
|
+
item['class'] = item['class'].to_s
|
228
|
+
item['queue'] = item['queue'].to_s
|
229
|
+
item['jid'] ||= SecureRandom.hex(12)
|
230
|
+
item['created_at'] ||= Time.now.to_f
|
228
231
|
item
|
229
232
|
end
|
230
233
|
|
231
234
|
def normalized_hash(item_class)
|
232
235
|
if item_class.is_a?(Class)
|
233
|
-
raise(ArgumentError, "Message must include a Sidekiq::Worker class, not class name: #{item_class.ancestors.inspect}") if !item_class.respond_to?('get_sidekiq_options'
|
236
|
+
raise(ArgumentError, "Message must include a Sidekiq::Worker class, not class name: #{item_class.ancestors.inspect}") if !item_class.respond_to?('get_sidekiq_options')
|
234
237
|
item_class.get_sidekiq_options
|
235
238
|
else
|
236
239
|
Sidekiq.default_worker_options
|