sidekiq 6.4.0 → 6.5.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/Changes.md +119 -1
  3. data/README.md +6 -1
  4. data/bin/sidekiq +3 -3
  5. data/bin/sidekiqload +70 -66
  6. data/bin/sidekiqmon +1 -1
  7. data/lib/sidekiq/api.rb +255 -100
  8. data/lib/sidekiq/cli.rb +60 -38
  9. data/lib/sidekiq/client.rb +44 -30
  10. data/lib/sidekiq/component.rb +65 -0
  11. data/lib/sidekiq/delay.rb +2 -2
  12. data/lib/sidekiq/extensions/action_mailer.rb +2 -2
  13. data/lib/sidekiq/extensions/active_record.rb +2 -2
  14. data/lib/sidekiq/extensions/class_methods.rb +2 -2
  15. data/lib/sidekiq/extensions/generic_proxy.rb +3 -3
  16. data/lib/sidekiq/fetch.rb +20 -18
  17. data/lib/sidekiq/job_logger.rb +15 -27
  18. data/lib/sidekiq/job_retry.rb +73 -52
  19. data/lib/sidekiq/job_util.rb +15 -9
  20. data/lib/sidekiq/launcher.rb +58 -54
  21. data/lib/sidekiq/logger.rb +8 -18
  22. data/lib/sidekiq/manager.rb +28 -25
  23. data/lib/sidekiq/metrics/deploy.rb +47 -0
  24. data/lib/sidekiq/metrics/query.rb +153 -0
  25. data/lib/sidekiq/metrics/shared.rb +94 -0
  26. data/lib/sidekiq/metrics/tracking.rb +134 -0
  27. data/lib/sidekiq/middleware/chain.rb +82 -38
  28. data/lib/sidekiq/middleware/current_attributes.rb +18 -12
  29. data/lib/sidekiq/middleware/i18n.rb +6 -4
  30. data/lib/sidekiq/middleware/modules.rb +21 -0
  31. data/lib/sidekiq/monitor.rb +2 -2
  32. data/lib/sidekiq/paginator.rb +17 -9
  33. data/lib/sidekiq/processor.rb +47 -41
  34. data/lib/sidekiq/rails.rb +19 -13
  35. data/lib/sidekiq/redis_client_adapter.rb +154 -0
  36. data/lib/sidekiq/redis_connection.rb +80 -49
  37. data/lib/sidekiq/ring_buffer.rb +29 -0
  38. data/lib/sidekiq/scheduled.rb +53 -24
  39. data/lib/sidekiq/testing/inline.rb +4 -4
  40. data/lib/sidekiq/testing.rb +37 -36
  41. data/lib/sidekiq/transaction_aware_client.rb +45 -0
  42. data/lib/sidekiq/version.rb +1 -1
  43. data/lib/sidekiq/web/action.rb +3 -3
  44. data/lib/sidekiq/web/application.rb +21 -5
  45. data/lib/sidekiq/web/csrf_protection.rb +2 -2
  46. data/lib/sidekiq/web/helpers.rb +21 -8
  47. data/lib/sidekiq/web.rb +8 -4
  48. data/lib/sidekiq/worker.rb +26 -20
  49. data/lib/sidekiq.rb +107 -31
  50. data/sidekiq.gemspec +2 -2
  51. data/web/assets/javascripts/application.js +59 -26
  52. data/web/assets/javascripts/chart.min.js +13 -0
  53. data/web/assets/javascripts/chartjs-plugin-annotation.min.js +7 -0
  54. data/web/assets/javascripts/dashboard.js +0 -17
  55. data/web/assets/javascripts/graph.js +16 -0
  56. data/web/assets/javascripts/metrics.js +262 -0
  57. data/web/assets/stylesheets/application.css +45 -1
  58. data/web/locales/el.yml +43 -19
  59. data/web/locales/en.yml +7 -0
  60. data/web/locales/ja.yml +7 -0
  61. data/web/locales/pt-br.yml +27 -9
  62. data/web/locales/zh-cn.yml +36 -11
  63. data/web/locales/zh-tw.yml +32 -7
  64. data/web/views/_nav.erb +1 -1
  65. data/web/views/_summary.erb +1 -1
  66. data/web/views/busy.erb +9 -4
  67. data/web/views/dashboard.erb +1 -0
  68. data/web/views/metrics.erb +69 -0
  69. data/web/views/metrics_for_job.erb +87 -0
  70. data/web/views/queue.erb +5 -1
  71. metadata +34 -9
  72. data/lib/sidekiq/exception_handler.rb +0 -27
  73. data/lib/sidekiq/util.rb +0 -108
