sidekiq 7.3.9 → 8.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Changes.md +225 -0
- data/README.md +34 -14
- data/bin/kiq +17 -0
- data/bin/lint-herb +13 -0
- data/bin/sidekiqload +12 -10
- data/bin/webload +69 -0
- data/lib/active_job/queue_adapters/sidekiq_adapter.rb +107 -58
- data/lib/generators/sidekiq/job_generator.rb +15 -3
- data/lib/generators/sidekiq/templates/job.rb.erb +1 -1
- data/lib/sidekiq/api.rb +310 -83
- data/lib/sidekiq/capsule.rb +10 -7
- data/lib/sidekiq/cli.rb +31 -24
- data/lib/sidekiq/client.rb +36 -18
- data/lib/sidekiq/component.rb +45 -3
- data/lib/sidekiq/config.rb +35 -30
- data/lib/sidekiq/embedded.rb +2 -1
- data/lib/sidekiq/fetch.rb +1 -0
- data/lib/sidekiq/iterable_job.rb +1 -0
- data/lib/sidekiq/job/iterable.rb +46 -18
- data/lib/sidekiq/job.rb +18 -3
- data/lib/sidekiq/job_logger.rb +8 -6
- data/lib/sidekiq/job_retry.rb +45 -18
- data/lib/sidekiq/job_util.rb +5 -1
- data/lib/sidekiq/launcher.rb +22 -12
- data/lib/sidekiq/loader.rb +57 -0
- data/lib/sidekiq/logger.rb +25 -69
- data/lib/sidekiq/manager.rb +2 -2
- data/lib/sidekiq/metrics/query.rb +77 -46
- data/lib/sidekiq/metrics/shared.rb +8 -5
- data/lib/sidekiq/metrics/tracking.rb +12 -7
- data/lib/sidekiq/middleware/current_attributes.rb +11 -19
- data/lib/sidekiq/middleware/i18n.rb +2 -0
- data/lib/sidekiq/monitor.rb +4 -8
- data/lib/sidekiq/paginator.rb +18 -2
- data/lib/sidekiq/processor.rb +26 -18
- data/lib/sidekiq/profiler.rb +73 -0
- data/lib/sidekiq/rails.rb +46 -67
- data/lib/sidekiq/redis_client_adapter.rb +6 -3
- data/lib/sidekiq/redis_connection.rb +16 -5
- data/lib/sidekiq/ring_buffer.rb +1 -0
- data/lib/sidekiq/scheduled.rb +6 -7
- data/lib/sidekiq/test_api.rb +331 -0
- data/lib/sidekiq/testing/inline.rb +2 -30
- data/lib/sidekiq/testing.rb +2 -333
- data/lib/sidekiq/transaction_aware_client.rb +13 -5
- data/lib/sidekiq/tui/controls.rb +53 -0
- data/lib/sidekiq/tui/filtering.rb +53 -0
- data/lib/sidekiq/tui/tabs/base_tab.rb +230 -0
- data/lib/sidekiq/tui/tabs/busy.rb +104 -0
- data/lib/sidekiq/tui/tabs/dead.rb +14 -0
- data/lib/sidekiq/tui/tabs/home.rb +115 -0
- data/lib/sidekiq/tui/tabs/metrics.rb +114 -0
- data/lib/sidekiq/tui/tabs/queues.rb +83 -0
- data/lib/sidekiq/tui/tabs/retries.rb +14 -0
- data/lib/sidekiq/tui/tabs/scheduled.rb +14 -0
- data/lib/sidekiq/tui/tabs/set_tab.rb +91 -0
- data/lib/sidekiq/tui/tabs.rb +15 -0
- data/lib/sidekiq/tui.rb +390 -0
- data/lib/sidekiq/version.rb +2 -2
- data/lib/sidekiq/web/action.rb +146 -83
- data/lib/sidekiq/web/application.rb +372 -328
- data/lib/sidekiq/web/config.rb +117 -0
- data/lib/sidekiq/web/helpers.rb +102 -44
- data/lib/sidekiq/web/router.rb +61 -77
- data/lib/sidekiq/web.rb +65 -151
- data/lib/sidekiq.rb +13 -1
- data/sidekiq.gemspec +9 -8
- data/web/assets/images/logo.png +0 -0
- data/web/assets/images/status.png +0 -0
- data/web/assets/javascripts/application.js +43 -27
- data/web/assets/javascripts/base-charts.js +30 -16
- data/web/assets/javascripts/chartjs-adapter-date-fns.min.js +7 -0
- data/web/assets/javascripts/dashboard.js +1 -1
- data/web/assets/javascripts/metrics.js +16 -34
- data/web/assets/stylesheets/style.css +776 -0
- data/web/locales/ar.yml +3 -1
- data/web/locales/cs.yml +2 -0
- data/web/locales/da.yml +2 -0
- data/web/locales/de.yml +2 -0
- data/web/locales/el.yml +2 -0
- data/web/locales/en.yml +8 -0
- data/web/locales/es.yml +25 -2
- data/web/locales/fa.yml +3 -1
- data/web/locales/fr.yml +3 -1
- data/web/locales/gd.yml +13 -1
- data/web/locales/he.yml +3 -1
- data/web/locales/hi.yml +36 -0
- data/web/locales/it.yml +9 -0
- data/web/locales/ja.yml +2 -0
- data/web/locales/ko.yml +2 -0
- data/web/locales/lt.yml +2 -0
- data/web/locales/nb.yml +2 -0
- data/web/locales/nl.yml +2 -0
- data/web/locales/pl.yml +2 -0
- data/web/locales/{pt-br.yml → pt-BR.yml} +3 -1
- data/web/locales/pt.yml +2 -0
- data/web/locales/ru.yml +2 -0
- data/web/locales/sv.yml +2 -0
- data/web/locales/ta.yml +83 -47
- data/web/locales/tr.yml +2 -0
- data/web/locales/uk.yml +7 -5
- data/web/locales/ur.yml +3 -1
- data/web/locales/vi.yml +2 -0
- data/web/locales/{zh-cn.yml → zh-CN.yml} +86 -73
- data/web/locales/{zh-tw.yml → zh-TW.yml} +3 -1
- data/web/views/_footer.html.erb +32 -0
- data/web/views/_job_info.html.erb +115 -0
- data/web/views/_metrics_period_select.html.erb +15 -0
- data/web/views/_nav.html.erb +45 -0
- data/web/views/_paging.html.erb +26 -0
- data/web/views/_poll_link.html.erb +4 -0
- data/web/views/{_summary.erb → _summary.html.erb} +16 -16
- data/web/views/busy.html.erb +155 -0
- data/web/views/dashboard.html.erb +104 -0
- data/web/views/dead.html.erb +38 -0
- data/web/views/filtering.html.erb +6 -0
- data/web/views/layout.html.erb +26 -0
- data/web/views/metrics.html.erb +85 -0
- data/web/views/metrics_for_job.html.erb +58 -0
- data/web/views/morgue.html.erb +69 -0
- data/web/views/profiles.html.erb +43 -0
- data/web/views/queue.html.erb +57 -0
- data/web/views/queues.html.erb +46 -0
- data/web/views/retries.html.erb +77 -0
- data/web/views/retry.html.erb +39 -0
- data/web/views/scheduled.html.erb +64 -0
- data/web/views/{scheduled_job_info.erb → scheduled_job_info.html.erb} +3 -3
- metadata +65 -47
- data/lib/sidekiq/web/csrf_protection.rb +0 -183
- data/web/assets/stylesheets/application-dark.css +0 -147
- data/web/assets/stylesheets/application-rtl.css +0 -163
- data/web/assets/stylesheets/application.css +0 -759
- data/web/assets/stylesheets/bootstrap-rtl.min.css +0 -9
- data/web/assets/stylesheets/bootstrap.css +0 -5
- data/web/views/_footer.erb +0 -34
- data/web/views/_job_info.erb +0 -105
- data/web/views/_metrics_period_select.erb +0 -12
- data/web/views/_nav.erb +0 -52
- data/web/views/_paging.erb +0 -25
- data/web/views/_poll_link.erb +0 -4
- data/web/views/_status.erb +0 -4
- data/web/views/busy.erb +0 -148
- data/web/views/dashboard.erb +0 -108
- data/web/views/dead.erb +0 -34
- data/web/views/filtering.erb +0 -6
- data/web/views/layout.erb +0 -42
- data/web/views/metrics.erb +0 -90
- data/web/views/metrics_for_job.erb +0 -59
- data/web/views/morgue.erb +0 -74
- data/web/views/queue.erb +0 -55
- data/web/views/queues.erb +0 -44
- data/web/views/retries.erb +0 -79
- data/web/views/retry.erb +0 -34
- data/web/views/scheduled.erb +0 -56
data/lib/sidekiq/job.rb
CHANGED
|
@@ -196,7 +196,7 @@ module Sidekiq
|
|
|
196
196
|
|
|
197
197
|
def set(options)
|
|
198
198
|
hash = options.transform_keys(&:to_s)
|
|
199
|
-
interval = hash.delete("wait_until") ||
|
|
199
|
+
interval = hash.delete("wait_until") || hash.delete("wait")
|
|
200
200
|
@opts.merge!(hash)
|
|
201
201
|
at(interval) if interval
|
|
202
202
|
self
|
|
@@ -226,6 +226,8 @@ module Sidekiq
|
|
|
226
226
|
end
|
|
227
227
|
return nil unless result
|
|
228
228
|
|
|
229
|
+
verify_json(item)
|
|
230
|
+
|
|
229
231
|
# round-trip the payload via JSON
|
|
230
232
|
msg = Sidekiq.load_json(Sidekiq.dump_json(item))
|
|
231
233
|
|
|
@@ -248,9 +250,9 @@ module Sidekiq
|
|
|
248
250
|
end
|
|
249
251
|
alias_method :perform_sync, :perform_inline
|
|
250
252
|
|
|
251
|
-
def perform_bulk(args,
|
|
253
|
+
def perform_bulk(args, **options)
|
|
252
254
|
client = @klass.build_client
|
|
253
|
-
client.push_bulk(@opts.merge("class" => @klass, "args" => args,
|
|
255
|
+
client.push_bulk(@opts.merge({"class" => @klass, "args" => args}, options))
|
|
254
256
|
end
|
|
255
257
|
|
|
256
258
|
# +interval+ must be a timestamp, numeric or something that acts
|
|
@@ -311,6 +313,11 @@ module Sidekiq
|
|
|
311
313
|
#
|
|
312
314
|
# +items+ must be an Array of Arrays.
|
|
313
315
|
#
|
|
316
|
+
# The +:at+ option schedules the jobs for future execution. It accepts
|
|
317
|
+
# either a single Numeric timestamp (or seconds-from-now) applied to every
|
|
318
|
+
# job, or an Array of Numeric values with the same size as +items+ to
|
|
319
|
+
# schedule each job at its corresponding time.
|
|
320
|
+
#
|
|
314
321
|
# For finer-grained control, use `Sidekiq::Client.push_bulk` directly.
|
|
315
322
|
#
|
|
316
323
|
# Example (3 Redis round trips):
|
|
@@ -323,6 +330,14 @@ module Sidekiq
|
|
|
323
330
|
#
|
|
324
331
|
# SomeJob.perform_bulk([[1], [2], [3]])
|
|
325
332
|
#
|
|
333
|
+
# Scheduling every job 60 seconds from now (single Numeric +:at+):
|
|
334
|
+
#
|
|
335
|
+
# SomeJob.perform_bulk([[1], [2], [3]], at: 60)
|
|
336
|
+
#
|
|
337
|
+
# Scheduling each job at its own time (Array +:at+):
|
|
338
|
+
#
|
|
339
|
+
# SomeJob.perform_bulk([[1], [2]], at: [Time.now.to_f + 30, Time.now.to_f + 60])
|
|
340
|
+
#
|
|
326
341
|
def perform_bulk(*args, **kwargs)
|
|
327
342
|
Setter.new(self, {}).perform_bulk(*args, **kwargs)
|
|
328
343
|
end
|
data/lib/sidekiq/job_logger.rb
CHANGED
|
@@ -26,16 +26,18 @@ module Sidekiq
|
|
|
26
26
|
# If we're using a wrapper class, like ActiveJob, use the "wrapped"
|
|
27
27
|
# attribute to expose the underlying thing.
|
|
28
28
|
h = {
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
jid: job_hash["jid"],
|
|
30
|
+
class: job_hash["wrapped"] || job_hash["class"]
|
|
31
31
|
}
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
|
|
33
|
+
@config[:logged_job_attributes].each do |attr|
|
|
34
|
+
h[attr.to_sym] = job_hash[attr] if job_hash.has_key?(attr)
|
|
35
|
+
end
|
|
34
36
|
|
|
35
37
|
Thread.current[:sidekiq_context] = h
|
|
36
38
|
level = job_hash["log_level"]
|
|
37
|
-
if level
|
|
38
|
-
@logger.
|
|
39
|
+
if level
|
|
40
|
+
@logger.with_level(level, &block)
|
|
39
41
|
else
|
|
40
42
|
yield
|
|
41
43
|
end
|
data/lib/sidekiq/job_retry.rb
CHANGED
|
@@ -72,8 +72,8 @@ module Sidekiq
|
|
|
72
72
|
|
|
73
73
|
DEFAULT_MAX_RETRY_ATTEMPTS = 25
|
|
74
74
|
|
|
75
|
-
def initialize(
|
|
76
|
-
@config =
|
|
75
|
+
def initialize(cfg)
|
|
76
|
+
@config = cfg
|
|
77
77
|
@max_retries = Sidekiq.default_configuration[:max_retries] || DEFAULT_MAX_RETRY_ATTEMPTS
|
|
78
78
|
@backtrace_cleaner = Sidekiq.default_configuration[:backtrace_cleaner]
|
|
79
79
|
end
|
|
@@ -96,7 +96,7 @@ module Sidekiq
|
|
|
96
96
|
if msg["retry"]
|
|
97
97
|
process_retry(nil, msg, queue, e)
|
|
98
98
|
else
|
|
99
|
-
@
|
|
99
|
+
@config[:death_handlers].each do |handler|
|
|
100
100
|
handler.call(msg, e)
|
|
101
101
|
rescue => handler_ex
|
|
102
102
|
handle_exception(handler_ex, {context: "Error calling death handler", job: msg})
|
|
@@ -139,6 +139,10 @@ module Sidekiq
|
|
|
139
139
|
|
|
140
140
|
private
|
|
141
141
|
|
|
142
|
+
def now_ms
|
|
143
|
+
::Process.clock_gettime(::Process::CLOCK_REALTIME, :millisecond)
|
|
144
|
+
end
|
|
145
|
+
|
|
142
146
|
# Note that +jobinst+ can be nil here if an error is raised before we can
|
|
143
147
|
# instantiate the job instance. All access must be guarded and
|
|
144
148
|
# best effort.
|
|
@@ -149,17 +153,17 @@ module Sidekiq
|
|
|
149
153
|
|
|
150
154
|
m = exception_message(exception)
|
|
151
155
|
if m.respond_to?(:scrub!)
|
|
152
|
-
m.force_encoding(
|
|
156
|
+
m.force_encoding(Encoding::UTF_8)
|
|
153
157
|
m.scrub!
|
|
154
158
|
end
|
|
155
159
|
|
|
156
160
|
msg["error_message"] = m
|
|
157
161
|
msg["error_class"] = exception.class.name
|
|
158
162
|
count = if msg["retry_count"]
|
|
159
|
-
msg["retried_at"] =
|
|
163
|
+
msg["retried_at"] = now_ms
|
|
160
164
|
msg["retry_count"] += 1
|
|
161
165
|
else
|
|
162
|
-
msg["failed_at"] =
|
|
166
|
+
msg["failed_at"] = now_ms
|
|
163
167
|
msg["retry_count"] = 0
|
|
164
168
|
end
|
|
165
169
|
|
|
@@ -174,22 +178,28 @@ module Sidekiq
|
|
|
174
178
|
msg["error_backtrace"] = compress_backtrace(lines)
|
|
175
179
|
end
|
|
176
180
|
|
|
177
|
-
|
|
178
|
-
|
|
181
|
+
# retry_for and retry are mutually exclusive - if retry_for is set,
|
|
182
|
+
# we exclusively use duration-based retry logic and ignore count-based logic
|
|
179
183
|
rf = msg["retry_for"]
|
|
180
|
-
|
|
184
|
+
if rf
|
|
185
|
+
return retries_exhausted(jobinst, msg, exception) if (time_for(msg["failed_at"]) + rf) < Time.now
|
|
186
|
+
elsif count >= max_retry_attempts
|
|
187
|
+
return retries_exhausted(jobinst, msg, exception)
|
|
188
|
+
end
|
|
181
189
|
|
|
182
190
|
strategy, delay = delay_for(jobinst, count, exception, msg)
|
|
183
191
|
case strategy
|
|
184
192
|
when :discard
|
|
185
|
-
|
|
193
|
+
msg["discarded_at"] = now_ms
|
|
194
|
+
|
|
195
|
+
return run_death_handlers(msg, exception)
|
|
186
196
|
when :kill
|
|
187
197
|
return retries_exhausted(jobinst, msg, exception)
|
|
188
198
|
end
|
|
189
199
|
|
|
190
200
|
# Logging here can break retries if the logging device raises ENOSPC #3979
|
|
191
201
|
# logger.debug { "Failure! Retry #{count} in #{delay} seconds" }
|
|
192
|
-
jitter = rand(10
|
|
202
|
+
jitter = rand(10 * (count + 1))
|
|
193
203
|
retry_at = Time.now.to_f + delay + jitter
|
|
194
204
|
payload = Sidekiq.dump_json(msg)
|
|
195
205
|
redis do |conn|
|
|
@@ -197,6 +207,14 @@ module Sidekiq
|
|
|
197
207
|
end
|
|
198
208
|
end
|
|
199
209
|
|
|
210
|
+
def time_for(item)
|
|
211
|
+
if item.is_a?(Float)
|
|
212
|
+
Time.at(item)
|
|
213
|
+
else
|
|
214
|
+
Time.at(item / 1000, item % 1000, :millisecond)
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
200
218
|
# returns (strategy, seconds)
|
|
201
219
|
def delay_for(jobinst, count, exception, msg)
|
|
202
220
|
rv = begin
|
|
@@ -243,13 +261,22 @@ module Sidekiq
|
|
|
243
261
|
handle_exception(e, {context: "Error calling retries_exhausted", job: msg})
|
|
244
262
|
end
|
|
245
263
|
|
|
246
|
-
|
|
247
|
-
|
|
264
|
+
discarded = msg["dead"] == false || rv == :discard
|
|
265
|
+
|
|
266
|
+
if discarded
|
|
267
|
+
msg["discarded_at"] = now_ms
|
|
268
|
+
else
|
|
269
|
+
send_to_morgue(msg)
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
run_death_handlers(msg, exception)
|
|
273
|
+
end
|
|
248
274
|
|
|
249
|
-
|
|
250
|
-
|
|
275
|
+
def run_death_handlers(job, exception)
|
|
276
|
+
@config[:death_handlers].each do |handler|
|
|
277
|
+
handler.call(job, exception)
|
|
251
278
|
rescue => e
|
|
252
|
-
handle_exception(e, {context: "Error calling death handler", job:
|
|
279
|
+
handle_exception(e, {context: "Error calling death handler", job: job})
|
|
253
280
|
end
|
|
254
281
|
end
|
|
255
282
|
|
|
@@ -261,8 +288,8 @@ module Sidekiq
|
|
|
261
288
|
redis do |conn|
|
|
262
289
|
conn.multi do |xa|
|
|
263
290
|
xa.zadd("dead", now.to_s, payload)
|
|
264
|
-
xa.zremrangebyscore("dead", "-inf", now - @
|
|
265
|
-
xa.zremrangebyrank("dead", 0, - @
|
|
291
|
+
xa.zremrangebyscore("dead", "-inf", now - @config[:dead_timeout_in_seconds])
|
|
292
|
+
xa.zremrangebyrank("dead", 0, - @config[:dead_max_jobs])
|
|
266
293
|
end
|
|
267
294
|
end
|
|
268
295
|
end
|
data/lib/sidekiq/job_util.rb
CHANGED
|
@@ -58,10 +58,14 @@ module Sidekiq
|
|
|
58
58
|
item["class"] = item["class"].to_s
|
|
59
59
|
item["queue"] = item["queue"].to_s
|
|
60
60
|
item["retry_for"] = item["retry_for"].to_i if item["retry_for"]
|
|
61
|
-
item["created_at"] ||=
|
|
61
|
+
item["created_at"] ||= now_in_millis
|
|
62
62
|
item
|
|
63
63
|
end
|
|
64
64
|
|
|
65
|
+
def now_in_millis
|
|
66
|
+
::Process.clock_gettime(::Process::CLOCK_REALTIME, :millisecond)
|
|
67
|
+
end
|
|
68
|
+
|
|
65
69
|
def normalized_hash(item_class)
|
|
66
70
|
if item_class.is_a?(Class)
|
|
67
71
|
raise(ArgumentError, "Message must include a Sidekiq::Job class, not class name: #{item_class.ancestors.inspect}") unless item_class.respond_to?(:get_sidekiq_options)
|
data/lib/sidekiq/launcher.rb
CHANGED
|
@@ -68,6 +68,7 @@ module Sidekiq
|
|
|
68
68
|
stoppers.each(&:join)
|
|
69
69
|
|
|
70
70
|
clear_heartbeat
|
|
71
|
+
fire_event(:exit, reverse: true)
|
|
71
72
|
end
|
|
72
73
|
|
|
73
74
|
def stopping?
|
|
@@ -81,7 +82,7 @@ module Sidekiq
|
|
|
81
82
|
❤
|
|
82
83
|
end
|
|
83
84
|
|
|
84
|
-
private
|
|
85
|
+
private
|
|
85
86
|
|
|
86
87
|
BEAT_PAUSE = 10
|
|
87
88
|
|
|
@@ -141,6 +142,12 @@ module Sidekiq
|
|
|
141
142
|
key = identity
|
|
142
143
|
fails = procd = 0
|
|
143
144
|
|
|
145
|
+
idle_timeout = config[:redis_idle_timeout]
|
|
146
|
+
if idle_timeout
|
|
147
|
+
config.capsules.each_value { |cap| cap.local_redis_pool.reap(idle_seconds: idle_timeout, &:close) }
|
|
148
|
+
config.local_redis_pool.reap(idle_seconds: idle_timeout, &:close)
|
|
149
|
+
end
|
|
150
|
+
|
|
144
151
|
begin
|
|
145
152
|
flush_stats
|
|
146
153
|
|
|
@@ -169,6 +176,7 @@ module Sidekiq
|
|
|
169
176
|
transaction.sadd("processes", [key])
|
|
170
177
|
transaction.exists(key)
|
|
171
178
|
transaction.hset(key, "info", to_json,
|
|
179
|
+
"concurrency", @config.total_concurrency,
|
|
172
180
|
"busy", curstate.size,
|
|
173
181
|
"beat", Time.now.to_f,
|
|
174
182
|
"rtt_us", rtt,
|
|
@@ -213,11 +221,11 @@ module Sidekiq
|
|
|
213
221
|
# Log a warning if it's a disaster.
|
|
214
222
|
if RTT_READINGS.all? { |x| x > RTT_WARNING_LEVEL }
|
|
215
223
|
logger.warn <<~EOM
|
|
216
|
-
Your Redis network connection
|
|
224
|
+
Your Redis network connection appears to be performing poorly.
|
|
217
225
|
Last RTT readings were #{RTT_READINGS.buffer.inspect}, ideally these should be < 1000.
|
|
218
|
-
Ensure Redis is running in the same AZ or datacenter as Sidekiq
|
|
219
|
-
|
|
220
|
-
|
|
226
|
+
Ensure Redis is running in the same AZ or datacenter as Sidekiq and that
|
|
227
|
+
your Sidekiq process is not CPU-saturated; reduce your concurrency and/or
|
|
228
|
+
see https://github.com/sidekiq/sidekiq/discussions/5039
|
|
221
229
|
EOM
|
|
222
230
|
RTT_READINGS.reset
|
|
223
231
|
end
|
|
@@ -227,7 +235,7 @@ module Sidekiq
|
|
|
227
235
|
MEMORY_GRABBER = case RUBY_PLATFORM
|
|
228
236
|
when /linux/
|
|
229
237
|
->(pid) {
|
|
230
|
-
IO.readlines("/proc/#{
|
|
238
|
+
IO.readlines("/proc/#{pid}/status").each do |line|
|
|
231
239
|
next unless line.start_with?("VmRSS:")
|
|
232
240
|
break line.split[1].to_i
|
|
233
241
|
end
|
|
@@ -250,9 +258,15 @@ module Sidekiq
|
|
|
250
258
|
"started_at" => Time.now.to_f,
|
|
251
259
|
"pid" => ::Process.pid,
|
|
252
260
|
"tag" => @config[:tag] || "",
|
|
253
|
-
"
|
|
261
|
+
"capsules" => @config.capsules.each_with_object({}) { |(name, cap), memo|
|
|
262
|
+
memo[name] = cap.to_h
|
|
263
|
+
},
|
|
264
|
+
#####
|
|
265
|
+
# TODO deprecated, remove in 9.0
|
|
266
|
+
# This data is now found in the `capsules` element above
|
|
254
267
|
"queues" => @config.capsules.values.flat_map { |cap| cap.queues }.uniq,
|
|
255
|
-
"weights" =>
|
|
268
|
+
"weights" => @config.capsules.values.map(&:weights),
|
|
269
|
+
#####
|
|
256
270
|
"labels" => @config[:labels].to_a,
|
|
257
271
|
"identity" => identity,
|
|
258
272
|
"version" => Sidekiq::VERSION,
|
|
@@ -260,10 +274,6 @@ module Sidekiq
|
|
|
260
274
|
}
|
|
261
275
|
end
|
|
262
276
|
|
|
263
|
-
def to_weights
|
|
264
|
-
@config.capsules.values.map(&:weights)
|
|
265
|
-
end
|
|
266
|
-
|
|
267
277
|
def to_json
|
|
268
278
|
# this data changes infrequently so dump it to a string
|
|
269
279
|
# now so we don't need to dump it every heartbeat.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
module Sidekiq
|
|
2
|
+
require "sidekiq/component"
|
|
3
|
+
|
|
4
|
+
class Loader
|
|
5
|
+
include Sidekiq::Component
|
|
6
|
+
|
|
7
|
+
def initialize(cfg = Sidekiq.default_configuration)
|
|
8
|
+
@config = cfg
|
|
9
|
+
@load_hooks = Hash.new { |h, k| h[k] = [] }
|
|
10
|
+
@loaded = Set.new
|
|
11
|
+
@lock = Mutex.new
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Declares a block that will be executed when a Sidekiq component is fully
|
|
15
|
+
# loaded. If the component has already loaded, the block is executed
|
|
16
|
+
# immediately.
|
|
17
|
+
#
|
|
18
|
+
# Sidekiq.loader.on_load(:api) do
|
|
19
|
+
# # extend the sidekiq API
|
|
20
|
+
# end
|
|
21
|
+
#
|
|
22
|
+
def on_load(name, &block)
|
|
23
|
+
# we don't want to hold the lock while calling the block
|
|
24
|
+
to_run = nil
|
|
25
|
+
|
|
26
|
+
@lock.synchronize do
|
|
27
|
+
if @loaded.include?(name)
|
|
28
|
+
to_run = block
|
|
29
|
+
else
|
|
30
|
+
@load_hooks[name] << block
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
to_run&.call
|
|
35
|
+
nil
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Executes all blocks registered to +name+ via on_load.
|
|
39
|
+
#
|
|
40
|
+
# Sidekiq.loader.run_load_hooks(:api)
|
|
41
|
+
#
|
|
42
|
+
# In the case of the above example, it will execute all hooks registered for +:api+.
|
|
43
|
+
#
|
|
44
|
+
def run_load_hooks(name)
|
|
45
|
+
hks = @lock.synchronize do
|
|
46
|
+
@loaded << name
|
|
47
|
+
@load_hooks.delete(name)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
hks&.each do |blk|
|
|
51
|
+
blk.call
|
|
52
|
+
rescue => ex
|
|
53
|
+
handle_exception(ex, hook: name)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
data/lib/sidekiq/logger.rb
CHANGED
|
@@ -22,92 +22,48 @@ module Sidekiq
|
|
|
22
22
|
end
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
-
module LoggingUtils
|
|
26
|
-
LEVELS = {
|
|
27
|
-
"debug" => 0,
|
|
28
|
-
"info" => 1,
|
|
29
|
-
"warn" => 2,
|
|
30
|
-
"error" => 3,
|
|
31
|
-
"fatal" => 4
|
|
32
|
-
}
|
|
33
|
-
LEVELS.default_proc = proc do |_, level|
|
|
34
|
-
puts("Invalid log level: #{level.inspect}")
|
|
35
|
-
nil
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
LEVELS.each do |level, numeric_level|
|
|
39
|
-
define_method(:"#{level}?") do
|
|
40
|
-
local_level.nil? ? super() : local_level <= numeric_level
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def local_level
|
|
45
|
-
Thread.current[:sidekiq_log_level]
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
def local_level=(level)
|
|
49
|
-
case level
|
|
50
|
-
when Integer
|
|
51
|
-
Thread.current[:sidekiq_log_level] = level
|
|
52
|
-
when Symbol, String
|
|
53
|
-
Thread.current[:sidekiq_log_level] = LEVELS[level.to_s]
|
|
54
|
-
when nil
|
|
55
|
-
Thread.current[:sidekiq_log_level] = nil
|
|
56
|
-
else
|
|
57
|
-
raise ArgumentError, "Invalid log level: #{level.inspect}"
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
def level
|
|
62
|
-
local_level || super
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
# Change the thread-local level for the duration of the given block.
|
|
66
|
-
def log_at(level)
|
|
67
|
-
old_local_level = local_level
|
|
68
|
-
self.local_level = level
|
|
69
|
-
yield
|
|
70
|
-
ensure
|
|
71
|
-
self.local_level = old_local_level
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
|
|
75
25
|
class Logger < ::Logger
|
|
76
|
-
include LoggingUtils
|
|
77
|
-
|
|
78
26
|
module Formatters
|
|
79
27
|
class Base < ::Logger::Formatter
|
|
28
|
+
COLORS = {
|
|
29
|
+
"DEBUG" => "\e[1;32mDEBUG\e[0m", # green
|
|
30
|
+
"INFO" => "\e[1;34mINFO \e[0m", # blue
|
|
31
|
+
"WARN" => "\e[1;33mWARN \e[0m", # yellow
|
|
32
|
+
"ERROR" => "\e[1;31mERROR\e[0m", # red
|
|
33
|
+
"FATAL" => "\e[1;35mFATAL\e[0m" # pink
|
|
34
|
+
}
|
|
35
|
+
|
|
80
36
|
def tid
|
|
81
37
|
Thread.current["sidekiq_tid"] ||= (Thread.current.object_id ^ ::Process.pid).to_s(36)
|
|
82
38
|
end
|
|
83
39
|
|
|
84
|
-
def
|
|
85
|
-
|
|
40
|
+
def format_context(ctxt = Sidekiq::Context.current)
|
|
41
|
+
(ctxt.size == 0) ? "" : " #{ctxt.map { |k, v|
|
|
42
|
+
case v
|
|
43
|
+
when Array
|
|
44
|
+
"#{k}=#{v.join(",")}"
|
|
45
|
+
else
|
|
46
|
+
"#{k}=#{v}"
|
|
47
|
+
end
|
|
48
|
+
}.join(" ")}"
|
|
86
49
|
end
|
|
50
|
+
end
|
|
87
51
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
case v
|
|
92
|
-
when Array
|
|
93
|
-
"#{k}=#{v.join(",")}"
|
|
94
|
-
else
|
|
95
|
-
"#{k}=#{v}"
|
|
96
|
-
end
|
|
97
|
-
}.join(" ")
|
|
98
|
-
end
|
|
52
|
+
class Pretty < Base
|
|
53
|
+
def call(severity, time, program_name, message)
|
|
54
|
+
"#{COLORS[severity]} #{time.utc.iso8601(3)} pid=#{::Process.pid} tid=#{tid}#{format_context}: #{message}\n"
|
|
99
55
|
end
|
|
100
56
|
end
|
|
101
57
|
|
|
102
|
-
class
|
|
58
|
+
class Plain < Base
|
|
103
59
|
def call(severity, time, program_name, message)
|
|
104
|
-
"#{time.utc.iso8601(3)} pid=#{::Process.pid} tid=#{tid}#{format_context}
|
|
60
|
+
"#{severity} #{time.utc.iso8601(3)} pid=#{::Process.pid} tid=#{tid}#{format_context}: #{message}\n"
|
|
105
61
|
end
|
|
106
62
|
end
|
|
107
63
|
|
|
108
64
|
class WithoutTimestamp < Pretty
|
|
109
65
|
def call(severity, time, program_name, message)
|
|
110
|
-
"pid=#{::Process.pid} tid=#{tid}#{format_context}
|
|
66
|
+
"#{COLORS[severity]} pid=#{::Process.pid} tid=#{tid}#{format_context}: #{message}\n"
|
|
111
67
|
end
|
|
112
68
|
end
|
|
113
69
|
|
|
@@ -120,7 +76,7 @@ module Sidekiq
|
|
|
120
76
|
lvl: severity,
|
|
121
77
|
msg: message
|
|
122
78
|
}
|
|
123
|
-
c =
|
|
79
|
+
c = Sidekiq::Context.current
|
|
124
80
|
hash["ctx"] = c unless c.empty?
|
|
125
81
|
|
|
126
82
|
Sidekiq.dump_json(hash) << "\n"
|
data/lib/sidekiq/manager.rb
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "sidekiq/processor"
|
|
4
|
-
require "set"
|
|
5
4
|
|
|
6
5
|
module Sidekiq
|
|
7
6
|
##
|
|
@@ -70,12 +69,13 @@ module Sidekiq
|
|
|
70
69
|
def processor_result(processor, reason = nil)
|
|
71
70
|
@plock.synchronize do
|
|
72
71
|
@workers.delete(processor)
|
|
73
|
-
|
|
72
|
+
if !@done && @count > @workers.size
|
|
74
73
|
p = Processor.new(@config, &method(:processor_result))
|
|
75
74
|
@workers << p
|
|
76
75
|
p.start
|
|
77
76
|
end
|
|
78
77
|
end
|
|
78
|
+
nil
|
|
79
79
|
end
|
|
80
80
|
|
|
81
81
|
def stopped?
|