sidekiq 6.4.0 → 7.0.8

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 (113) hide show
  1. checksums.yaml +4 -4
  2. data/Changes.md +187 -12
  3. data/README.md +44 -31
  4. data/bin/sidekiq +4 -9
  5. data/bin/sidekiqload +189 -117
  6. data/bin/sidekiqmon +4 -1
  7. data/lib/sidekiq/api.rb +304 -186
  8. data/lib/sidekiq/capsule.rb +127 -0
  9. data/lib/sidekiq/cli.rb +83 -80
  10. data/lib/sidekiq/client.rb +71 -44
  11. data/lib/sidekiq/component.rb +68 -0
  12. data/lib/sidekiq/config.rb +270 -0
  13. data/lib/sidekiq/deploy.rb +62 -0
  14. data/lib/sidekiq/embedded.rb +61 -0
  15. data/lib/sidekiq/fetch.rb +23 -24
  16. data/lib/sidekiq/job.rb +375 -10
  17. data/lib/sidekiq/job_logger.rb +16 -28
  18. data/lib/sidekiq/job_retry.rb +76 -54
  19. data/lib/sidekiq/job_util.rb +59 -19
  20. data/lib/sidekiq/launcher.rb +103 -95
  21. data/lib/sidekiq/logger.rb +9 -44
  22. data/lib/sidekiq/manager.rb +33 -32
  23. data/lib/sidekiq/metrics/query.rb +153 -0
  24. data/lib/sidekiq/metrics/shared.rb +95 -0
  25. data/lib/sidekiq/metrics/tracking.rb +136 -0
  26. data/lib/sidekiq/middleware/chain.rb +96 -51
  27. data/lib/sidekiq/middleware/current_attributes.rb +16 -17
  28. data/lib/sidekiq/middleware/i18n.rb +6 -4
  29. data/lib/sidekiq/middleware/modules.rb +21 -0
  30. data/lib/sidekiq/monitor.rb +17 -4
  31. data/lib/sidekiq/paginator.rb +17 -9
  32. data/lib/sidekiq/processor.rb +60 -60
  33. data/lib/sidekiq/rails.rb +12 -10
  34. data/lib/sidekiq/redis_client_adapter.rb +115 -0
  35. data/lib/sidekiq/redis_connection.rb +13 -82
  36. data/lib/sidekiq/ring_buffer.rb +29 -0
  37. data/lib/sidekiq/scheduled.rb +65 -37
  38. data/lib/sidekiq/testing/inline.rb +4 -4
  39. data/lib/sidekiq/testing.rb +41 -68
  40. data/lib/sidekiq/transaction_aware_client.rb +44 -0
  41. data/lib/sidekiq/version.rb +2 -1
  42. data/lib/sidekiq/web/action.rb +3 -3
  43. data/lib/sidekiq/web/application.rb +40 -9
  44. data/lib/sidekiq/web/csrf_protection.rb +3 -3
  45. data/lib/sidekiq/web/helpers.rb +35 -21
  46. data/lib/sidekiq/web.rb +10 -17
  47. data/lib/sidekiq/worker_compatibility_alias.rb +13 -0
  48. data/lib/sidekiq.rb +84 -206
  49. data/sidekiq.gemspec +21 -10
  50. data/web/assets/javascripts/application.js +76 -26
  51. data/web/assets/javascripts/base-charts.js +106 -0
  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-charts.js +166 -0
  55. data/web/assets/javascripts/dashboard.js +3 -240
  56. data/web/assets/javascripts/metrics.js +264 -0
  57. data/web/assets/stylesheets/application-dark.css +4 -0
  58. data/web/assets/stylesheets/application-rtl.css +2 -91
  59. data/web/assets/stylesheets/application.css +66 -297
  60. data/web/locales/ar.yml +70 -70
  61. data/web/locales/cs.yml +62 -62
  62. data/web/locales/da.yml +60 -53
  63. data/web/locales/de.yml +65 -65
  64. data/web/locales/el.yml +43 -24
  65. data/web/locales/en.yml +82 -69
  66. data/web/locales/es.yml +68 -68
  67. data/web/locales/fa.yml +65 -65
  68. data/web/locales/fr.yml +67 -67
  69. data/web/locales/he.yml +65 -64
  70. data/web/locales/hi.yml +59 -59
  71. data/web/locales/it.yml +53 -53
  72. data/web/locales/ja.yml +73 -68
  73. data/web/locales/ko.yml +52 -52
  74. data/web/locales/lt.yml +66 -66
  75. data/web/locales/nb.yml +61 -61
  76. data/web/locales/nl.yml +52 -52
  77. data/web/locales/pl.yml +45 -45
  78. data/web/locales/pt-br.yml +63 -55
  79. data/web/locales/pt.yml +51 -51
  80. data/web/locales/ru.yml +67 -66
  81. data/web/locales/sv.yml +53 -53
  82. data/web/locales/ta.yml +60 -60
  83. data/web/locales/uk.yml +62 -61
  84. data/web/locales/ur.yml +64 -64
  85. data/web/locales/vi.yml +67 -67
  86. data/web/locales/zh-cn.yml +43 -16
  87. data/web/locales/zh-tw.yml +42 -8
  88. data/web/views/_footer.erb +5 -2
  89. data/web/views/_job_info.erb +18 -2
  90. data/web/views/_metrics_period_select.erb +12 -0
  91. data/web/views/_nav.erb +1 -1
  92. data/web/views/_paging.erb +2 -0
  93. data/web/views/_poll_link.erb +1 -1
  94. data/web/views/_summary.erb +1 -1
  95. data/web/views/busy.erb +42 -26
  96. data/web/views/dashboard.erb +36 -4
  97. data/web/views/metrics.erb +82 -0
  98. data/web/views/metrics_for_job.erb +71 -0
  99. data/web/views/morgue.erb +5 -9
  100. data/web/views/queue.erb +15 -15
  101. data/web/views/queues.erb +3 -1
  102. data/web/views/retries.erb +5 -9
  103. data/web/views/scheduled.erb +12 -13
  104. metadata +64 -28
  105. data/lib/sidekiq/delay.rb +0 -43
  106. data/lib/sidekiq/exception_handler.rb +0 -27
  107. data/lib/sidekiq/extensions/action_mailer.rb +0 -48
  108. data/lib/sidekiq/extensions/active_record.rb +0 -43
  109. data/lib/sidekiq/extensions/class_methods.rb +0 -43
  110. data/lib/sidekiq/extensions/generic_proxy.rb +0 -33
  111. data/lib/sidekiq/util.rb +0 -108
  112. data/lib/sidekiq/worker.rb +0 -364
  113. /data/{LICENSE → LICENSE.txt} +0 -0
