sidekiq 6.5.12 → 7.0.6

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 (103) hide show
  1. checksums.yaml +4 -4
  2. data/Changes.md +63 -22
  3. data/README.md +40 -32
  4. data/bin/sidekiq +3 -8
  5. data/bin/sidekiqload +186 -118
  6. data/bin/sidekiqmon +3 -0
  7. data/lib/sidekiq/api.rb +84 -121
  8. data/lib/sidekiq/capsule.rb +127 -0
  9. data/lib/sidekiq/cli.rb +55 -74
  10. data/lib/sidekiq/client.rb +29 -16
  11. data/lib/sidekiq/component.rb +3 -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 +11 -14
  16. data/lib/sidekiq/job.rb +375 -10
  17. data/lib/sidekiq/job_logger.rb +2 -2
  18. data/lib/sidekiq/job_retry.rb +9 -9
  19. data/lib/sidekiq/job_util.rb +48 -14
  20. data/lib/sidekiq/launcher.rb +64 -61
  21. data/lib/sidekiq/logger.rb +1 -26
  22. data/lib/sidekiq/manager.rb +9 -11
  23. data/lib/sidekiq/metrics/query.rb +2 -2
  24. data/lib/sidekiq/metrics/shared.rb +4 -3
  25. data/lib/sidekiq/metrics/tracking.rb +20 -18
  26. data/lib/sidekiq/middleware/chain.rb +19 -18
  27. data/lib/sidekiq/middleware/current_attributes.rb +8 -15
  28. data/lib/sidekiq/monitor.rb +16 -3
  29. data/lib/sidekiq/processor.rb +21 -27
  30. data/lib/sidekiq/rails.rb +13 -17
  31. data/lib/sidekiq/redis_client_adapter.rb +8 -47
  32. data/lib/sidekiq/redis_connection.rb +11 -111
  33. data/lib/sidekiq/scheduled.rb +20 -21
  34. data/lib/sidekiq/testing.rb +5 -33
  35. data/lib/sidekiq/transaction_aware_client.rb +4 -5
  36. data/lib/sidekiq/version.rb +2 -1
  37. data/lib/sidekiq/web/application.rb +21 -6
  38. data/lib/sidekiq/web/csrf_protection.rb +1 -1
  39. data/lib/sidekiq/web/helpers.rb +16 -15
  40. data/lib/sidekiq/web.rb +6 -17
  41. data/lib/sidekiq/worker_compatibility_alias.rb +13 -0
  42. data/lib/sidekiq.rb +76 -274
  43. data/sidekiq.gemspec +20 -10
  44. data/web/assets/javascripts/application.js +18 -1
  45. data/web/assets/javascripts/base-charts.js +106 -0
  46. data/web/assets/javascripts/dashboard-charts.js +166 -0
  47. data/web/assets/javascripts/dashboard.js +3 -223
  48. data/web/assets/javascripts/metrics.js +117 -115
  49. data/web/assets/stylesheets/application-dark.css +4 -0
  50. data/web/assets/stylesheets/application-rtl.css +2 -91
  51. data/web/assets/stylesheets/application.css +23 -298
  52. data/web/locales/ar.yml +70 -70
  53. data/web/locales/cs.yml +62 -62
  54. data/web/locales/da.yml +60 -53
  55. data/web/locales/de.yml +65 -65
  56. data/web/locales/el.yml +2 -7
  57. data/web/locales/en.yml +76 -70
  58. data/web/locales/es.yml +68 -68
  59. data/web/locales/fa.yml +65 -65
  60. data/web/locales/fr.yml +67 -67
  61. data/web/locales/he.yml +65 -64
  62. data/web/locales/hi.yml +59 -59
  63. data/web/locales/it.yml +53 -53
  64. data/web/locales/ja.yml +64 -68
  65. data/web/locales/ko.yml +52 -52
  66. data/web/locales/lt.yml +66 -66
  67. data/web/locales/nb.yml +61 -61
  68. data/web/locales/nl.yml +52 -52
  69. data/web/locales/pl.yml +45 -45
  70. data/web/locales/pt-br.yml +59 -69
  71. data/web/locales/pt.yml +51 -51
  72. data/web/locales/ru.yml +67 -66
  73. data/web/locales/sv.yml +53 -53
  74. data/web/locales/ta.yml +60 -60
  75. data/web/locales/uk.yml +62 -61
  76. data/web/locales/ur.yml +64 -64
  77. data/web/locales/vi.yml +67 -67
  78. data/web/locales/zh-cn.yml +20 -18
  79. data/web/locales/zh-tw.yml +10 -1
  80. data/web/views/_footer.erb +5 -2
  81. data/web/views/_job_info.erb +18 -2
  82. data/web/views/_metrics_period_select.erb +12 -0
  83. data/web/views/_paging.erb +2 -0
  84. data/web/views/_poll_link.erb +1 -1
  85. data/web/views/busy.erb +37 -26
  86. data/web/views/dashboard.erb +36 -5
  87. data/web/views/metrics.erb +33 -20
  88. data/web/views/metrics_for_job.erb +22 -38
  89. data/web/views/morgue.erb +5 -9
  90. data/web/views/queue.erb +10 -14
  91. data/web/views/queues.erb +3 -1
  92. data/web/views/retries.erb +5 -9
  93. data/web/views/scheduled.erb +12 -13
  94. metadata +50 -40
  95. data/lib/sidekiq/delay.rb +0 -43
  96. data/lib/sidekiq/extensions/action_mailer.rb +0 -48
  97. data/lib/sidekiq/extensions/active_record.rb +0 -43
  98. data/lib/sidekiq/extensions/class_methods.rb +0 -43
  99. data/lib/sidekiq/extensions/generic_proxy.rb +0 -33
  100. data/lib/sidekiq/metrics/deploy.rb +0 -47
  101. data/lib/sidekiq/worker.rb +0 -370
  102. data/web/assets/javascripts/graph.js +0 -16
  103. /data/{LICENSE → LICENSE.txt} +0 -0
