sidekiq 6.2.2 → 8.1.5
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 +726 -11
- data/LICENSE.txt +9 -0
- data/README.md +70 -39
- data/bin/kiq +17 -0
- data/bin/lint-herb +13 -0
- data/bin/multi_queue_bench +271 -0
- data/bin/sidekiq +4 -9
- data/bin/sidekiqload +214 -115
- data/bin/sidekiqmon +4 -1
- data/bin/webload +69 -0
- data/lib/active_job/queue_adapters/sidekiq_adapter.rb +124 -0
- data/lib/generators/sidekiq/job_generator.rb +71 -0
- data/lib/generators/sidekiq/templates/{worker.rb.erb → job.rb.erb} +3 -3
- data/lib/generators/sidekiq/templates/{worker_spec.rb.erb → job_spec.rb.erb} +1 -1
- data/lib/generators/sidekiq/templates/{worker_test.rb.erb → job_test.rb.erb} +1 -1
- data/lib/sidekiq/api.rb +729 -264
- data/lib/sidekiq/capsule.rb +135 -0
- data/lib/sidekiq/cli.rb +124 -100
- data/lib/sidekiq/client.rb +153 -106
- data/lib/sidekiq/component.rb +132 -0
- data/lib/sidekiq/config.rb +320 -0
- data/lib/sidekiq/deploy.rb +64 -0
- data/lib/sidekiq/embedded.rb +64 -0
- data/lib/sidekiq/fetch.rb +27 -26
- data/lib/sidekiq/iterable_job.rb +56 -0
- data/lib/sidekiq/job/interrupt_handler.rb +24 -0
- data/lib/sidekiq/job/iterable/active_record_enumerator.rb +53 -0
- data/lib/sidekiq/job/iterable/csv_enumerator.rb +47 -0
- data/lib/sidekiq/job/iterable/enumerators.rb +135 -0
- data/lib/sidekiq/job/iterable.rb +322 -0
- data/lib/sidekiq/job.rb +397 -5
- data/lib/sidekiq/job_logger.rb +23 -32
- data/lib/sidekiq/job_retry.rb +141 -68
- data/lib/sidekiq/job_util.rb +113 -0
- data/lib/sidekiq/launcher.rb +122 -98
- data/lib/sidekiq/loader.rb +57 -0
- data/lib/sidekiq/logger.rb +27 -106
- data/lib/sidekiq/manager.rb +41 -43
- data/lib/sidekiq/metrics/query.rb +184 -0
- data/lib/sidekiq/metrics/shared.rb +109 -0
- data/lib/sidekiq/metrics/tracking.rb +153 -0
- data/lib/sidekiq/middleware/chain.rb +96 -51
- data/lib/sidekiq/middleware/current_attributes.rb +120 -0
- data/lib/sidekiq/middleware/i18n.rb +8 -4
- data/lib/sidekiq/middleware/modules.rb +23 -0
- data/lib/sidekiq/monitor.rb +16 -6
- data/lib/sidekiq/paginator.rb +37 -10
- data/lib/sidekiq/processor.rb +105 -87
- data/lib/sidekiq/profiler.rb +73 -0
- data/lib/sidekiq/rails.rb +49 -36
- data/lib/sidekiq/redis_client_adapter.rb +117 -0
- data/lib/sidekiq/redis_connection.rb +55 -86
- data/lib/sidekiq/ring_buffer.rb +32 -0
- data/lib/sidekiq/scheduled.rb +106 -50
- data/lib/sidekiq/systemd.rb +2 -0
- data/lib/sidekiq/test_api.rb +331 -0
- data/lib/sidekiq/testing/inline.rb +2 -30
- data/lib/sidekiq/testing.rb +2 -342
- data/lib/sidekiq/transaction_aware_client.rb +59 -0
- data/lib/sidekiq/tui/controls.rb +53 -0
- data/lib/sidekiq/tui/filtering.rb +53 -0
- data/lib/sidekiq/tui/tabs/base_tab.rb +204 -0
- data/lib/sidekiq/tui/tabs/busy.rb +118 -0
- data/lib/sidekiq/tui/tabs/dead.rb +19 -0
- data/lib/sidekiq/tui/tabs/home.rb +144 -0
- data/lib/sidekiq/tui/tabs/metrics.rb +131 -0
- data/lib/sidekiq/tui/tabs/queues.rb +95 -0
- data/lib/sidekiq/tui/tabs/retries.rb +19 -0
- data/lib/sidekiq/tui/tabs/scheduled.rb +19 -0
- data/lib/sidekiq/tui/tabs/set_tab.rb +96 -0
- data/lib/sidekiq/tui/tabs.rb +15 -0
- data/lib/sidekiq/tui.rb +382 -0
- data/lib/sidekiq/version.rb +6 -1
- data/lib/sidekiq/web/action.rb +149 -64
- data/lib/sidekiq/web/application.rb +376 -268
- data/lib/sidekiq/web/config.rb +117 -0
- data/lib/sidekiq/web/helpers.rb +213 -87
- data/lib/sidekiq/web/router.rb +61 -74
- data/lib/sidekiq/web.rb +71 -100
- data/lib/sidekiq/worker_compatibility_alias.rb +13 -0
- data/lib/sidekiq.rb +95 -196
- data/sidekiq.gemspec +14 -11
- data/web/assets/images/logo.png +0 -0
- data/web/assets/images/status.png +0 -0
- data/web/assets/javascripts/application.js +171 -57
- data/web/assets/javascripts/base-charts.js +120 -0
- data/web/assets/javascripts/chart.min.js +13 -0
- data/web/assets/javascripts/chartjs-adapter-date-fns.min.js +7 -0
- data/web/assets/javascripts/chartjs-plugin-annotation.min.js +7 -0
- data/web/assets/javascripts/dashboard-charts.js +194 -0
- data/web/assets/javascripts/dashboard.js +41 -274
- data/web/assets/javascripts/metrics.js +280 -0
- data/web/assets/stylesheets/style.css +776 -0
- data/web/locales/ar.yml +72 -70
- data/web/locales/cs.yml +64 -62
- data/web/locales/da.yml +62 -53
- data/web/locales/de.yml +67 -65
- data/web/locales/el.yml +45 -24
- data/web/locales/en.yml +93 -69
- data/web/locales/es.yml +91 -68
- data/web/locales/fa.yml +67 -65
- data/web/locales/fr.yml +82 -67
- data/web/locales/gd.yml +110 -0
- data/web/locales/he.yml +67 -64
- data/web/locales/hi.yml +61 -59
- data/web/locales/it.yml +94 -54
- data/web/locales/ja.yml +74 -68
- data/web/locales/ko.yml +54 -52
- data/web/locales/lt.yml +68 -66
- data/web/locales/nb.yml +63 -61
- data/web/locales/nl.yml +54 -52
- data/web/locales/pl.yml +47 -45
- data/web/locales/{pt-br.yml → pt-BR.yml} +85 -56
- data/web/locales/pt.yml +53 -51
- data/web/locales/ru.yml +69 -66
- data/web/locales/sv.yml +55 -53
- data/web/locales/ta.yml +62 -60
- data/web/locales/tr.yml +102 -0
- data/web/locales/uk.yml +87 -61
- data/web/locales/ur.yml +66 -64
- data/web/locales/vi.yml +69 -67
- data/web/locales/zh-CN.yml +107 -0
- data/web/locales/{zh-tw.yml → zh-TW.yml} +44 -9
- 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.html.erb +40 -0
- data/web/views/busy.html.erb +151 -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 +130 -61
- data/LICENSE +0 -9
- data/lib/generators/sidekiq/worker_generator.rb +0 -57
- data/lib/sidekiq/delay.rb +0 -41
- data/lib/sidekiq/exception_handler.rb +0 -27
- data/lib/sidekiq/extensions/action_mailer.rb +0 -48
- data/lib/sidekiq/extensions/active_record.rb +0 -43
- data/lib/sidekiq/extensions/class_methods.rb +0 -43
- data/lib/sidekiq/extensions/generic_proxy.rb +0 -33
- data/lib/sidekiq/util.rb +0 -95
- data/lib/sidekiq/web/csrf_protection.rb +0 -180
- data/lib/sidekiq/worker.rb +0 -244
- data/web/assets/stylesheets/application-dark.css +0 -147
- data/web/assets/stylesheets/application-rtl.css +0 -246
- data/web/assets/stylesheets/application.css +0 -1053
- data/web/assets/stylesheets/bootstrap-rtl.min.css +0 -9
- data/web/assets/stylesheets/bootstrap.css +0 -5
- data/web/locales/zh-cn.yml +0 -68
- data/web/views/_footer.erb +0 -20
- data/web/views/_job_info.erb +0 -89
- data/web/views/_nav.erb +0 -52
- data/web/views/_paging.erb +0 -23
- data/web/views/_poll_link.erb +0 -7
- data/web/views/_status.erb +0 -4
- data/web/views/_summary.erb +0 -40
- data/web/views/busy.erb +0 -132
- data/web/views/dashboard.erb +0 -83
- data/web/views/dead.erb +0 -34
- data/web/views/layout.erb +0 -42
- data/web/views/morgue.erb +0 -78
- data/web/views/queue.erb +0 -55
- data/web/views/queues.erb +0 -38
- data/web/views/retries.erb +0 -83
- data/web/views/retry.erb +0 -34
- data/web/views/scheduled.erb +0 -57
data/lib/sidekiq/launcher.rb
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "sidekiq/manager"
|
|
4
|
-
require "sidekiq/
|
|
4
|
+
require "sidekiq/capsule"
|
|
5
5
|
require "sidekiq/scheduled"
|
|
6
|
+
require "sidekiq/ring_buffer"
|
|
6
7
|
|
|
7
8
|
module Sidekiq
|
|
8
|
-
# The Launcher starts the
|
|
9
|
+
# The Launcher starts the Capsule Managers, the Poller thread and provides the process heartbeat.
|
|
9
10
|
class Launcher
|
|
10
|
-
include
|
|
11
|
+
include Sidekiq::Component
|
|
11
12
|
|
|
12
13
|
STATS_TTL = 5 * 365 * 24 * 60 * 60 # 5 years
|
|
13
14
|
|
|
@@ -15,141 +16,153 @@ module Sidekiq
|
|
|
15
16
|
proc { "sidekiq" },
|
|
16
17
|
proc { Sidekiq::VERSION },
|
|
17
18
|
proc { |me, data| data["tag"] },
|
|
18
|
-
proc { |me, data| "[#{Processor::
|
|
19
|
+
proc { |me, data| "[#{Processor::WORK_STATE.size} of #{me.config.total_concurrency} busy]" },
|
|
19
20
|
proc { |me, data| "stopping" if me.stopping? }
|
|
20
21
|
]
|
|
21
22
|
|
|
22
|
-
attr_accessor :
|
|
23
|
+
attr_accessor :managers, :poller
|
|
23
24
|
|
|
24
|
-
def initialize(
|
|
25
|
-
|
|
26
|
-
@
|
|
27
|
-
@
|
|
25
|
+
def initialize(config, embedded: false)
|
|
26
|
+
@config = config
|
|
27
|
+
@embedded = embedded
|
|
28
|
+
@managers = config.capsules.values.map do |cap|
|
|
29
|
+
Sidekiq::Manager.new(cap)
|
|
30
|
+
end
|
|
31
|
+
@poller = Sidekiq::Scheduled::Poller.new(@config)
|
|
28
32
|
@done = false
|
|
29
|
-
@options = options
|
|
30
33
|
end
|
|
31
34
|
|
|
32
|
-
|
|
33
|
-
|
|
35
|
+
# Start this Sidekiq instance. If an embedding process already
|
|
36
|
+
# has a heartbeat thread, caller can use `async_beat: false`
|
|
37
|
+
# and instead have thread call Launcher#heartbeat every N seconds.
|
|
38
|
+
def run(async_beat: true)
|
|
39
|
+
logger.debug { @config.merge!({}) }
|
|
40
|
+
Sidekiq.freeze!
|
|
41
|
+
@thread = safe_thread("heartbeat", &method(:start_heartbeat)) if async_beat
|
|
34
42
|
@poller.start
|
|
35
|
-
@
|
|
43
|
+
@managers.each(&:start)
|
|
36
44
|
end
|
|
37
45
|
|
|
38
46
|
# Stops this instance from processing any more jobs,
|
|
39
|
-
#
|
|
40
47
|
def quiet
|
|
48
|
+
return if @done
|
|
49
|
+
|
|
41
50
|
@done = true
|
|
42
|
-
@
|
|
51
|
+
@managers.each(&:quiet)
|
|
43
52
|
@poller.terminate
|
|
53
|
+
fire_event(:quiet, reverse: true)
|
|
44
54
|
end
|
|
45
55
|
|
|
46
|
-
# Shuts down
|
|
47
|
-
# return until all work is complete and cleaned up.
|
|
48
|
-
# It can take up to the timeout to complete.
|
|
56
|
+
# Shuts down this Sidekiq instance. Waits up to the deadline for all jobs to complete.
|
|
49
57
|
def stop
|
|
50
|
-
deadline = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC) + @
|
|
51
|
-
|
|
52
|
-
@done = true
|
|
53
|
-
@manager.quiet
|
|
54
|
-
@poller.terminate
|
|
58
|
+
deadline = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC) + @config[:timeout]
|
|
55
59
|
|
|
56
|
-
|
|
60
|
+
quiet
|
|
61
|
+
stoppers = @managers.map do |mgr|
|
|
62
|
+
Thread.new do
|
|
63
|
+
mgr.stop(deadline)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
57
66
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
strategy = @options[:fetch]
|
|
61
|
-
strategy.bulk_requeue([], @options)
|
|
67
|
+
fire_event(:shutdown, reverse: true)
|
|
68
|
+
stoppers.each(&:join)
|
|
62
69
|
|
|
63
70
|
clear_heartbeat
|
|
71
|
+
fire_event(:exit, reverse: true)
|
|
64
72
|
end
|
|
65
73
|
|
|
66
74
|
def stopping?
|
|
67
75
|
@done
|
|
68
76
|
end
|
|
69
77
|
|
|
70
|
-
|
|
78
|
+
# If embedding Sidekiq, you can have the process heartbeat
|
|
79
|
+
# call this method to regularly heartbeat rather than creating
|
|
80
|
+
# a separate thread.
|
|
81
|
+
def heartbeat
|
|
82
|
+
❤
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
private
|
|
86
|
+
|
|
87
|
+
BEAT_PAUSE = 10
|
|
71
88
|
|
|
72
89
|
def start_heartbeat
|
|
73
90
|
loop do
|
|
74
|
-
|
|
75
|
-
sleep
|
|
91
|
+
beat
|
|
92
|
+
sleep BEAT_PAUSE
|
|
76
93
|
end
|
|
77
|
-
|
|
94
|
+
logger.info("Heartbeat stopping...")
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def beat
|
|
98
|
+
$0 = PROCTITLES.map { |proc| proc.call(self, to_data) }.compact.join(" ") unless @embedded
|
|
99
|
+
❤
|
|
78
100
|
end
|
|
79
101
|
|
|
80
102
|
def clear_heartbeat
|
|
103
|
+
flush_stats
|
|
104
|
+
|
|
81
105
|
# Remove record from Redis since we are shutting down.
|
|
82
106
|
# Note we don't stop the heartbeat thread; if the process
|
|
83
107
|
# doesn't actually exit, it'll reappear in the Web UI.
|
|
84
|
-
|
|
85
|
-
conn.pipelined do
|
|
86
|
-
|
|
87
|
-
|
|
108
|
+
redis do |conn|
|
|
109
|
+
conn.pipelined do |pipeline|
|
|
110
|
+
pipeline.srem("processes", [identity])
|
|
111
|
+
pipeline.unlink("#{identity}:work")
|
|
88
112
|
end
|
|
89
113
|
end
|
|
90
114
|
rescue
|
|
91
115
|
# best effort, ignore network errors
|
|
92
116
|
end
|
|
93
117
|
|
|
94
|
-
def
|
|
95
|
-
$0 = PROCTITLES.map { |proc| proc.call(self, to_data) }.compact.join(" ")
|
|
96
|
-
|
|
97
|
-
❤
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
def self.flush_stats
|
|
118
|
+
def flush_stats
|
|
101
119
|
fails = Processor::FAILURE.reset
|
|
102
120
|
procd = Processor::PROCESSED.reset
|
|
103
121
|
return if fails + procd == 0
|
|
104
122
|
|
|
105
123
|
nowdate = Time.now.utc.strftime("%Y-%m-%d")
|
|
106
124
|
begin
|
|
107
|
-
|
|
108
|
-
conn.pipelined do
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
125
|
+
redis do |conn|
|
|
126
|
+
conn.pipelined do |pipeline|
|
|
127
|
+
pipeline.incrby("stat:processed", procd)
|
|
128
|
+
pipeline.incrby("stat:processed:#{nowdate}", procd)
|
|
129
|
+
pipeline.expire("stat:processed:#{nowdate}", STATS_TTL)
|
|
130
|
+
|
|
131
|
+
pipeline.incrby("stat:failed", fails)
|
|
132
|
+
pipeline.incrby("stat:failed:#{nowdate}", fails)
|
|
133
|
+
pipeline.expire("stat:failed:#{nowdate}", STATS_TTL)
|
|
116
134
|
end
|
|
117
135
|
end
|
|
118
136
|
rescue => ex
|
|
119
|
-
|
|
120
|
-
# try to handle the exception
|
|
121
|
-
Sidekiq.logger.warn("Unable to flush stats: #{ex}")
|
|
137
|
+
logger.warn("Unable to flush stats: #{ex}")
|
|
122
138
|
end
|
|
123
139
|
end
|
|
124
|
-
at_exit(&method(:flush_stats))
|
|
125
140
|
|
|
126
141
|
def ❤
|
|
127
142
|
key = identity
|
|
128
143
|
fails = procd = 0
|
|
129
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
|
+
|
|
130
151
|
begin
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
curstate = Processor::
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
conn.multi do
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
conn.incrby("stat:failed", fails)
|
|
145
|
-
conn.incrby("stat:failed:#{nowdate}", fails)
|
|
146
|
-
conn.expire("stat:failed:#{nowdate}", STATS_TTL)
|
|
147
|
-
|
|
148
|
-
conn.unlink(workers_key)
|
|
149
|
-
curstate.each_pair do |tid, hash|
|
|
150
|
-
conn.hset(workers_key, tid, Sidekiq.dump_json(hash))
|
|
152
|
+
flush_stats
|
|
153
|
+
|
|
154
|
+
curstate = Processor::WORK_STATE.dup
|
|
155
|
+
curstate.transform_values! { |val| Sidekiq.dump_json(val) }
|
|
156
|
+
|
|
157
|
+
redis do |conn|
|
|
158
|
+
# work is the current set of executing jobs
|
|
159
|
+
work_key = "#{key}:work"
|
|
160
|
+
conn.multi do |transaction|
|
|
161
|
+
transaction.unlink(work_key)
|
|
162
|
+
if curstate.size > 0
|
|
163
|
+
transaction.hset(work_key, curstate)
|
|
164
|
+
transaction.expire(work_key, 60)
|
|
151
165
|
end
|
|
152
|
-
conn.expire(workers_key, 60)
|
|
153
166
|
end
|
|
154
167
|
end
|
|
155
168
|
|
|
@@ -158,27 +171,27 @@ module Sidekiq
|
|
|
158
171
|
fails = procd = 0
|
|
159
172
|
kb = memory_usage(::Process.pid)
|
|
160
173
|
|
|
161
|
-
_, exists, _, _,
|
|
162
|
-
conn.multi {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
174
|
+
_, exists, _, _, signal = redis { |conn|
|
|
175
|
+
conn.multi { |transaction|
|
|
176
|
+
transaction.sadd("processes", [key])
|
|
177
|
+
transaction.exists(key)
|
|
178
|
+
transaction.hset(key, "info", to_json,
|
|
179
|
+
"concurrency", @config.total_concurrency,
|
|
166
180
|
"busy", curstate.size,
|
|
167
181
|
"beat", Time.now.to_f,
|
|
168
182
|
"rtt_us", rtt,
|
|
169
|
-
"quiet", @done,
|
|
183
|
+
"quiet", @done.to_s,
|
|
170
184
|
"rss", kb)
|
|
171
|
-
|
|
172
|
-
|
|
185
|
+
transaction.expire(key, 60)
|
|
186
|
+
transaction.rpop("#{key}-signals")
|
|
173
187
|
}
|
|
174
188
|
}
|
|
175
189
|
|
|
176
190
|
# first heartbeat or recovering from an outage and need to reestablish our heartbeat
|
|
177
|
-
fire_event(:heartbeat) unless exists
|
|
178
|
-
|
|
179
|
-
return unless msg
|
|
191
|
+
fire_event(:heartbeat) unless exists > 0
|
|
192
|
+
fire_event(:beat, oneshot: false)
|
|
180
193
|
|
|
181
|
-
::Process.kill(
|
|
194
|
+
::Process.kill(signal, ::Process.pid) if signal && !@embedded
|
|
182
195
|
rescue => e
|
|
183
196
|
# ignore all redis/network issues
|
|
184
197
|
logger.error("heartbeat: #{e}")
|
|
@@ -196,7 +209,7 @@ module Sidekiq
|
|
|
196
209
|
|
|
197
210
|
def check_rtt
|
|
198
211
|
a = b = 0
|
|
199
|
-
|
|
212
|
+
redis do |x|
|
|
200
213
|
a = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :microsecond)
|
|
201
214
|
x.ping
|
|
202
215
|
b = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :microsecond)
|
|
@@ -207,10 +220,12 @@ module Sidekiq
|
|
|
207
220
|
# Workable is < 10,000µs
|
|
208
221
|
# Log a warning if it's a disaster.
|
|
209
222
|
if RTT_READINGS.all? { |x| x > RTT_WARNING_LEVEL }
|
|
210
|
-
|
|
211
|
-
Your Redis network connection
|
|
223
|
+
logger.warn <<~EOM
|
|
224
|
+
Your Redis network connection appears to be performing poorly.
|
|
212
225
|
Last RTT readings were #{RTT_READINGS.buffer.inspect}, ideally these should be < 1000.
|
|
213
|
-
Ensure Redis is running in the same AZ or datacenter as Sidekiq
|
|
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
|
|
214
229
|
EOM
|
|
215
230
|
RTT_READINGS.reset
|
|
216
231
|
end
|
|
@@ -242,11 +257,20 @@ module Sidekiq
|
|
|
242
257
|
"hostname" => hostname,
|
|
243
258
|
"started_at" => Time.now.to_f,
|
|
244
259
|
"pid" => ::Process.pid,
|
|
245
|
-
"tag" => @
|
|
246
|
-
"
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
260
|
+
"tag" => @config[:tag] || "",
|
|
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
|
|
267
|
+
"queues" => @config.capsules.values.flat_map { |cap| cap.queues }.uniq,
|
|
268
|
+
"weights" => @config.capsules.values.map(&:weights),
|
|
269
|
+
#####
|
|
270
|
+
"labels" => @config[:labels].to_a,
|
|
271
|
+
"identity" => identity,
|
|
272
|
+
"version" => Sidekiq::VERSION,
|
|
273
|
+
"embedded" => @embedded
|
|
250
274
|
}
|
|
251
275
|
end
|
|
252
276
|
|
|
@@ -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
|
@@ -16,133 +16,54 @@ module Sidekiq
|
|
|
16
16
|
def self.current
|
|
17
17
|
Thread.current[:sidekiq_context] ||= {}
|
|
18
18
|
end
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
module LoggingUtils
|
|
22
|
-
LEVELS = {
|
|
23
|
-
"debug" => 0,
|
|
24
|
-
"info" => 1,
|
|
25
|
-
"warn" => 2,
|
|
26
|
-
"error" => 3,
|
|
27
|
-
"fatal" => 4
|
|
28
|
-
}
|
|
29
|
-
LEVELS.default_proc = proc do |_, level|
|
|
30
|
-
Sidekiq.logger.warn("Invalid log level: #{level.inspect}")
|
|
31
|
-
nil
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def debug?
|
|
35
|
-
level <= 0
|
|
36
|
-
end
|
|
37
19
|
|
|
38
|
-
def
|
|
39
|
-
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
def warn?
|
|
43
|
-
level <= 2
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def error?
|
|
47
|
-
level <= 3
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
def fatal?
|
|
51
|
-
level <= 4
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
def local_level
|
|
55
|
-
Thread.current[:sidekiq_log_level]
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
def local_level=(level)
|
|
59
|
-
case level
|
|
60
|
-
when Integer
|
|
61
|
-
Thread.current[:sidekiq_log_level] = level
|
|
62
|
-
when Symbol, String
|
|
63
|
-
Thread.current[:sidekiq_log_level] = LEVELS[level.to_s]
|
|
64
|
-
when nil
|
|
65
|
-
Thread.current[:sidekiq_log_level] = nil
|
|
66
|
-
else
|
|
67
|
-
raise ArgumentError, "Invalid log level: #{level.inspect}"
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
def level
|
|
72
|
-
local_level || super
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
# Change the thread-local level for the duration of the given block.
|
|
76
|
-
def log_at(level)
|
|
77
|
-
old_local_level = local_level
|
|
78
|
-
self.local_level = level
|
|
79
|
-
yield
|
|
80
|
-
ensure
|
|
81
|
-
self.local_level = old_local_level
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
# Redefined to check severity against #level, and thus the thread-local level, rather than +@level+.
|
|
85
|
-
# FIXME: Remove when the minimum Ruby version supports overriding Logger#level.
|
|
86
|
-
def add(severity, message = nil, progname = nil, &block)
|
|
87
|
-
severity ||= ::Logger::UNKNOWN
|
|
88
|
-
progname ||= @progname
|
|
89
|
-
|
|
90
|
-
return true if @logdev.nil? || severity < level
|
|
91
|
-
|
|
92
|
-
if message.nil?
|
|
93
|
-
if block
|
|
94
|
-
message = yield
|
|
95
|
-
else
|
|
96
|
-
message = progname
|
|
97
|
-
progname = @progname
|
|
98
|
-
end
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
@logdev.write format_message(format_severity(severity), Time.now, progname, message)
|
|
20
|
+
def self.add(k, v)
|
|
21
|
+
current[k] = v
|
|
102
22
|
end
|
|
103
23
|
end
|
|
104
24
|
|
|
105
25
|
class Logger < ::Logger
|
|
106
|
-
include LoggingUtils
|
|
107
|
-
|
|
108
|
-
def initialize(*args, **kwargs)
|
|
109
|
-
super
|
|
110
|
-
self.formatter = Sidekiq.log_formatter
|
|
111
|
-
end
|
|
112
|
-
|
|
113
26
|
module Formatters
|
|
114
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
|
+
|
|
115
36
|
def tid
|
|
116
37
|
Thread.current["sidekiq_tid"] ||= (Thread.current.object_id ^ ::Process.pid).to_s(36)
|
|
117
38
|
end
|
|
118
39
|
|
|
119
|
-
def
|
|
120
|
-
|
|
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(" ")}"
|
|
121
49
|
end
|
|
50
|
+
end
|
|
122
51
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
case v
|
|
127
|
-
when Array
|
|
128
|
-
"#{k}=#{v.join(",")}"
|
|
129
|
-
else
|
|
130
|
-
"#{k}=#{v}"
|
|
131
|
-
end
|
|
132
|
-
}.join(" ")
|
|
133
|
-
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"
|
|
134
55
|
end
|
|
135
56
|
end
|
|
136
57
|
|
|
137
|
-
class
|
|
58
|
+
class Plain < Base
|
|
138
59
|
def call(severity, time, program_name, message)
|
|
139
|
-
"#{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"
|
|
140
61
|
end
|
|
141
62
|
end
|
|
142
63
|
|
|
143
64
|
class WithoutTimestamp < Pretty
|
|
144
65
|
def call(severity, time, program_name, message)
|
|
145
|
-
"pid=#{::Process.pid} tid=#{tid}#{format_context}
|
|
66
|
+
"#{COLORS[severity]} pid=#{::Process.pid} tid=#{tid}#{format_context}: #{message}\n"
|
|
146
67
|
end
|
|
147
68
|
end
|
|
148
69
|
|
|
@@ -155,7 +76,7 @@ module Sidekiq
|
|
|
155
76
|
lvl: severity,
|
|
156
77
|
msg: message
|
|
157
78
|
}
|
|
158
|
-
c =
|
|
79
|
+
c = Sidekiq::Context.current
|
|
159
80
|
hash["ctx"] = c unless c.empty?
|
|
160
81
|
|
|
161
82
|
Sidekiq.dump_json(hash) << "\n"
|