@@ -101,20 +101,20 @@ module Sidekiq
101
101
  ##
102
102
  # The Queues class is only for testing the fake queue implementation.
103
103
  # There are 2 data structures involved in tandem. This is due to the
104
- # Rspec syntax of change(QueueWorker.jobs, :size). It keeps a reference
104
+ # Rspec syntax of change(HardJob.jobs, :size). It keeps a reference
105
105
  # to the array. Because the array was dervied from a filter of the total
106
106
  # jobs enqueued, it appeared as though the array didn't change.
107
107
  #
108
108
  # To solve this, we'll keep 2 hashes containing the jobs. One with keys based
109
- # on the queue, and another with keys of the worker names, so the array for
110
- # QueueWorker.jobs is a straight reference to a real array.
109
+ # on the queue, and another with keys of the job type, so the array for
110
+ # HardJob.jobs is a straight reference to a real array.
111
111
  #
112
112
  # Queue-based hash:
113
113
  #
114
114
  # {
115
115
  # "default"=>[
116
116
  # {
117
- # "class"=>"TestTesting::QueueWorker",
117
+ # "class"=>"TestTesting::HardJob",
118
118
  # "args"=>[1, 2],
119
119
  # "retry"=>true,
120
120
  # "queue"=>"default",
@@ -124,12 +124,12 @@ module Sidekiq
124
124
  # ]
125
125
  # }
126
126
  #
127
- # Worker-based hash:
127
+ # Job-based hash:
128
128
  #
129
129
  # {
130
- # "TestTesting::QueueWorker"=>[
130
+ # "TestTesting::HardJob"=>[
131
131
  # {
132
- # "class"=>"TestTesting::QueueWorker",
132
+ # "class"=>"TestTesting::HardJob",
133
133
  # "args"=>[1, 2],
134
134
  # "retry"=>true,
135
135
  # "queue"=>"default",
@@ -144,14 +144,14 @@ module Sidekiq
144
144
  # require 'sidekiq/testing'
145
145
  #
146
146
  # assert_equal 0, Sidekiq::Queues["default"].size
147
- # HardWorker.perform_async(:something)
147
+ # HardJob.perform_async(:something)
148
148
  # assert_equal 1, Sidekiq::Queues["default"].size
149
149
  # assert_equal :something, Sidekiq::Queues["default"].first['args'][0]
150
150
  #
151
- # You can also clear all workers' jobs:
151
+ # You can also clear all jobs:
152
152
  #
153
153
  # assert_equal 0, Sidekiq::Queues["default"].size
154
- # HardWorker.perform_async(:something)
154
+ # HardJob.perform_async(:something)
155
155
  # Sidekiq::Queues.clear_all
156
156
  # assert_equal 0, Sidekiq::Queues["default"].size
157
157
  #
@@ -170,35 +170,36 @@ module Sidekiq
170
170
 
171
171
  def push(queue, klass, job)
172
172
  jobs_by_queue[queue] << job
173
- jobs_by_worker[klass] << job
173
+ jobs_by_class[klass] << job
174
174
  end
175
175
 
176
176
  def jobs_by_queue
177
177
  @jobs_by_queue ||= Hash.new { |hash, key| hash[key] = [] }
178
178
  end
179
179
 
180
- def jobs_by_worker
181
- @jobs_by_worker ||= Hash.new { |hash, key| hash[key] = [] }
180
+ def jobs_by_class
181
+ @jobs_by_class ||= Hash.new { |hash, key| hash[key] = [] }
182
182
  end
183
+ alias_method :jobs_by_worker, :jobs_by_class
183
184
 
184
185
  def delete_for(jid, queue, klass)
185
186
  jobs_by_queue[queue.to_s].delete_if { |job| job["jid"] == jid }