@@ -1,47 +0,0 @@
1
- require "sidekiq"
2
- require "time"
3
-
4
- # This file is designed to be required within the user's
5
- # deployment script; it should need a bare minimum of dependencies.
6
- #
7
- # require "sidekiq/metrics/deploy"
8
- # gitdesc = `git log -1 --format="%h %s"`.strip
9
- # d = Sidekiq::Metrics::Deploy.new
10
- # d.mark(label: gitdesc)
11
- #
12
- # Note that you cannot mark more than once per minute. This is a feature, not a bug.
13
- module Sidekiq
14
- module Metrics
15
- class Deploy
16
- MARK_TTL = 90 * 24 * 60 * 60 # 90 days
17
-
18
- def initialize(pool = Sidekiq.redis_pool)
19
- @pool = pool
20
- end
21
-
22
- def mark(at: Time.now, label: "")
23
- # we need to round the timestamp so that we gracefully
24
- # handle an excepted common error in marking deploys:
25
- # having every process mark its deploy, leading
26
- # to N marks for each deploy. Instead we round the time
27
- # to the minute so that multple marks within that minute
28
- # will all naturally rollup into one mark per minute.
29
- whence = at.utc
30
- floor = Time.utc(whence.year, whence.month, whence.mday, whence.hour, whence.min, 0)
31
- datecode = floor.strftime("%Y%m%d")
32
- key = "#{datecode}-marks"
33
- @pool.with do |c|
34
- c.pipelined do |pipe|
35
- pipe.hsetnx(key, floor.iso8601, label)
36
- pipe.expire(key, MARK_TTL)
37
- end
38
- end
39
- end
40
-
41
- def fetch(date = Time.now.utc.to_date)
42
- datecode = date.strftime("%Y%m%d")
43
- @pool.with { |c| c.hgetall("#{datecode}-marks") }
44
- end
45
- end
46
- end
47
- end
@@ -1,370 +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_job_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
- # NB: the internal hash always has stringified keys
179
- @opts = opts.transform_keys(&:to_s)
180
-
181
- # ActiveJob compatibility
182
- interval = @opts.delete("wait_until") || @opts.delete("wait")
183
- at(interval) if interval
184
- end
185
-
186
- def set(options)
187
- hash = options.transform_keys(&:to_s)
188
- interval = hash.delete("wait_until") || @opts.delete("wait")
189
- @opts.merge!(hash)
190
- at(interval) if interval
191
- self
192
- end
193
-
194
- def perform_async(*args)
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
236
- end
237
- alias_method :perform_sync, :perform_inline
238
-
239
- def perform_bulk(args, batch_size: 1_000)
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))
243
- end
244
-
245
- result.is_a?(Enumerator::Lazy) ? result.force : result
246
- end
247
-
248
- # +interval+ must be a timestamp, numeric or something that acts
249
- # numeric (like an activesupport time interval).
250
- def perform_in(interval, *args)
251
- at(interval).perform_async(*args)
252
- end
253
- alias_method :perform_at, :perform_in
254
-
255
- private
256
-
257
- def at(interval)
258
- int = interval.to_f
259
- now = Time.now.to_f
260
- ts = ((int < 1_000_000_000) ? now + int : int)
261
- # Optimization to enqueue something now that is scheduled to go out now or in the past
262
- @opts["at"] = ts if ts > now
263
- self
264
- end
265
- end
266
-
267
- module ClassMethods
268
- def delay(*args)
269
- raise ArgumentError, "Do not call .delay on a Sidekiq::Worker class, call .perform_async"
270
- end
271
-
272
- def delay_for(*args)
273
- raise ArgumentError, "Do not call .delay_for on a Sidekiq::Worker class, call .perform_in"
274
- end
275
-
276
- def delay_until(*args)
277
- raise ArgumentError, "Do not call .delay_until on a Sidekiq::Worker class, call .perform_at"
278
- end
279
-
280
- def queue_as(q)
281
- sidekiq_options("queue" => q.to_s)
282
- end
283
-
284
- def set(options)
285
- Setter.new(self, options)
286
- end
287
-
288
- def perform_async(*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)
295
- end
296
- alias_method :perform_sync, :perform_inline
297
-
298
- ##
299
- # Push a large number of jobs to Redis, while limiting the batch of
300
- # each job payload to 1,000. This method helps cut down on the number
301
- # of round trips to Redis, which can increase the performance of enqueueing
302
- # large numbers of jobs.
303
- #
304
- # +items+ must be an Array of Arrays.
305
- #
306
- # For finer-grained control, use `Sidekiq::Client.push_bulk` directly.
307
- #
308
- # Example (3 Redis round trips):
309
- #
310
- # SomeWorker.perform_async(1)
311
- # SomeWorker.perform_async(2)
312
- # SomeWorker.perform_async(3)
313
- #
314
- # Would instead become (1 Redis round trip):
315
- #
316
- # SomeWorker.perform_bulk([[1], [2], [3]])
317
- #
318
- def perform_bulk(*args, **kwargs)
319
- Setter.new(self, {}).perform_bulk(*args, **kwargs)
320
- end
321
-
322
- # +interval+ must be a timestamp, numeric or something that acts
323
- # numeric (like an activesupport time interval).
324
- def perform_in(interval, *args)
325
- int = interval.to_f
326
- now = Time.now.to_f
327
- ts = ((int < 1_000_000_000) ? now + int : int)
328
-
329
- item = {"class" => self, "args" => args}
330
-
331
- # Optimization to enqueue something now that is scheduled to go out now or in the past
332
- item["at"] = ts if ts > now
333
-
334
- client_push(item)
335
- end
336
- alias_method :perform_at, :perform_in
337
-
338
- ##
339
- # Allows customization for this type of Worker.
340
- # Legal options:
341
- #
342
- # queue - use a named queue for this Worker, default 'default'
343
- # retry - enable retries via JobRetry, *true* to use the default
344
- # or *Integer* count
345
- # backtrace - whether to save any error backtrace in the retry payload to display in web UI,
346
- # can be true, false or an integer number of lines to save, default *false*
347
- # pool - use the given Redis connection pool to push this type of job to a given shard.
348
- #
349
- # In practice, any option is allowed. This is the main mechanism to configure the
350
- # options for a specific job.
351
- #
352
- # These options will be saved into the serialized job when enqueued by
353
- # the client.
354
- def sidekiq_options(opts = {})
355
- super
356
- end
357
-
358
- def client_push(item) # :nodoc:
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
362
-
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)
367
- end
368
- end
369
- end
370
- end