sidekiq 6.3.1 → 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.

Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/Changes.md +56 -0
  3. data/LICENSE +3 -3
  4. data/README.md +7 -2
  5. data/bin/sidekiq +3 -3
  6. data/bin/sidekiqload +57 -65
  7. data/bin/sidekiqmon +1 -1
  8. data/lib/generators/sidekiq/job_generator.rb +57 -0
  9. data/lib/generators/sidekiq/templates/{worker.rb.erb → job.rb.erb} +2 -2
  10. data/lib/generators/sidekiq/templates/{worker_spec.rb.erb → job_spec.rb.erb} +1 -1
  11. data/lib/generators/sidekiq/templates/{worker_test.rb.erb → job_test.rb.erb} +1 -1
  12. data/lib/sidekiq/api.rb +68 -62
  13. data/lib/sidekiq/cli.rb +16 -6
  14. data/lib/sidekiq/client.rb +44 -64
  15. data/lib/sidekiq/delay.rb +2 -0
  16. data/lib/sidekiq/extensions/generic_proxy.rb +1 -1
  17. data/lib/sidekiq/fetch.rb +2 -2
  18. data/lib/sidekiq/job_logger.rb +15 -27
  19. data/lib/sidekiq/job_retry.rb +28 -26
  20. data/lib/sidekiq/job_util.rb +67 -0
  21. data/lib/sidekiq/launcher.rb +37 -36
  22. data/lib/sidekiq/logger.rb +8 -18
  23. data/lib/sidekiq/manager.rb +13 -15
  24. data/lib/sidekiq/middleware/chain.rb +4 -4
  25. data/lib/sidekiq/middleware/current_attributes.rb +6 -1
  26. data/lib/sidekiq/middleware/i18n.rb +4 -4
  27. data/lib/sidekiq/monitor.rb +1 -1
  28. data/lib/sidekiq/paginator.rb +8 -8
  29. data/lib/sidekiq/processor.rb +27 -27
  30. data/lib/sidekiq/rails.rb +11 -4
  31. data/lib/sidekiq/redis_connection.rb +2 -2
  32. data/lib/sidekiq/scheduled.rb +13 -3
  33. data/lib/sidekiq/testing/inline.rb +4 -4
  34. data/lib/sidekiq/testing.rb +36 -35
  35. data/lib/sidekiq/util.rb +13 -0
  36. data/lib/sidekiq/version.rb +1 -1
  37. data/lib/sidekiq/web/application.rb +5 -2
  38. data/lib/sidekiq/web/csrf_protection.rb +2 -2
  39. data/lib/sidekiq/web/helpers.rb +4 -4
  40. data/lib/sidekiq/web.rb +3 -3
  41. data/lib/sidekiq/worker.rb +71 -16
  42. data/lib/sidekiq.rb +27 -15
  43. data/web/assets/javascripts/application.js +58 -26
  44. data/web/assets/stylesheets/application-dark.css +13 -17
  45. data/web/assets/stylesheets/application.css +4 -5
  46. data/web/views/_summary.erb +1 -1
  47. data/web/views/busy.erb +3 -3
  48. metadata +8 -7
  49. data/lib/generators/sidekiq/worker_generator.rb +0 -57
@@ -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
191
  jobs_by_queue[queue].clear
191
- jobs_by_worker[klass].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
data/lib/sidekiq/util.rb CHANGED
@@ -39,6 +39,19 @@ module Sidekiq
39
39
  module Util
40
40
  include ExceptionHandler
41
41
 
42
+ # hack for quicker development / testing environment #2774
43
+ PAUSE_TIME = $stdout.tty? ? 0.1 : 0.5
44
+
45
+ # Wait for the orblock to be true or the deadline passed.
46
+ def wait_for(deadline, &condblock)
47
+ remaining = deadline - ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
48
+ while remaining > PAUSE_TIME
49
+ return if condblock.call
50
+ sleep PAUSE_TIME
51
+ remaining = deadline - ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
52
+ end
53
+ end
54
+
42
55
  def watchdog(last_words)
43
56
  yield
44
57
  rescue Exception => ex
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sidekiq
4
- VERSION = "6.3.1"
4
+ VERSION = "6.4.2"
5
5
  end
@@ -50,7 +50,10 @@ module Sidekiq
50
50
 
51
51
  get "/" do
52
52
  @redis_info = redis_info.select { |k, v| REDIS_KEYS.include? k }
53
- stats_history = Sidekiq::Stats::History.new((params["days"] || 30).to_i)
53
+ days = (params["days"] || 30).to_i
54
+ return halt(401) if days < 1 || days > 180
55
+
56
+ stats_history = Sidekiq::Stats::History.new(days)
54
57
  @processed_history = stats_history.processed