186
- jobs_by_worker[klass].delete_if { |job| job["jid"] == jid }
187
+ jobs_by_class[klass].delete_if { |job| job["jid"] == jid }
187
188
  end
188
189
 
189
190
  def clear_for(queue, klass)
190
- jobs_by_queue[queue].clear
191
- jobs_by_worker[klass].clear
191
+ jobs_by_queue[queue.to_s].clear
192
+ jobs_by_class[klass].clear
192
193
  end
193
194
 
194
195
  def clear_all
195
196
  jobs_by_queue.clear
196
- jobs_by_worker.clear
197
+ jobs_by_class.clear
197
198
  end
198
199
  end
199
200
  end
200
201
 
201
- module Worker
202
+ module Job
202
203
  ##
203
204
  # The Sidekiq testing infrastructure overrides perform_async
204
205
  # so that it does not actually touch the network. Instead it
@@ -212,16 +213,16 @@ module Sidekiq
212
213
  #
213
214
  # require 'sidekiq/testing'
214
215
  #
215
- # assert_equal 0, HardWorker.jobs.size
216
- # HardWorker.perform_async(:something)
217
- # assert_equal 1, HardWorker.jobs.size
218
- # assert_equal :something, HardWorker.jobs[0]['args'][0]
216
+ # assert_equal 0, HardJob.jobs.size
217
+ # HardJob.perform_async(:something)
218
+ # assert_equal 1, HardJob.jobs.size
219
+ # assert_equal :something, HardJob.jobs[0]['args'][0]
219
220
  #
220
221
  # assert_equal 0, Sidekiq::Extensions::DelayedMailer.jobs.size
221
222
  # MyMailer.delay.send_welcome_email('foo@example.com')
222
223
  # assert_equal 1, Sidekiq::Extensions::DelayedMailer.jobs.size
223
224
  #
224
- # You can also clear and drain all workers' jobs:
225
+ # You can also clear and drain all job types:
225
226
  #
226
227
  # assert_equal 0, Sidekiq::Extensions::DelayedMailer.jobs.size
227
228
  # assert_equal 0, Sidekiq::Extensions::DelayedModel.jobs.size
@@ -241,14 +242,14 @@ module Sidekiq
241
242
  #
242
243
  # RSpec.configure do |config|
243
244
  # config.before(:each) do
244
- # Sidekiq::Worker.clear_all
245
+ # Sidekiq::Job.clear_all
245
246
  # end
246
247
  # end
247
248
  #
248
249
  # or for acceptance testing, i.e. with cucumber:
249
250
  #
250
251
  # AfterStep do
251
- # Sidekiq::Worker.drain_all
252
+ # Sidekiq::Job.drain_all
252
253
  # end
253
254
  #
254
255
  # When I sign up as "foo@example.com"
@@ -262,7 +263,7 @@ module Sidekiq
262
263
 
263
264
  # Jobs queued for this worker
264
265
  def jobs
265
- Queues.jobs_by_worker[to_s]
266
+ Queues.jobs_by_class[to_s]
266
267
  end
267
268
 
268
269
  # Clear all jobs for this worker
@@ -288,11 +289,11 @@ module Sidekiq
288
289
  end
289
290
 
290
291
  def process_job(job)
291
- worker = new
292
- worker.jid = job["jid"]
293
- worker.bid = job["bid"] if worker.respond_to?(:bid=)
294
- Sidekiq::Testing.server_middleware.invoke(worker, job, job["queue"]) do
295
- execute_job(worker, job["args"])
292
+ inst = new
293
+ inst.jid = job["jid"]
294
+ inst.bid = job["bid"] if inst.respond_to?(:bid=)
295
+ Sidekiq::Testing.server_middleware.invoke(inst, job, job["queue"]) do
296
+ execute_job(inst, job["args"])
296
297
  end
297
298
  end
298
299
 
@@ -306,18 +307,18 @@ module Sidekiq
306
307
  Queues.jobs_by_queue.values.flatten
307
308
  end
308
309
 
309
- # Clear all queued jobs across all workers
310
+ # Clear all queued jobs
310
311
  def clear_all
311
312
  Queues.clear_all
312
313
  end
313
314
 
314
- # Drain all queued jobs across all workers
315
+ # Drain (execute) all queued jobs
315
316
  def drain_all
