sidekiq 6.2.1 → 6.5.1

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 (79) hide show
  1. checksums.yaml +4 -4
  2. data/Changes.md +132 -1
  3. data/LICENSE +3 -3
  4. data/README.md +9 -4
  5. data/bin/sidekiq +3 -3
  6. data/bin/sidekiqload +70 -66
  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/.DS_Store +0 -0
  13. data/lib/sidekiq/api.rb +192 -135
  14. data/lib/sidekiq/cli.rb +59 -40
  15. data/lib/sidekiq/client.rb +46 -66
  16. data/lib/sidekiq/{util.rb → component.rb} +11 -42
  17. data/lib/sidekiq/delay.rb +3 -1
  18. data/lib/sidekiq/extensions/generic_proxy.rb +4 -2
  19. data/lib/sidekiq/fetch.rb +23 -20
  20. data/lib/sidekiq/job.rb +13 -0
  21. data/lib/sidekiq/job_logger.rb +16 -28
  22. data/lib/sidekiq/job_retry.rb +37 -38
  23. data/lib/sidekiq/job_util.rb +71 -0
  24. data/lib/sidekiq/launcher.rb +67 -65
  25. data/lib/sidekiq/logger.rb +8 -18
  26. data/lib/sidekiq/manager.rb +35 -34
  27. data/lib/sidekiq/middleware/chain.rb +27 -16
  28. data/lib/sidekiq/middleware/current_attributes.rb +61 -0
  29. data/lib/sidekiq/middleware/i18n.rb +6 -4
  30. data/lib/sidekiq/middleware/modules.rb +21 -0
  31. data/lib/sidekiq/monitor.rb +1 -1
  32. data/lib/sidekiq/paginator.rb +8 -8
  33. data/lib/sidekiq/processor.rb +38 -38
  34. data/lib/sidekiq/rails.rb +22 -4
  35. data/lib/sidekiq/redis_client_adapter.rb +154 -0
  36. data/lib/sidekiq/redis_connection.rb +85 -54
  37. data/lib/sidekiq/ring_buffer.rb +29 -0
  38. data/lib/sidekiq/scheduled.rb +60 -24
  39. data/lib/sidekiq/testing/inline.rb +4 -4
  40. data/lib/sidekiq/testing.rb +38 -39
  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 +1 -1
  44. data/lib/sidekiq/web/application.rb +9 -6
  45. data/lib/sidekiq/web/csrf_protection.rb +2 -2
  46. data/lib/sidekiq/web/helpers.rb +14 -26
  47. data/lib/sidekiq/web.rb +6 -5
  48. data/lib/sidekiq/worker.rb +136 -13
  49. data/lib/sidekiq.rb +105 -30
  50. data/sidekiq.gemspec +1 -1
  51. data/web/assets/javascripts/application.js +113 -60
  52. data/web/assets/javascripts/dashboard.js +51 -51
  53. data/web/assets/stylesheets/application-dark.css +28 -45
  54. data/web/assets/stylesheets/application-rtl.css +0 -4
  55. data/web/assets/stylesheets/application.css +24 -237
  56. data/web/locales/ar.yml +8 -2
  57. data/web/locales/en.yml +4 -1
  58. data/web/locales/es.yml +18 -2
  59. data/web/locales/fr.yml +7 -0
  60. data/web/locales/ja.yml +3 -0
  61. data/web/locales/lt.yml +1 -1
  62. data/web/locales/pt-br.yml +27 -9
  63. data/web/views/_footer.erb +1 -1
  64. data/web/views/_job_info.erb +1 -1
  65. data/web/views/_poll_link.erb +2 -5
  66. data/web/views/_summary.erb +7 -7
  67. data/web/views/busy.erb +8 -8
  68. data/web/views/dashboard.erb +22 -14
  69. data/web/views/dead.erb +1 -1
  70. data/web/views/layout.erb +1 -1
  71. data/web/views/morgue.erb +6 -6
  72. data/web/views/queue.erb +10 -10
  73. data/web/views/queues.erb +3 -3
  74. data/web/views/retries.erb +7 -7
  75. data/web/views/retry.erb +1 -1
  76. data/web/views/scheduled.erb +1 -1
  77. metadata +17 -10
  78. data/lib/generators/sidekiq/worker_generator.rb +0 -57
  79. data/lib/sidekiq/exception_handler.rb +0 -27