55
58
  @failed_history = stats_history.failed
56
59
 
@@ -299,7 +302,7 @@ module Sidekiq
299
302
  return [404, {"Content-Type" => "text/plain", "X-Cascade" => "pass"}, ["Not Found"]] unless action
300
303
 
301
304
  app = @klass
302
- resp = catch(:halt) do # rubocop:disable Standard/SemanticBlocks
305
+ resp = catch(:halt) do
303
306
  self.class.run_befores(app, action)
304
307
  action.instance_exec env, &action.block
305
308
  ensure
@@ -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)
@@ -140,8 +140,8 @@ module Sidekiq
140
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
@@ -175,7 +175,7 @@ module Sidekiq
175
175
  end
176
176
 
177
177
  def current_status
178
- workers.size == 0 ? "idle" : "active"
178
+ workset.size == 0 ? "idle" : "active"
179
179
  end
180
180
 
181
181
  def relative_time(time)
@@ -242,7 +242,7 @@ module Sidekiq
242
242
  queue class args retry_count retried_at failed_at
243
243
  jid error_message error_class backtrace
244
244
  error_backtrace enqueued_at retry wrapped
245
- created_at tags
245
+ created_at tags display_class
246
246
  ])
247
247
 
248
248
  def retry_extra_items(retry_job)
data/lib/sidekiq/web.rb CHANGED
@@ -148,9 +148,9 @@ module Sidekiq
148
148
 
149
149
  ::Rack::Builder.new do
150
150
  use Rack::Static, urls: ["/stylesheets", "/images", "/javascripts"],
151
- root: ASSETS,
152
- cascade: true,
153
- header_rules: rules
151
+ root: ASSETS,
152
+ cascade: true,
153
+ header_rules: rules
154
154
  m.each { |middleware, block| use(*middleware, &block) }
155
155
  use Sidekiq::Web::CsrfProtection unless $TESTING
156
156
  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)
@@ -171,30 +171,78 @@ module Sidekiq
171
171
  # SomeWorker.set(queue: 'foo').perform_async(....)
172
172
  #
173
173
  class Setter
174
+ include Sidekiq::JobUtil
175
+
174
176
  def initialize(klass, opts)
175
177
  @klass = klass
176
- @opts = opts
178
+ # NB: the internal hash always has stringified keys
179
+ @opts = opts.transform_keys(&:to_s)
177
180
 
178
181
  # ActiveJob compatibility
179
- interval = @opts.delete(:wait_until) || @opts.delete(:wait)
182
+ interval = @opts.delete("wait_until") || @opts.delete("wait")
180
183
  at(interval) if interval
181
184
  end
182
185
 
183
186
  def set(options)
184
- interval = options.delete(:wait_until) || options.delete(:wait)
185
- @opts.merge!(options)
187
+ hash = options.transform_keys(&:to_s)
188
+ interval = hash.delete("wait_until") || @opts.delete("wait")
189
+ @opts.merge!(hash)
186
190
  at(interval) if interval
187
191
  self
188
192
  end
189
193
 
190
194
  def perform_async(*args)
191
- @klass.client_push(@opts.merge("args" => args, "class" => @klass))
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
192
236
  end
237
+ alias_method :perform_sync, :perform_inline
193
238
 
194
239
  def perform_bulk(args, batch_size: 1_000)
195
- args.each_slice(batch_size).flat_map do |slice|
196
- Sidekiq::Client.push_bulk(@opts.merge("class" => @klass, "args" => slice))
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))
197
243
  end
244
+
245
+ result.is_a?(Enumerator::Lazy) ? result.force : result
198
246
  end
199
247
 
200
248
  # +interval+ must be a timestamp, numeric or something that acts
@@ -238,8 +286,14 @@ module Sidekiq
238
286
  end
239
287
 
240
288
  def perform_async(*args)
241
- client_push("class" => self, "args" => args)
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)
242
295
  end
296
+ alias_method :perform_sync, :perform_inline
243
297
 
244
298
  ##
245
299
  # Push a large number of jobs to Redis, while limiting the batch of
@@ -261,10 +315,8 @@ module Sidekiq
261
315
  #
262
316
  # SomeWorker.perform_bulk([[1], [2], [3]])
263
317
  #
264
- def perform_bulk(items, batch_size: 1_000)
265
- items.each_slice(batch_size).flat_map do |slice|
266
- Sidekiq::Client.push_bulk("class" => self, "args" => slice)
267
- end
318
+ def perform_bulk(*args, **kwargs)
319
+ Setter.new(self, {}).perform_bulk(*args, **kwargs)
268
320
  end
269
321
 