316
317
  while jobs.any?
317
- worker_classes = jobs.map { |job| job["class"] }.uniq
318
+ job_classes = jobs.map { |job| job["class"] }.uniq
318
319
 
319
- worker_classes.each do |worker_class|
320
- Sidekiq::Testing.constantize(worker_class).drain
320
+ job_classes.each do |job_class|
321
+ Sidekiq::Testing.constantize(job_class).drain
321
322
  end
322
323
  end
323
324
  end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "securerandom"
4
+ require "sidekiq/client"
5
+
6
+ module Sidekiq
7
+ class TransactionAwareClient
8
+ def initialize(redis_pool)
9
+ @redis_client = Client.new(redis_pool)
10
+ end
11
+
12
+ def push(item)
13
+ # pre-allocate the JID so we can return it immediately and
14
+ # save it to the database as part of the transaction.
15
+ item["jid"] ||= SecureRandom.hex(12)
16
+ AfterCommitEverywhere.after_commit { @redis_client.push(item) }
17
+ item["jid"]
18
+ end
19
+
20
+ ##
21
+ # We don't provide transactionality for push_bulk because we don't want
22
+ # to hold potentially hundreds of thousands of job records in memory due to
23
+ # a long running enqueue process.
24
+ def push_bulk(items)
25
+ @redis_client.push_bulk(items)
26
+ end
27
+ end
28
+ end
29
+
30
+ ##
31
+ # Use `Sidekiq.transactional_push!` in your sidekiq.rb initializer
32
+ module Sidekiq
33
+ def self.transactional_push!
34
+ begin
35
+ require "after_commit_everywhere"
36
+ rescue LoadError
37
+ Sidekiq.logger.error("You need to add after_commit_everywhere to your Gemfile to use Sidekiq's transactional client")
38
+ raise
39
+ end
40
+
41
+ default_job_options["client_class"] = Sidekiq::TransactionAwareClient
42
+ Sidekiq::JobUtil::TRANSIENT_ATTRIBUTES << "client_class"
43
+ true
44
+ end
45
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sidekiq
4
- VERSION = "6.4.0"
4
+ VERSION = "6.5.12"
5
5
  end
@@ -15,11 +15,11 @@ module Sidekiq
15
15
  end
16
16
 
17
17
  def halt(res)
18
- throw :halt, [res, {"Content-Type" => "text/plain"}, [res.to_s]]
18
+ throw :halt, [res, {"content-type" => "text/plain"}, [res.to_s]]
19
19
  end
20
20
 
21
21
  def redirect(location)
22
- throw :halt, [302, {"Location" => "#{request.base_url}#{location}"}, []]
22
+ throw :halt, [302, {"location" => "#{request.base_url}#{location}"}, []]
23
23
  end
24
24
 
25
25
  def params
@@ -68,7 +68,7 @@ module Sidekiq
68
68
  end
69
69
 
70
70
  def json(payload)
71
- [200, {"Content-Type" => "application/json", "Cache-Control" => "private, no-store"}, [Sidekiq.dump_json(payload)]]
71
+ [200, {"content-type" => "application/json", "cache-control" => "private, no-store"}, [Sidekiq.dump_json(payload)]]
72
72
  end
73
73
 
74
74
  def initialize(env, block)
@@ -60,7 +60,23 @@ module Sidekiq
60
60
  erb(:dashboard)
61
61
  end
62
62
 
63
+ get "/metrics" do
64
+ q = Sidekiq::Metrics::Query.new
65
+ @query_result = q.top_jobs
66
+ erb(:metrics)
67
+ end
68
+
69
+ get "/metrics/:name" do
70
+ @name = route_params[:name]
71
+ q = Sidekiq::Metrics::Query.new
72
+ @query_result = q.for_job(@name)
73
+ erb(:metrics_for_job)
74
+ end
75
+
63
76
  get "/busy" do
77
+ @count = (params["count"] || 100).to_i
78
+ (@current_page, @total_size, @workset) = page_items(workset, params["page"], @count)
79
+
64
80
  erb(:busy)
65
81
  end
66
82
 
@@ -299,7 +315,7 @@ module Sidekiq
299
315
 
300
316
  def call(env)
301
317
  action = self.class.match(env)