data/lib/sidekiq.rb CHANGED
@@ -5,7 +5,9 @@ fail "Sidekiq #{Sidekiq::VERSION} does not support Ruby versions below 2.5.0." i
5
5
 
6
6
  require "sidekiq/logger"
7
7
  require "sidekiq/client"
8
+ require "sidekiq/transaction_aware_client"
8
9
  require "sidekiq/worker"
10
+ require "sidekiq/job"
9
11
  require "sidekiq/redis_connection"
10
12
  require "sidekiq/delay"
11
13
 
@@ -25,6 +27,7 @@ module Sidekiq
25
27
  timeout: 25,
26
28
  poll_interval_average: nil,
27
29
  average_scheduled_poll_interval: 5,
30
+ on_complex_arguments: :warn,
28
31
  error_handlers: [],
29
32
  death_handlers: [],
30
33
  lifecycle_events: {
@@ -38,11 +41,6 @@ module Sidekiq
38
41
  reloader: proc { |&block| block.call }
39
42
  }
40
43
 
41
- DEFAULT_WORKER_OPTIONS = {
42
- "retry" => true,
43
- "queue" => "default"
44
- }
45
-
46
44
  FAKE_INFO = {
47
45
  "redis_version" => "9.9.9",
48
46
  "uptime_in_days" => "9999",
@@ -55,19 +53,79 @@ module Sidekiq
55
53
  puts "Calm down, yo."
56
54
  end
57
55
 
56
+ # config.concurrency = 5
57
+ def self.concurrency=(val)
58
+ self[:concurrency] = Integer(val)
59
+ end
60
+
61
+ # config.queues = %w( high default low ) # strict
62
+ # config.queues = %w( high,3 default,2 low,1 ) # weighted
63
+ # config.queues = %w( feature1,1 feature2,1 feature3,1 ) # random
64
+ #
65
+ # With weighted priority, queue will be checked first (weight / total) of the time.
66
+ # high will be checked first (3/6) or 50% of the time.
67
+ # I'd recommend setting weights between 1-10. Weights in the hundreds or thousands
68
+ # are ridiculous and unnecessarily expensive. You can get random queue ordering
69
+ # by explicitly setting all weights to 1.
70
+ def self.queues=(val)
71
+ self[:queues] = Array(val).each_with_object([]) do |qstr, memo|
72
+ name, weight = qstr.split(",")
73
+ self[:strict] = false if weight.to_i > 0
74
+ [weight.to_i, 1].max.times do
75
+ memo << name
76
+ end
77
+ end
78
+ end
79
+
80
+ ### Private APIs
81
+ def self.default_error_handler(ex, ctx)
82
+ logger.warn(dump_json(ctx)) unless ctx.empty?
83
+ logger.warn("#{ex.class.name}: #{ex.message}")
84
+ logger.warn(ex.backtrace.join("\n")) unless ex.backtrace.nil?
85
+ end
86
+
87
+ @config = DEFAULTS.dup
58
88
  def self.options
59
- @options ||= DEFAULTS.dup
89
+ logger.warn "`config.options[:key] = value` is deprecated, use `config[:key] = value`: #{caller(1..2)}"
90
+ @config
60
91
  end
61
92
 
62
93
  def self.options=(opts)
63
- @options = opts
94
+ logger.warn "config.options = hash` is deprecated, use `config.merge!(hash)`: #{caller(1..2)}"
95
+ @config = opts
96
+ end
97
+
98
+ def self.[](key)
99
+ @config[key]
100
+ end
101
+
102
+ def self.[]=(key, val)
103
+ @config[key] = val
64
104
  end
65
105
 
106
+ def self.merge!(hash)
107
+ @config.merge!(hash)
108
+ end
109
+
110
+ def self.fetch(*args, &block)
111
+ @config.fetch(*args, &block)
112
+ end
113
+
114
+ def self.handle_exception(ex, ctx = {})
115
+ self[:error_handlers].each do |handler|
116
+ handler.call(ex, ctx)
117
+ rescue => ex
118
+ logger.error "!!! ERROR HANDLER THREW AN ERROR !!!"
119
+ logger.error ex
120
+ logger.error ex.backtrace.join("\n") unless ex.backtrace.nil?
121
+ end
122
+ end
123
+ ###
124
+
66
125
  ##
67
126
  # Configuration for Sidekiq server, use like:
68
127
  #
69
128
  # Sidekiq.configure_server do |config|
70
- # config.redis = { :namespace => 'myapp', :size => 25, :url => 'redis://myhost:8877/0' }
71
129
  # config.server_middleware do |chain|
72
130
  # chain.add MyServerHook
73
131
  # end
@@ -80,7 +138,7 @@ module Sidekiq
80
138
  # Configuration for Sidekiq client, use like:
81
139
  #
82
140
  # Sidekiq.configure_client do |config|
83
- # config.redis = { :namespace => 'myapp', :size => 1, :url => 'redis://myhost:8877/0' }
141
+ # config.redis = { size: 1, url: 'redis://myhost:8877/0' }
84
142
  # end
85
143
  def self.configure_client
86
144
  yield self unless server?
@@ -96,11 +154,13 @@ module Sidekiq
96
154
  retryable = true
97
155
  begin
98
156
  yield conn
99
- rescue Redis::BaseError => ex
157
+ rescue RedisConnection.adapter::BaseError => ex
100
158
  # 2550 Failover can cause the server to become a replica, need
101
159
  # to disconnect and reopen the socket to get back to the primary.
102
160
  # 4495 Use the same logic if we have a "Not enough replicas" error from the primary
103
- if retryable && ex.message =~ /READONLY|NOREPLICAS/
161
+ # 4985 Use the same logic when a blocking command is force-unblocked
162
+ # The same retry logic is also used in client.rb
163
+ if retryable && ex.message =~ /READONLY|NOREPLICAS|UNBLOCKED/
104
164
  conn.disconnect!
105
165
  retryable = false
106
166
  retry
@@ -119,7 +179,7 @@ module Sidekiq
119
179
  else
120
180
  conn.info
121
181
  end
122
- rescue Redis::CommandError => ex
182
+ rescue RedisConnection.adapter::CommandError => ex
123
183
  # 2850 return fake version when INFO command has (probably) been renamed
124
184
  raise unless /unknown command/.match?(ex.message)
125
185
  FAKE_INFO
@@ -127,19 +187,19 @@ module Sidekiq
127
187
  end
128
188
 
129
189
  def self.redis_pool
130
- @redis ||= Sidekiq::RedisConnection.create
190
+ @redis ||= RedisConnection.create
131
191
  end
132
192
 
133
193
  def self.redis=(hash)
134
194
  @redis = if hash.is_a?(ConnectionPool)
135
195
  hash
136
196
  else
137
- Sidekiq::RedisConnection.create(hash)
197
+ RedisConnection.create(hash)
138
198
  end
139
199
  end
140
200
 
141
201
  def self.client_middleware
142
- @client_chain ||= Middleware::Chain.new
202
+ @client_chain ||= Middleware::Chain.new(self)
143
203
  yield @client_chain if block_given?
144
204
  @client_chain
145
205
  end
@@ -151,16 +211,23 @@ module Sidekiq
151
211
  end
152
212
 
153
213
  def self.default_server_middleware
154
- Middleware::Chain.new
214
+ Middleware::Chain.new(self)
155
215
  end
156
216
 
157
- def self.default_worker_options=(hash)
158
- # stringify
159
- @default_worker_options = default_worker_options.merge(hash.transform_keys(&:to_s))
217
+ def self.default_worker_options=(hash) # deprecated
218
+ @default_job_options = default_job_options.merge(hash.transform_keys(&:to_s))
160
219
  end
161
220
 
162
- def self.default_worker_options
163
- defined?(@default_worker_options) ? @default_worker_options : DEFAULT_WORKER_OPTIONS
221
+ def self.default_job_options=(hash)
222
+ @default_job_options = default_job_options.merge(hash.transform_keys(&:to_s))
223
+ end
224
+
225
+ def self.default_worker_options # deprecated
226
+ @default_job_options ||= {"retry" => true, "queue" => "default"}
227
+ end
228
+
229
+ def self.default_job_options
230
+ @default_job_options ||= {"retry" => true, "queue" => "default"}
164
231
  end
165
232
 
166
233
  ##
@@ -173,7 +240,7 @@ module Sidekiq
173
240
  # end
174
241
  # end
175
242
  def self.death_handlers
176
- options[:death_handlers]
243
+ self[:death_handlers]
177
244
  end
178
245
 
179
246
  def self.load_json(string)
@@ -198,7 +265,7 @@ module Sidekiq
198
265
  end
199
266
 
200
267
  def self.logger
201
- @logger ||= Sidekiq::Logger.new($stdout, level: Logger::INFO)
268
+ @logger ||= Sidekiq::Logger.new($stdout, level: :info)
202
269
  end
203
270
 
204
271
  def self.logger=(logger)
@@ -216,13 +283,17 @@ module Sidekiq
216
283
  defined?(Sidekiq::Pro)
217
284
  end
218
285
 
286
+ def self.ent?
287
+ defined?(Sidekiq::Enterprise)
288
+ end
289
+
219
290
  # How frequently Redis should be checked by a random Sidekiq process for
220
291
  # scheduled and retriable jobs. Each individual process will take turns by
221
292
  # waiting some multiple of this value.
222
293
  #
223
294
  # See sidekiq/scheduled.rb for an in-depth explanation of this value
224
295
  def self.average_scheduled_poll_interval=(interval)
225
- options[:average_scheduled_poll_interval] = interval
296
+ self[:average_scheduled_poll_interval] = interval
226
297
  end
227
298
 
228
299
  # Register a proc to handle any error which occurs within the Sidekiq process.
@@ -233,7 +304,7 @@ module Sidekiq
233
304
  #
234
305
  # The default error handler logs errors to Sidekiq.logger.
235
306
  def self.error_handlers
236
- options[:error_handlers]
307
+ self[:error_handlers]
237
308
  end
238
309
 
239
310
  # Register a block to run at a point in the Sidekiq lifecycle.
@@ -246,16 +317,20 @@ module Sidekiq
246
317
  # end
247
318
  def self.on(event, &block)
248
319
  raise ArgumentError, "Symbols only please: #{event}" unless event.is_a?(Symbol)
249
- raise ArgumentError, "Invalid event name: #{event}" unless options[:lifecycle_events].key?(event)
250
- options[:lifecycle_events][event] << block
320
+ raise ArgumentError, "Invalid event name: #{event}" unless self[:lifecycle_events].key?(event)
321
+ self[:lifecycle_events][event] << block
322
+ end
323
+
324
+ def self.strict_args!(mode = :raise)
325
+ self[:on_complex_arguments] = mode
251
326
  end
252
327
 
253
- # We are shutting down Sidekiq but what about workers that
328
+ # We are shutting down Sidekiq but what about threads that
254
329
  # are working on some long job? This error is
255
- # raised in workers that have not finished within the hard
330
+ # raised in jobs that have not finished within the hard
256
331
  # timeout limit. This is needed to rollback db transactions,
257
332
  # otherwise Ruby's Thread#kill will commit. See #377.
258
- # DO NOT RESCUE THIS ERROR IN YOUR WORKERS
333
+ # DO NOT RESCUE THIS ERROR IN YOUR JOBS
259
334
  class Shutdown < Interrupt; end
260
335
  end
261
336
 
data/sidekiq.gemspec CHANGED
@@ -18,7 +18,7 @@ Gem::Specification.new do |gem|
18
18
  "homepage_uri" => "https://sidekiq.org",
19
19
  "bug_tracker_uri" => "https://github.com/mperham/sidekiq/issues",
20
20
  "documentation_uri" => "https://github.com/mperham/sidekiq/wiki",
21
- "changelog_uri" => "https://github.com/mperham/sidekiq/blob/master/Changes.md",
21
+ "changelog_uri" => "https://github.com/mperham/sidekiq/blob/main/Changes.md",
22
22
  "source_code_uri" => "https://github.com/mperham/sidekiq"
23
23
  }
24
24