270
322
  # +interval+ must be a timestamp, numeric or something that acts
@@ -301,10 +353,13 @@ module Sidekiq
301
353
  end
302
354
 
303
355
  def client_push(item) # :nodoc:
304
- pool = Thread.current[:sidekiq_via_pool] || get_sidekiq_options["pool"] || Sidekiq.redis_pool
305
- stringified_item = item.transform_keys(&:to_s)
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
306
359
 
307
- Sidekiq::Client.new(pool).push(stringified_item)
360
+ def build_client # :nodoc:
361
+ pool = Thread.current[:sidekiq_via_pool] || get_sidekiq_options["pool"] || Sidekiq.redis_pool
362
+ Sidekiq::Client.new(pool)
308
363
  end
309
364
  end
310
365
  end
data/lib/sidekiq.rb CHANGED
@@ -26,6 +26,7 @@ module Sidekiq
26
26
  timeout: 25,
27
27
  poll_interval_average: nil,
28
28
  average_scheduled_poll_interval: 5,
29
+ on_complex_arguments: :warn,
29
30
  error_handlers: [],
30
31
  death_handlers: [],
31
32
  lifecycle_events: {
@@ -39,11 +40,6 @@ module Sidekiq
39
40
  reloader: proc { |&block| block.call }
40
41
  }
41
42
 