@@ -1,364 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "sidekiq/client"
4
-
5
- module Sidekiq
6
- ##
7
- # Include this module in your worker class and you can easily create
8
- # asynchronous jobs:
9
- #
10
- # class HardWorker
11
- # include Sidekiq::Worker
12
- # sidekiq_options queue: 'critical', retry: 5
13
- #
14
- # def perform(*args)
15
- # # do some work
16
- # end
17
- # end
18
- #
19
- # Then in your Rails app, you can do this:
20
- #
21
- # HardWorker.perform_async(1, 2, 3)
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
- #
44
- module Worker
45
- ##
46
- # The Options module is extracted so we can include it in ActiveJob::Base
47
- # and allow native AJs to configure Sidekiq features/internals.
48
- module Options
49
- def self.included(base)
50
- base.extend(ClassMethods)
51
- base.sidekiq_class_attribute :sidekiq_options_hash
52
- base.sidekiq_class_attribute :sidekiq_retry_in_block
53
- base.sidekiq_class_attribute :sidekiq_retries_exhausted_block
54
- end
55
-
56
- module ClassMethods
57
- ACCESSOR_MUTEX = Mutex.new
58
-
59
- ##
60
- # Allows customization for this type of Worker.
61
- # Legal options:
62
- #
63
- # queue - name of queue to use for this job type, default *default*
64
- # retry - enable retries for this Worker in case of error during execution,
65
- # *true* to use the default or *Integer* count
66
- # backtrace - whether to save any error backtrace in the retry payload to display in web UI,
67
- # can be true, false or an integer number of lines to save, default *false*
68
- #
69
- # In practice, any option is allowed. This is the main mechanism to configure the
70
- # options for a specific job.
71
- def sidekiq_options(opts = {})
72
- opts = opts.transform_keys(&:to_s) # stringify
73
- self.sidekiq_options_hash = get_sidekiq_options.merge(opts)
74
- end
75
-
76
- def sidekiq_retry_in(&block)
77
- self.sidekiq_retry_in_block = block
78
- end
79
-
80
- def sidekiq_retries_exhausted(&block)
81
- self.sidekiq_retries_exhausted_block = block
82
- end
83
-
84
- def get_sidekiq_options # :nodoc:
85
- self.sidekiq_options_hash ||= Sidekiq.default_worker_options
86
- end
87
-
88
- def sidekiq_class_attribute(*attrs)
89
- instance_reader = true
90
- instance_writer = true
91
-
92
- attrs.each do |name|
93
- synchronized_getter = "__synchronized_#{name}"
94
-
95
- singleton_class.instance_eval do
96
- undef_method(name) if method_defined?(name) || private_method_defined?(name)
97
- end
98
-
99
- define_singleton_method(synchronized_getter) { nil }
100
- singleton_class.class_eval do
101
- private(synchronized_getter)
102
- end
103
-
104
- define_singleton_method(name) { ACCESSOR_MUTEX.synchronize { send synchronized_getter } }
105
-
106
- ivar = "@#{name}"
107
-
108
- singleton_class.instance_eval do
109
- m = "#{name}="
110
- undef_method(m) if method_defined?(m) || private_method_defined?(m)
111
- end
112
- define_singleton_method("#{name}=") do |val|
113
- singleton_class.class_eval do
114
- ACCESSOR_MUTEX.synchronize do
115
- undef_method(synchronized_getter) if method_defined?(synchronized_getter) || private_method_defined?(synchronized_getter)
116
- define_method(synchronized_getter) { val }
117
- end
118
- end
119
-
120
- if singleton_class?
121
- class_eval do
122
- undef_method(name) if method_defined?(name) || private_method_defined?(name)
123
- define_method(name) do
124
- if instance_variable_defined? ivar
125
- instance_variable_get ivar
126
- else
127
- singleton_class.send name
128
- end
129
- end
130
- end
131
- end
132
- val
133
- end
134
-
135
- if instance_reader
136
- undef_method(name) if method_defined?(name) || private_method_defined?(name)
137
- define_method(name) do
138
- if instance_variable_defined?(ivar)
139
- instance_variable_get ivar
140
- else
141
- self.class.public_send name
142
- end
143
- end
144
- end
145
-
146
- if instance_writer
147
- m = "#{name}="
148
- undef_method(m) if method_defined?(m) || private_method_defined?(m)
149
- attr_writer name
150
- end
151
- end
152
- end
153
- end
154
- end
155
-
156
- attr_accessor :jid
157
-
158
- def self.included(base)
159
- raise ArgumentError, "Sidekiq::Worker cannot be included in an ActiveJob: #{base.name}" if base.ancestors.any? { |c| c.name == "ActiveJob::Base" }
160
-
161
- base.include(Options)
162
- base.extend(ClassMethods)
163
- end
164
-
165
- def logger
166
- Sidekiq.logger
167
- end
168
-
169
- # This helper class encapsulates the set options for `set`, e.g.
170
- #
171
- # SomeWorker.set(queue: 'foo').perform_async(....)
172
- #
173
- class Setter
174
- include Sidekiq::JobUtil
175
-
176
- def initialize(klass, opts)
177
- @klass = klass
178
- @opts = opts
179
-
180
- # ActiveJob compatibility
181
- interval = @opts.delete(:wait_until) || @opts.delete(:wait)
182
- at(interval) if interval
183
- end
184
-
185
- def set(options)
186
- interval = options.delete(:wait_until) || options.delete(:wait)
187
- @opts.merge!(options)
188
- at(interval) if interval
189
- self
190
- end
191
-
192
- def perform_async(*args)
193
- if @opts["sync"] == true
194
- perform_inline(*args)
195
- else
196
- @klass.client_push(@opts.merge("args" => args, "class" => @klass))
197
- end
198
- end
199
-
200
- # Explicit inline execution of a job. Returns nil if the job did not
201
- # execute, true otherwise.
202
- def perform_inline(*args)
203
- raw = @opts.merge("args" => args, "class" => @klass).transform_keys(&:to_s)
204
-
205
- # validate and normalize payload
206
- item = normalize_item(raw)
207
- queue = item["queue"]
208
-
209
- # run client-side middleware
210
- result = Sidekiq.client_middleware.invoke(item["class"], item, queue, Sidekiq.redis_pool) do
211
- item
212
- end
213
- return nil unless result
214
-
215
- # round-trip the payload via JSON
216
- msg = Sidekiq.load_json(Sidekiq.dump_json(item))
217
-
218
- # prepare the job instance
219
- klass = msg["class"].constantize
220
- job = klass.new
221
- job.jid = msg["jid"]
222
- job.bid = msg["bid"] if job.respond_to?(:bid)
223
-
224
- # run the job through server-side middleware
225
- result = Sidekiq.server_middleware.invoke(job, msg, msg["queue"]) do
226
- # perform it
227
- job.perform(*msg["args"])
228
- true
229
- end
230
- return nil unless result
231
- # jobs do not return a result. they should store any
232
- # modified state.
233
- true
234
- end
235
- alias_method :perform_sync, :perform_inline
236
-
237
- def perform_bulk(args, batch_size: 1_000)
238
- hash = @opts.transform_keys(&:to_s)
239
- result = args.each_slice(batch_size).flat_map do |slice|
240
- Sidekiq::Client.push_bulk(hash.merge("class" => @klass, "args" => slice))
241
- end
242
-
243
- result.is_a?(Enumerator::Lazy) ? result.force : result
244
- end
245
-
246
- # +interval+ must be a timestamp, numeric or something that acts
247
- # numeric (like an activesupport time interval).
248
- def perform_in(interval, *args)
249
- at(interval).perform_async(*args)
250
- end
251
- alias_method :perform_at, :perform_in
252
-
253
- private
254
-
255
- def at(interval)
256
- int = interval.to_f
257
- now = Time.now.to_f
258
- ts = (int < 1_000_000_000 ? now + int : int)
259
- # Optimization to enqueue something now that is scheduled to go out now or in the past
260
- @opts["at"] = ts if ts > now
261
- self
262
- end
263
- end
264
-
265
- module ClassMethods
266
- def delay(*args)
267
- raise ArgumentError, "Do not call .delay on a Sidekiq::Worker class, call .perform_async"
268
- end
269
-
270
- def delay_for(*args)
271
- raise ArgumentError, "Do not call .delay_for on a Sidekiq::Worker class, call .perform_in"
272
- end
273
-
274
- def delay_until(*args)
275
- raise ArgumentError, "Do not call .delay_until on a Sidekiq::Worker class, call .perform_at"
276
- end
277
-
278
- def queue_as(q)
279
- sidekiq_options("queue" => q.to_s)
280
- end
281
-
282
- def set(options)
283
- Setter.new(self, options)
284
- end
285
-
286
- def perform_async(*args)
287
- Setter.new(self, {}).perform_async(*args)
288
- end
289
-
290
- # Inline execution of job's perform method after passing through Sidekiq.client_middleware and Sidekiq.server_middleware
291
- def perform_inline(*args)
292
- Setter.new(self, {}).perform_inline(*args)
293
- end
294
-
295
- ##
296
- # Push a large number of jobs to Redis, while limiting the batch of
297
- # each job payload to 1,000. This method helps cut down on the number
298
- # of round trips to Redis, which can increase the performance of enqueueing
299
- # large numbers of jobs.
300
- #
301
- # +items+ must be an Array of Arrays.
302
- #
303
- # For finer-grained control, use `Sidekiq::Client.push_bulk` directly.
304
- #
305
- # Example (3 Redis round trips):
306
- #
307
- # SomeWorker.perform_async(1)
308
- # SomeWorker.perform_async(2)
309
- # SomeWorker.perform_async(3)
310
- #
311
- # Would instead become (1 Redis round trip):
312
- #
313
- # SomeWorker.perform_bulk([[1], [2], [3]])
314
- #
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
321
- end
322
-
323
- # +interval+ must be a timestamp, numeric or something that acts
324
- # numeric (like an activesupport time interval).
325
- def perform_in(interval, *args)
326
- int = interval.to_f
327
- now = Time.now.to_f
328
- ts = (int < 1_000_000_000 ? now + int : int)
329
-
330
- item = {"class" => self, "args" => args}
331
-
332
- # Optimization to enqueue something now that is scheduled to go out now or in the past
333
- item["at"] = ts if ts > now
334
-
335
- client_push(item)
336
- end
337
- alias_method :perform_at, :perform_in
338
-
339
- ##
340
- # Allows customization for this type of Worker.
341
- # Legal options:
342
- #
343
- # queue - use a named queue for this Worker, default 'default'
344
- # retry - enable the RetryJobs middleware for this Worker, *true* to use the default
345
- # or *Integer* count
346
- # backtrace - whether to save any error backtrace in the retry payload to display in web UI,
347
- # can be true, false or an integer number of lines to save, default *false*
348
- # pool - use the given Redis connection pool to push this type of job to a given shard.
349
- #
350
- # In practice, any option is allowed. This is the main mechanism to configure the
351
- # options for a specific job.
352
- def sidekiq_options(opts = {})
353
- super
354
- end
355
-
356
- 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
-
360
- Sidekiq::Client.new(pool).push(stringified_item)
361
- end
362
- end
363
- end
364
- end
File without changes