302
- return [404, {"Content-Type" => "text/plain", "X-Cascade" => "pass"}, ["Not Found"]] unless action
318
+ return [404, {"content-type" => "text/plain", "x-cascade" => "pass"}, ["Not Found"]] unless action
303
319
 
304
320
  app = @klass
305
321
  resp = catch(:halt) do
@@ -316,10 +332,10 @@ module Sidekiq
316
332
  else
317
333
  # rendered content goes here
318
334
  headers = {
319
- "Content-Type" => "text/html",
320
- "Cache-Control" => "private, no-store",
321
- "Content-Language" => action.locale,
322
- "Content-Security-Policy" => CSP_HEADER
335
+ "content-type" => "text/html",
336
+ "cache-control" => "private, no-store",
337
+ "content-language" => action.locale,
338
+ "content-security-policy" => CSP_HEADER
323
339
  }
324
340
  # we'll let Rack calculate Content-Length for us.
325
341
  [200, headers, [resp]]
@@ -143,7 +143,7 @@ module Sidekiq
143
143
  one_time_pad = SecureRandom.random_bytes(token.length)
144
144
  encrypted_token = xor_byte_strings(one_time_pad, token)
145
145
  masked_token = one_time_pad + encrypted_token
146
- Base64.strict_encode64(masked_token)
146
+ Base64.urlsafe_encode64(masked_token)
147
147
  end
148
148
 
149
149
  # Essentially the inverse of +mask_token+.
@@ -169,7 +169,7 @@ module Sidekiq
169
169
  end
170
170
 
171
171
  def decode_token(token)
172
- Base64.strict_decode64(token)
172
+ Base64.urlsafe_decode64(token)
173
173
  end
174
174
 
175
175
  def xor_byte_strings(s1, s2)
@@ -15,7 +15,7 @@ module Sidekiq
15
15
  # so extensions can be localized
16
16
  @strings[lang] ||= settings.locales.each_with_object({}) do |path, global|
17
17
  find_locale_files(lang).each do |file|
18
- strs = YAML.load(File.open(file))
18
+ strs = YAML.safe_load(File.open(file))
19
19
  global.merge!(strs[lang])
20
20
  end
21
21
  end
@@ -137,17 +137,30 @@ module Sidekiq
137
137
  end
138
138
 
139
139
  def sort_direction_label
140
- params[:direction] == "asc" ? "&uarr;" : "&darr;"
140
+ (params[:direction] == "asc") ? "&uarr;" : "&darr;"
141
141
  end
142
142
 
143
- def workers
144
- @workers ||= Sidekiq::Workers.new
143
+ def workset
144
+ @work ||= Sidekiq::WorkSet.new
145
145
  end
146
146
 
147
147
  def processes
148
148
  @processes ||= Sidekiq::ProcessSet.new
149
149
  end
150
150
 
151
+ # Sorts processes by hostname following the natural sort order
152
+ def sorted_processes
153
+ @sorted_processes ||= begin
154
+ return processes unless processes.all? { |p| p["hostname"] }
155
+
156
+ processes.to_a.sort_by do |process|
157
+ # Kudos to `shurikk` on StackOverflow
158
+ # https://stackoverflow.com/a/15170063/575547
159
+ process["hostname"].split(/(\d+)/).map { |a| /\d+/.match?(a) ? a.to_i : a }
160
+ end
161
+ end
162
+ end
163
+
151
164
  def stats
152
165
  @stats ||= Sidekiq::Stats.new
153
166
  end
@@ -175,7 +188,7 @@ module Sidekiq
175
188
  end
176
189
 
177
190
  def current_status
178
- workers.size == 0 ? "idle" : "active"
191
+ (workset.size == 0) ? "idle" : "active"
179
192
  end
180
193
 
181
194
  def relative_time(time)
@@ -208,7 +221,7 @@ module Sidekiq
208
221
  end
209
222
 
210
223
  def truncate(text, truncate_after_chars = 2000)
211
- truncate_after_chars && text.size > truncate_after_chars ? "#{text[0..truncate_after_chars]}..." : text
224
+ (truncate_after_chars && text.size > truncate_after_chars) ? "#{text[0..truncate_after_chars]}..." : text
212
225
  end