42
- DEFAULT_WORKER_OPTIONS = {
43
- "retry" => true,
44
- "queue" => "default"
45
- }
46
-
47
43
  FAKE_INFO = {
48
44
  "redis_version" => "9.9.9",
49
45
  "uptime_in_days" => "9999",
@@ -102,6 +98,7 @@ module Sidekiq
102
98
  # to disconnect and reopen the socket to get back to the primary.
103
99
  # 4495 Use the same logic if we have a "Not enough replicas" error from the primary
104
100
  # 4985 Use the same logic when a blocking command is force-unblocked
101
+ # The same retry logic is also used in client.rb
105
102
  if retryable && ex.message =~ /READONLY|NOREPLICAS|UNBLOCKED/
106
103
  conn.disconnect!
107
104
  retryable = false
@@ -156,13 +153,20 @@ module Sidekiq
156
153
  Middleware::Chain.new
157
154
  end
158
155
 
159
- def self.default_worker_options=(hash)
160
- # stringify
161
- @default_worker_options = default_worker_options.merge(hash.transform_keys(&:to_s))
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"}
162
166
  end
163
167
 
164
- def self.default_worker_options
165
- defined?(@default_worker_options) ? @default_worker_options : DEFAULT_WORKER_OPTIONS
168
+ def self.default_job_options
169
+ @default_job_options ||= {"retry" => true, "queue" => "default"}
166
170
  end
167
171
 
168
172
  ##
@@ -200,12 +204,12 @@ module Sidekiq
200
204
  end
201
205
 
202
206
  def self.logger
203
- @logger ||= Sidekiq::Logger.new($stdout, level: Logger::INFO)
207
+ @logger ||= Sidekiq::Logger.new($stdout, level: :info)
204
208
  end
205
209
 
206
210
  def self.logger=(logger)
207
211
  if logger.nil?
208
- self.logger.level = Logger::FATAL
212
+ self.logger.fatal!
209
213
  return self.logger
210
214
  end
211
215
 
@@ -218,6 +222,10 @@ module Sidekiq
218
222
  defined?(Sidekiq::Pro)
219
223
  end
220
224
 
225
+ def self.ent?
226
+ defined?(Sidekiq::Enterprise)
227
+ end
228
+
221
229
  # How frequently Redis should be checked by a random Sidekiq process for
222
230
  # scheduled and retriable jobs. Each individual process will take turns by
223
231
  # waiting some multiple of this value.
@@ -252,12 +260,16 @@ module Sidekiq
252
260
  options[:lifecycle_events][event] << block
253
261
  end
254
262
 
255
- # We are shutting down Sidekiq but what about workers that
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
256
268
  # are working on some long job? This error is
257
- # raised in workers that have not finished within the hard
269
+ # raised in jobs that have not finished within the hard
258
270
  # timeout limit. This is needed to rollback db transactions,
259
271
  # otherwise Ruby's Thread#kill will commit. See #377.
260
- # DO NOT RESCUE THIS ERROR IN YOUR WORKERS
272
+ # DO NOT RESCUE THIS ERROR IN YOUR JOBS
261
273
  class Shutdown < Interrupt; end
262
274
  end
263
275
 
@@ -9,7 +9,9 @@ var ready = (callback) => {
9
9
  else document.addEventListener("DOMContentLoaded", callback);
10
10
  }
11
11
 
12
- ready(() => {
12
+ ready(addListeners)
13
+
14
+ function addListeners() {
13
15
  document.querySelectorAll(".check_all").forEach(node => {
14
16
  node.addEventListener("click", event => {
15
17
  node.closest('table').querySelectorAll('input[type=checkbox]').forEach(inp => { inp.checked = !!node.checked; });
@@ -26,42 +28,48 @@ ready(() => {
26
28
  })
27
29
 
28
30
  document.querySelectorAll("[data-toggle]").forEach(node => {
29
- node.addEventListener("click", event => {
30
- var targName = node.getAttribute("data-toggle");
31
- var full = document.getElementById(targName + "_full");
32
- if (full.style.display == "block") {
33
- full.style.display = 'none';
34
- } else {
35
- full.style.display = 'block';
36
- }
37
- })
31
+ node.addEventListener("click", addDataToggleListeners)
38
32
  })
39
33
 
40
34
  updateFuzzyTimes();
35
+ setLivePollFromUrl();
41
36
 
42
37
  var buttons = document.querySelectorAll(".live-poll");
43
38
  if (buttons.length > 0) {
44
39
  buttons.forEach(node => {
45
- node.addEventListener("click", event => {
46
- if (localStorage.sidekiqLivePoll == "enabled") {
47
- localStorage.sidekiqLivePoll = "disabled";
48
- clearTimeout(livePollTimer);
49
- livePollTimer = null;
50
- } else {
51
- localStorage.sidekiqLivePoll = "enabled";
52
- livePollCallback();
53
- }
54
-
55
- updateLivePollButton();
56
- })
40
+ node.addEventListener("click", addPollingListeners)
57
41
  });
58
42
 
59
43
  updateLivePollButton();
60
- if (localStorage.sidekiqLivePoll == "enabled") {
44
+ if (localStorage.sidekiqLivePoll == "enabled" && !livePollTimer) {
61
45
  scheduleLivePoll();
62
46
  }
63
47
  }
64
- })
48
+ }
49
+
50
+ function addPollingListeners(_event) {
51
+ if (localStorage.sidekiqLivePoll == "enabled") {
52
+ localStorage.sidekiqLivePoll = "disabled";
53
+ clearTimeout(livePollTimer);
54
+ livePollTimer = null;
55
+ } else {
56
+ localStorage.sidekiqLivePoll = "enabled";
57
+ livePollCallback();
58
+ }
59
+
60
+ updateLivePollButton();
61
+ }
62
+
63
+ function addDataToggleListeners(event) {
64
+ var source = event.target || event.srcElement;
65
+ var targName = source.getAttribute("data-toggle");
66
+ var full = document.getElementById(targName + "_full");
67
+ if (full.style.display == "block") {
68
+ full.style.display = 'none';
69
+ } else {
70
+ full.style.display = 'block';
71
+ }
72
+ }
65
73
 
66
74
  function updateFuzzyTimes() {
67
75
  var locale = document.body.getAttribute("data-locale");
@@ -76,6 +84,14 @@ function updateFuzzyTimes() {
76
84
  t.cancel();
77
85
  }
78
86
 
87
+ function setLivePollFromUrl() {
88
+ var url_params = new URL(window.location.href).searchParams
89
+
90
+ if (url_params.get("poll") == "true") {
91
+ localStorage.sidekiqLivePoll = "enabled";
92
+ }
93
+ }
94
+
79
95
  function updateLivePollButton() {
80
96
  if (localStorage.sidekiqLivePoll == "enabled") {
81
97
  document.querySelectorAll('.live-poll-stop').forEach(box => { box.style.display = "inline-block" })
@@ -89,7 +105,19 @@ function updateLivePollButton() {
89
105
  function livePollCallback() {
90
106
  clearTimeout(livePollTimer);
91
107
 
92
- fetch(window.location.href).then(resp => resp.text()).then(replacePage).finally(scheduleLivePoll)
108
+ fetch(window.location.href)
109
+ .then(checkResponse)
110
+ .then(resp => resp.text())
111
+ .then(replacePage)
112
+ .catch(showError)
113
+ .finally(scheduleLivePoll)
114
+ }
115
+
116
+ function checkResponse(resp) {
117
+ if (!resp.ok) {
118
+ throw response.error();
119
+ }
120
+ return resp
93
121
  }
94
122
 
95
123
  function scheduleLivePoll() {
@@ -107,5 +135,9 @@ function replacePage(text) {
107
135
  var header_status = doc.querySelector('.status')
108
136
  document.querySelector('.status').replaceWith(header_status)
109
137
 
110
- updateFuzzyTimes();
138
+ addListeners();
139
+ }
140
+
141
+ function showError(error) {
142
+ console.error(error)
111
143
  }