213
226
 
214
227
  def display_args(args, truncate_after_chars = 2000)
@@ -242,7 +255,7 @@ module Sidekiq
242
255
  queue class args retry_count retried_at failed_at
243
256
  jid error_message error_class backtrace
244
257
  error_backtrace enqueued_at retry wrapped
245
- created_at tags
258
+ created_at tags display_class
246
259
  ])
247
260
 
248
261
  def retry_extra_items(retry_job)
@@ -301,7 +314,7 @@ module Sidekiq
301
314
  end
302
315
 
303
316
  def environment_title_prefix
304
- environment = Sidekiq.options[:environment] || ENV["APP_ENV"] || ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
317
+ environment = Sidekiq[:environment] || ENV["APP_ENV"] || ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
305
318
 
306
319
  "[#{environment.upcase}] " unless environment == "production"
307
320
  end
data/lib/sidekiq/web.rb CHANGED
@@ -33,6 +33,10 @@ module Sidekiq
33
33
  "Dead" => "morgue"
34
34
  }
35
35
 
36
+ if ENV["SIDEKIQ_METRICS_BETA"] == "1"
37
+ DEFAULT_TABS["Metrics"] = "metrics"
38
+ end
39
+
36
40
  class << self
37
41
  def settings
38
42
  self
@@ -144,13 +148,13 @@ module Sidekiq
144
148
  m = middlewares
145
149
 
146
150
  rules = []
147
- rules = [[:all, {"Cache-Control" => "public, max-age=86400"}]] unless ENV["SIDEKIQ_WEB_TESTING"]
151
+ rules = [[:all, {"cache-control" => "public, max-age=86400"}]] unless ENV["SIDEKIQ_WEB_TESTING"]
148
152
 
149
153
  ::Rack::Builder.new do
150
154
  use Rack::Static, urls: ["/stylesheets", "/images", "/javascripts"],
151
- root: ASSETS,
152
- cascade: true,
153
- header_rules: rules
155
+ root: ASSETS,
156
+ cascade: true,
157
+ header_rules: rules
154
158
  m.each { |middleware, block| use(*middleware, &block) }
155
159
  use Sidekiq::Web::CsrfProtection unless $TESTING
156
160
  run WebApplication.new(klass)
@@ -82,7 +82,7 @@ module Sidekiq
82
82
  end
83
83
 
84
84
  def get_sidekiq_options # :nodoc:
85
- self.sidekiq_options_hash ||= Sidekiq.default_worker_options
85
+ self.sidekiq_options_hash ||= Sidekiq.default_job_options
86
86
  end
87
87
 
88
88
  def sidekiq_class_attribute(*attrs)
@@ -175,16 +175,18 @@ module Sidekiq
175
175
 
176
176
  def initialize(klass, opts)
177
177
  @klass = klass
178
- @opts = opts
178
+ # NB: the internal hash always has stringified keys
179
+ @opts = opts.transform_keys(&:to_s)
179
180
 
180
181
  # ActiveJob compatibility
181
- interval = @opts.delete(:wait_until) || @opts.delete(:wait)
182
+ interval = @opts.delete("wait_until") || @opts.delete("wait")
182
183
  at(interval) if interval
183
184
  end
184
185
 
185
186
  def set(options)
186
- interval = options.delete(:wait_until) || options.delete(:wait)
187
- @opts.merge!(options)
187
+ hash = options.transform_keys(&:to_s)
188
+ interval = hash.delete("wait_until") || @opts.delete("wait")
189
+ @opts.merge!(hash)
188
190
  at(interval) if interval
189
191
  self
190
192
  end
@@ -200,7 +202,7 @@ module Sidekiq
200
202
  # Explicit inline execution of a job. Returns nil if the job did not
201
203
  # execute, true otherwise.
202
204
  def perform_inline(*args)
203
- raw = @opts.merge("args" => args, "class" => @klass).transform_keys(&:to_s)
205
+ raw = @opts.merge("args" => args, "class" => @klass)
204
206
 
205
207
  # validate and normalize payload
206
208
  item = normalize_item(raw)
@@ -235,9 +237,9 @@ module Sidekiq
235
237
  alias_method :perform_sync, :perform_inline
236
238
 
237
239
  def perform_bulk(args, batch_size: 1_000)
238
- hash = @opts.transform_keys(&:to_s)
240
+ client = @klass.build_client
239
241
  result = args.each_slice(batch_size).flat_map do |slice|
240
- Sidekiq::Client.push_bulk(hash.merge("class" => @klass, "args" => slice))
242
+ client.push_bulk(@opts.merge("class" => @klass, "args" => slice))
241
243
  end
242
244
 
243
245
  result.is_a?(Enumerator::Lazy) ? result.force : result
@@ -255,7 +257,7 @@ module Sidekiq
255
257
  def at(interval)
256
258
  int = interval.to_f
257
259
  now = Time.now.to_f
258
- ts = (int < 1_000_000_000 ? now + int : int)
260
+ ts = ((int < 1_000_000_000) ? now + int : int)
259
261
  # Optimization to enqueue something now that is scheduled to go out now or in the past
260
262
  @opts["at"] = ts if ts > now
261
263
  self
@@ -291,6 +293,7 @@ module Sidekiq
291
293
  def perform_inline(*args)
292
294
  Setter.new(self, {}).perform_inline(*args)
293
295
  end
296
+ alias_method :perform_sync, :perform_inline
294
297
 
295
298
  ##
296
299
  # Push a large number of jobs to Redis, while limiting the batch of
@@ -312,12 +315,8 @@ module Sidekiq
312
315
  #
313
316
  # SomeWorker.perform_bulk([[1], [2], [3]])
314
317
  #
315
- def perform_bulk(items, batch_size: 1_000)
316
- result = items.each_slice(batch_size).flat_map do |slice|
317
- Sidekiq::Client.push_bulk("class" => self, "args" => slice)
318
- end
319
-
320
- result.is_a?(Enumerator::Lazy) ? result.force : result
318
+ def perform_bulk(*args, **kwargs)
319
+ Setter.new(self, {}).perform_bulk(*args, **kwargs)
321
320
  end
322
321
 
323
322
  # +interval+ must be a timestamp, numeric or something that acts
@@ -325,7 +324,7 @@ module Sidekiq
325
324
  def perform_in(interval, *args)
326
325
  int = interval.to_f
327
326
  now = Time.now.to_f
328
- ts = (int < 1_000_000_000 ? now + int : int)
327
+ ts = ((int < 1_000_000_000) ? now + int : int)
329
328
 
330
329
  item = {"class" => self, "args" => args}
331
330
 
@@ -341,7 +340,7 @@ module Sidekiq
341
340
  # Legal options:
342
341
  #
343
342
  # queue - use a named queue for this Worker, default 'default'
344
- # retry - enable the RetryJobs middleware for this Worker, *true* to use the default
343
+ # retry - enable retries via JobRetry, *true* to use the default
345
344
  # or *Integer* count
346
345
  # backtrace - whether to save any error backtrace in the retry payload to display in web UI,
347
346
  # can be true, false or an integer number of lines to save, default *false*
@@ -349,15 +348,22 @@ module Sidekiq
349
348
  #
350
349
  # In practice, any option is allowed. This is the main mechanism to configure the
351
350
  # options for a specific job.
351
+ #
352
+ # These options will be saved into the serialized job when enqueued by
353
+ # the client.
352
354
  def sidekiq_options(opts = {})
353
355
  super
354
356
  end
355
357
 
356
358
  def client_push(item) # :nodoc:
357
- pool = Thread.current[:sidekiq_via_pool] || get_sidekiq_options["pool"] || Sidekiq.redis_pool
358
- stringified_item = item.transform_keys(&:to_s)
359
+ raise ArgumentError, "Job payloads should contain no Symbols: #{item}" if item.any? { |k, v| k.is_a?(::Symbol) }
360
+ build_client.push(item)
361
+ end
359
362
 
360
- Sidekiq::Client.new(pool).push(stringified_item)
363
+ def build_client # :nodoc:
364
+ pool = Thread.current[:sidekiq_via_pool] || get_sidekiq_options["pool"] || Sidekiq.redis_pool
365
+ client_class = get_sidekiq_options["client_class"] || Sidekiq::Client
366
+ client_class.new(pool)
361
367
  end
362
368
  end
363
369
  end