sidekiq 5.1.1 → 7.1.2
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.
- checksums.yaml +5 -5
- data/Changes.md +627 -8
- data/LICENSE.txt +9 -0
- data/README.md +47 -50
- data/bin/sidekiq +22 -3
- data/bin/sidekiqload +213 -115
- data/bin/sidekiqmon +11 -0
- data/lib/generators/sidekiq/job_generator.rb +57 -0
- data/lib/generators/sidekiq/templates/{worker.rb.erb → job.rb.erb} +2 -2
- 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 +566 -329
- data/lib/sidekiq/capsule.rb +127 -0
- data/lib/sidekiq/cli.rb +241 -256
- data/lib/sidekiq/client.rb +125 -102
- data/lib/sidekiq/component.rb +68 -0
- data/lib/sidekiq/config.rb +278 -0
- data/lib/sidekiq/deploy.rb +62 -0
- data/lib/sidekiq/embedded.rb +61 -0
- data/lib/sidekiq/fetch.rb +49 -42
- data/lib/sidekiq/job.rb +374 -0
- data/lib/sidekiq/job_logger.rb +36 -9
- data/lib/sidekiq/job_retry.rb +147 -98
- data/lib/sidekiq/job_util.rb +105 -0
- data/lib/sidekiq/launcher.rb +207 -103
- data/lib/sidekiq/logger.rb +131 -0
- data/lib/sidekiq/manager.rb +43 -47
- data/lib/sidekiq/metrics/query.rb +153 -0
- data/lib/sidekiq/metrics/shared.rb +95 -0
- data/lib/sidekiq/metrics/tracking.rb +136 -0
- data/lib/sidekiq/middleware/chain.rb +113 -56
- data/lib/sidekiq/middleware/current_attributes.rb +95 -0
- data/lib/sidekiq/middleware/i18n.rb +7 -7
- data/lib/sidekiq/middleware/modules.rb +21 -0
- data/lib/sidekiq/monitor.rb +146 -0
- data/lib/sidekiq/paginator.rb +28 -16
- data/lib/sidekiq/processor.rb +159 -107
- data/lib/sidekiq/rails.rb +54 -43
- data/lib/sidekiq/redis_client_adapter.rb +96 -0
- data/lib/sidekiq/redis_connection.rb +39 -81
- data/lib/sidekiq/ring_buffer.rb +29 -0
- data/lib/sidekiq/scheduled.rb +139 -48
- data/lib/sidekiq/sd_notify.rb +149 -0
- data/lib/sidekiq/systemd.rb +24 -0
- data/lib/sidekiq/testing/inline.rb +6 -5
- data/lib/sidekiq/testing.rb +70 -88
- data/lib/sidekiq/transaction_aware_client.rb +44 -0
- data/lib/sidekiq/version.rb +3 -1
- data/lib/sidekiq/web/action.rb +15 -11
- data/lib/sidekiq/web/application.rb +143 -77
- data/lib/sidekiq/web/csrf_protection.rb +180 -0
- data/lib/sidekiq/web/helpers.rb +144 -106
- data/lib/sidekiq/web/router.rb +23 -19
- data/lib/sidekiq/web.rb +60 -111
- data/lib/sidekiq/worker_compatibility_alias.rb +13 -0
- data/lib/sidekiq.rb +94 -183
- data/sidekiq.gemspec +25 -23
- data/web/assets/images/apple-touch-icon.png +0 -0
- data/web/assets/javascripts/application.js +130 -61
- data/web/assets/javascripts/base-charts.js +106 -0
- data/web/assets/javascripts/chart.min.js +13 -0
- data/web/assets/javascripts/chartjs-plugin-annotation.min.js +7 -0
- data/web/assets/javascripts/dashboard-charts.js +166 -0
- data/web/assets/javascripts/dashboard.js +36 -282
- data/web/assets/javascripts/metrics.js +264 -0
- data/web/assets/stylesheets/application-dark.css +147 -0
- data/web/assets/stylesheets/application-rtl.css +2 -95
- data/web/assets/stylesheets/application.css +134 -521
- data/web/assets/stylesheets/bootstrap.css +2 -2
- data/web/locales/ar.yml +71 -64
- data/web/locales/cs.yml +62 -62
- data/web/locales/da.yml +60 -53
- data/web/locales/de.yml +65 -53
- data/web/locales/el.yml +43 -24
- data/web/locales/en.yml +84 -65
- data/web/locales/es.yml +70 -54
- data/web/locales/fa.yml +65 -65
- data/web/locales/fr.yml +83 -62
- data/web/locales/gd.yml +99 -0
- data/web/locales/he.yml +65 -64
- data/web/locales/hi.yml +59 -59
- data/web/locales/it.yml +53 -53
- data/web/locales/ja.yml +75 -64
- data/web/locales/ko.yml +52 -52
- data/web/locales/lt.yml +83 -0
- data/web/locales/nb.yml +61 -61
- data/web/locales/nl.yml +52 -52
- data/web/locales/pl.yml +45 -45
- data/web/locales/pt-br.yml +63 -55
- data/web/locales/pt.yml +51 -51
- data/web/locales/ru.yml +68 -63
- data/web/locales/sv.yml +53 -53
- data/web/locales/ta.yml +60 -60
- data/web/locales/uk.yml +62 -61
- data/web/locales/ur.yml +64 -64
- data/web/locales/vi.yml +83 -0
- data/web/locales/zh-cn.yml +43 -16
- data/web/locales/zh-tw.yml +42 -8
- data/web/views/_footer.erb +8 -2
- data/web/views/_job_info.erb +21 -4
- data/web/views/_metrics_period_select.erb +12 -0
- data/web/views/_nav.erb +4 -18
- data/web/views/_paging.erb +2 -0
- data/web/views/_poll_link.erb +3 -6
- data/web/views/_summary.erb +7 -7
- data/web/views/busy.erb +75 -25
- data/web/views/dashboard.erb +58 -18
- data/web/views/dead.erb +3 -3
- data/web/views/layout.erb +4 -2
- data/web/views/metrics.erb +82 -0
- data/web/views/metrics_for_job.erb +68 -0
- data/web/views/morgue.erb +14 -15
- data/web/views/queue.erb +33 -23
- data/web/views/queues.erb +14 -4
- data/web/views/retries.erb +19 -16
- data/web/views/retry.erb +3 -3
- data/web/views/scheduled.erb +17 -15
- metadata +71 -140
- data/.github/contributing.md +0 -32
- data/.github/issue_template.md +0 -11
- data/.gitignore +0 -13
- data/.travis.yml +0 -14
- data/3.0-Upgrade.md +0 -70
- data/4.0-Upgrade.md +0 -53
- data/5.0-Upgrade.md +0 -56
- data/COMM-LICENSE +0 -95
- data/Ent-Changes.md +0 -210
- data/Gemfile +0 -8
- data/LICENSE +0 -9
- data/Pro-2.0-Upgrade.md +0 -138
- data/Pro-3.0-Upgrade.md +0 -44
- data/Pro-4.0-Upgrade.md +0 -35
- data/Pro-Changes.md +0 -716
- data/Rakefile +0 -8
- data/bin/sidekiqctl +0 -99
- data/code_of_conduct.md +0 -50
- data/lib/generators/sidekiq/worker_generator.rb +0 -49
- data/lib/sidekiq/core_ext.rb +0 -1
- data/lib/sidekiq/delay.rb +0 -41
- data/lib/sidekiq/exception_handler.rb +0 -29
- data/lib/sidekiq/extensions/action_mailer.rb +0 -57
- data/lib/sidekiq/extensions/active_record.rb +0 -40
- data/lib/sidekiq/extensions/class_methods.rb +0 -40
- data/lib/sidekiq/extensions/generic_proxy.rb +0 -31
- data/lib/sidekiq/logging.rb +0 -122
- data/lib/sidekiq/middleware/server/active_record.rb +0 -22
- data/lib/sidekiq/middleware/server/active_record_cache.rb +0 -11
- data/lib/sidekiq/util.rb +0 -66
- data/lib/sidekiq/worker.rb +0 -204
data/lib/sidekiq/processor.rb
CHANGED
@@ -1,11 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require 'thread'
|
7
|
-
require 'concurrent/map'
|
8
|
-
require 'concurrent/atomic/atomic_fixnum'
|
2
|
+
|
3
|
+
require "sidekiq/fetch"
|
4
|
+
require "sidekiq/job_logger"
|
5
|
+
require "sidekiq/job_retry"
|
9
6
|
|
10
7
|
module Sidekiq
|
11
8
|
##
|
@@ -13,45 +10,45 @@ module Sidekiq
|
|
13
10
|
#
|
14
11
|
# 1. fetches a job from Redis
|
15
12
|
# 2. executes the job
|
16
|
-
# a. instantiate the
|
13
|
+
# a. instantiate the job class
|
17
14
|
# b. run the middleware chain
|
18
15
|
# c. call #perform
|
19
16
|
#
|
20
|
-
# A Processor can exit due to shutdown
|
21
|
-
#
|
17
|
+
# A Processor can exit due to shutdown or due to
|
18
|
+
# an error during job execution.
|
22
19
|
#
|
23
20
|
# If an error occurs in the job execution, the
|
24
21
|
# Processor calls the Manager to create a new one
|
25
22
|
# to replace itself and exits.
|
26
23
|
#
|
27
24
|
class Processor
|
28
|
-
|
29
|
-
include Util
|
25
|
+
include Sidekiq::Component
|
30
26
|
|
31
27
|
attr_reader :thread
|
32
28
|
attr_reader :job
|
29
|
+
attr_reader :capsule
|
33
30
|
|
34
|
-
def initialize(
|
35
|
-
@
|
31
|
+
def initialize(capsule, &block)
|
32
|
+
@config = @capsule = capsule
|
33
|
+
@callback = block
|
36
34
|
@down = false
|
37
35
|
@done = false
|
38
36
|
@job = nil
|
39
37
|
@thread = nil
|
40
|
-
@
|
41
|
-
@
|
42
|
-
@
|
43
|
-
@retrier = Sidekiq::JobRetry.new
|
38
|
+
@reloader = Sidekiq.default_configuration[:reloader]
|
39
|
+
@job_logger = (capsule.config[:job_logger] || Sidekiq::JobLogger).new(logger)
|
40
|
+
@retrier = Sidekiq::JobRetry.new(capsule)
|
44
41
|
end
|
45
42
|
|
46
|
-
def terminate(wait=false)
|
43
|
+
def terminate(wait = false)
|
47
44
|
@done = true
|
48
|
-
return
|
45
|
+
return unless @thread
|
49
46
|
@thread.value if wait
|
50
47
|
end
|
51
48
|
|
52
|
-
def kill(wait=false)
|
49
|
+
def kill(wait = false)
|
53
50
|
@done = true
|
54
|
-
return
|
51
|
+
return unless @thread
|
55
52
|
# unlike the other actors, terminate does not wait
|
56
53
|
# for the thread to finish because we don't know how
|
57
54
|
# long the job will take to finish. Instead we
|
@@ -62,39 +59,40 @@ module Sidekiq
|
|
62
59
|
end
|
63
60
|
|
64
61
|
def start
|
65
|
-
@thread ||= safe_thread("processor", &method(:run))
|
62
|
+
@thread ||= safe_thread("#{config.name}/processor", &method(:run))
|
66
63
|
end
|
67
64
|
|
68
65
|
private unless $TESTING
|
69
66
|
|
70
67
|
def run
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
68
|
+
# By setting this thread-local, Sidekiq.redis will access +Sidekiq::Capsule#redis_pool+
|
69
|
+
# instead of the global pool in +Sidekiq::Config#redis_pool+.
|
70
|
+
Thread.current[:sidekiq_capsule] = @capsule
|
71
|
+
|
72
|
+
process_one until @done
|
73
|
+
@callback.call(self)
|
74
|
+
rescue Sidekiq::Shutdown
|
75
|
+
@callback.call(self)
|
76
|
+
rescue Exception => ex
|
77
|
+
@callback.call(self, ex)
|
81
78
|
end
|
82
79
|
|
83
|
-
def process_one
|
80
|
+
def process_one(&block)
|
84
81
|
@job = fetch
|
85
82
|
process(@job) if @job
|
86
83
|
@job = nil
|
87
84
|
end
|
88
85
|
|
89
86
|
def get_one
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
rescue Sidekiq::Shutdown
|
95
|
-
rescue => ex
|
96
|
-
handle_fetch_exception(ex)
|
87
|
+
uow = capsule.fetcher.retrieve_work
|
88
|
+
if @down
|
89
|
+
logger.info { "Redis is online, #{::Process.clock_gettime(::Process::CLOCK_MONOTONIC) - @down} sec downtime" }
|
90
|
+
@down = nil
|
97
91
|
end
|
92
|
+
uow
|
93
|
+
rescue Sidekiq::Shutdown
|
94
|
+
rescue => ex
|
95
|
+
handle_fetch_exception(ex)
|
98
96
|
end
|
99
97
|
|
100
98
|
def fetch
|
@@ -108,8 +106,8 @@ module Sidekiq
|
|
108
106
|
end
|
109
107
|
|
110
108
|
def handle_fetch_exception(ex)
|
111
|
-
|
112
|
-
@down =
|
109
|
+
unless @down
|
110
|
+
@down = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
113
111
|
logger.error("Error fetching job: #{ex}")
|
114
112
|
handle_exception(ex)
|
115
113
|
end
|
@@ -117,26 +115,29 @@ module Sidekiq
|
|
117
115
|
nil
|
118
116
|
end
|
119
117
|
|
120
|
-
def dispatch(job_hash, queue)
|
118
|
+
def dispatch(job_hash, queue, jobstr)
|
121
119
|
# since middleware can mutate the job hash
|
122
|
-
# we clone
|
120
|
+
# we need to clone it to report the original
|
123
121
|
# job structure to the Web UI
|
124
|
-
|
122
|
+
# or to push back to redis when retrying.
|
123
|
+
# To avoid costly and, most of the time, useless cloning here,
|
124
|
+
# we pass original String of JSON to respected methods
|
125
|
+
# to re-parse it there if we need access to the original, untouched job
|
125
126
|
|
126
|
-
|
127
|
-
@retrier.global(
|
128
|
-
@
|
129
|
-
stats(
|
127
|
+
@job_logger.prepare(job_hash) do
|
128
|
+
@retrier.global(jobstr, queue) do
|
129
|
+
@job_logger.call(job_hash, queue) do
|
130
|
+
stats(jobstr, queue) do
|
130
131
|
# Rails 5 requires a Reloader to wrap code execution. In order to
|
131
132
|
# constantize the worker and instantiate an instance, we have to call
|
132
133
|
# the Reloader. It handles code loading, db connection management, etc.
|
133
134
|
# Effectively this block denotes a "unit of work" to Rails.
|
134
135
|
@reloader.call do
|
135
|
-
klass
|
136
|
-
|
137
|
-
|
138
|
-
@retrier.local(
|
139
|
-
yield
|
136
|
+
klass = Object.const_get(job_hash["class"])
|
137
|
+
inst = klass.new
|
138
|
+
inst.jid = job_hash["jid"]
|
139
|
+
@retrier.local(inst, jobstr, queue) do
|
140
|
+
yield inst
|
140
141
|
end
|
141
142
|
end
|
142
143
|
end
|
@@ -145,84 +146,135 @@ module Sidekiq
|
|
145
146
|
end
|
146
147
|
end
|
147
148
|
|
148
|
-
|
149
|
-
|
150
|
-
queue = work.queue_name
|
149
|
+
IGNORE_SHUTDOWN_INTERRUPTS = {Sidekiq::Shutdown => :never}
|
150
|
+
private_constant :IGNORE_SHUTDOWN_INTERRUPTS
|
151
151
|
|
152
|
-
|
152
|
+
def process(uow)
|
153
|
+
jobstr = uow.job
|
154
|
+
queue = uow.queue_name
|
155
|
+
|
156
|
+
# Treat malformed JSON as a special case: job goes straight to the morgue.
|
157
|
+
job_hash = nil
|
153
158
|
begin
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
159
|
+
job_hash = Sidekiq.load_json(jobstr)
|
160
|
+
rescue => ex
|
161
|
+
handle_exception(ex, {context: "Invalid JSON for job", jobstr: jobstr})
|
162
|
+
now = Time.now.to_f
|
163
|
+
redis do |conn|
|
164
|
+
conn.multi do |xa|
|
165
|
+
xa.zadd("dead", now.to_s, jobstr)
|
166
|
+
xa.zremrangebyscore("dead", "-inf", now - @capsule.config[:dead_timeout_in_seconds])
|
167
|
+
xa.zremrangebyrank("dead", 0, - @capsule.config[:dead_max_jobs])
|
168
|
+
end
|
164
169
|
end
|
170
|
+
return uow.acknowledge
|
171
|
+
end
|
165
172
|
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
173
|
+
ack = false
|
174
|
+
begin
|
175
|
+
dispatch(job_hash, queue, jobstr) do |inst|
|
176
|
+
config.server_middleware.invoke(inst, job_hash, queue) do
|
177
|
+
execute_job(inst, job_hash["args"])
|
170
178
|
end
|
171
179
|
end
|
180
|
+
ack = true
|
172
181
|
rescue Sidekiq::Shutdown
|
173
182
|
# Had to force kill this job because it didn't finish
|
174
183
|
# within the timeout. Don't acknowledge the work since
|
175
184
|
# we didn't properly finish it.
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
185
|
+
rescue Sidekiq::JobRetry::Handled => h
|
186
|
+
# this is the common case: job raised error and Sidekiq::JobRetry::Handled
|
187
|
+
# signals that we created a retry successfully. We can acknowlege the job.
|
188
|
+
ack = true
|
189
|
+
e = h.cause || h
|
190
|
+
handle_exception(e, {context: "Job raised exception", job: job_hash})
|
180
191
|
raise e
|
192
|
+
rescue Exception => ex
|
193
|
+
# Unexpected error! This is very bad and indicates an exception that got past
|
194
|
+
# the retry subsystem (e.g. network partition). We won't acknowledge the job
|
195
|
+
# so it can be rescued when using Sidekiq Pro.
|
196
|
+
handle_exception(ex, {context: "Internal exception!", job: job_hash, jobstr: jobstr})
|
197
|
+
raise ex
|
181
198
|
ensure
|
182
|
-
|
199
|
+
if ack
|
200
|
+
# We don't want a shutdown signal to interrupt job acknowledgment.
|
201
|
+
Thread.handle_interrupt(IGNORE_SHUTDOWN_INTERRUPTS) do
|
202
|
+
uow.acknowledge
|
203
|
+
end
|
204
|
+
end
|
183
205
|
end
|
184
206
|
end
|
185
207
|
|
186
|
-
def execute_job(
|
187
|
-
|
208
|
+
def execute_job(inst, cloned_args)
|
209
|
+
inst.perform(*cloned_args)
|
188
210
|
end
|
189
211
|
|
190
|
-
|
191
|
-
|
192
|
-
|
212
|
+
# Ruby doesn't provide atomic counters out of the box so we'll
|
213
|
+
# implement something simple ourselves.
|
214
|
+
# https://bugs.ruby-lang.org/issues/14706
|
215
|
+
class Counter
|
216
|
+
def initialize
|
217
|
+
@value = 0
|
218
|
+
@lock = Mutex.new
|
219
|
+
end
|
193
220
|
|
194
|
-
|
195
|
-
|
196
|
-
|
221
|
+
def incr(amount = 1)
|
222
|
+
@lock.synchronize { @value += amount }
|
223
|
+
end
|
197
224
|
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
WORKER_STATE.delete(tid)
|
205
|
-
PROCESSED.increment
|
225
|
+
def reset
|
226
|
+
@lock.synchronize {
|
227
|
+
val = @value
|
228
|
+
@value = 0
|
229
|
+
val
|
230
|
+
}
|
206
231
|
end
|
207
232
|
end
|
208
233
|
|
209
|
-
#
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
234
|
+
# jruby's Hash implementation is not threadsafe, so we wrap it in a mutex here
|
235
|
+
class SharedWorkState
|
236
|
+
def initialize
|
237
|
+
@work_state = {}
|
238
|
+
@lock = Mutex.new
|
239
|
+
end
|
240
|
+
|
241
|
+
def set(tid, hash)
|
242
|
+
@lock.synchronize { @work_state[tid] = hash }
|
243
|
+
end
|
215
244
|
|
216
|
-
|
217
|
-
|
218
|
-
|
245
|
+
def delete(tid)
|
246
|
+
@lock.synchronize { @work_state.delete(tid) }
|
247
|
+
end
|
219
248
|
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
249
|
+
def dup
|
250
|
+
@lock.synchronize { @work_state.dup }
|
251
|
+
end
|
252
|
+
|
253
|
+
def size
|
254
|
+
@lock.synchronize { @work_state.size }
|
255
|
+
end
|
256
|
+
|
257
|
+
def clear
|
258
|
+
@lock.synchronize { @work_state.clear }
|
224
259
|
end
|
225
260
|
end
|
226
261
|
|
262
|
+
PROCESSED = Counter.new
|
263
|
+
FAILURE = Counter.new
|
264
|
+
WORK_STATE = SharedWorkState.new
|
265
|
+
|
266
|
+
def stats(jobstr, queue)
|
267
|
+
WORK_STATE.set(tid, {queue: queue, payload: jobstr, run_at: Time.now.to_i})
|
268
|
+
|
269
|
+
begin
|
270
|
+
yield
|
271
|
+
rescue Exception
|
272
|
+
FAILURE.incr
|
273
|
+
raise
|
274
|
+
ensure
|
275
|
+
WORK_STATE.delete(tid)
|
276
|
+
PROCESSED.incr
|
277
|
+
end
|
278
|
+
end
|
227
279
|
end
|
228
280
|
end
|
data/lib/sidekiq/rails.rb
CHANGED
@@ -1,49 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
module Sidekiq
|
3
|
-
class Rails < ::Rails::Engine
|
4
|
-
# We need to setup this up before any application configuration which might
|
5
|
-
# change Sidekiq middleware.
|
6
|
-
#
|
7
|
-
# This hook happens after `Rails::Application` is inherited within
|
8
|
-
# config/application.rb and before config is touched, usually within the
|
9
|
-
# class block. Definitely before config/environments/*.rb and
|
10
|
-
# config/initializers/*.rb.
|
11
|
-
config.before_configuration do
|
12
|
-
if defined?(::ActiveRecord)
|
13
|
-
Sidekiq.server_middleware do |chain|
|
14
|
-
if ::Rails::VERSION::MAJOR < 5
|
15
|
-
require 'sidekiq/middleware/server/active_record'
|
16
|
-
chain.add Sidekiq::Middleware::Server::ActiveRecord
|
17
|
-
end
|
18
|
-
|
19
|
-
require 'sidekiq/middleware/server/active_record_cache'
|
20
|
-
chain.add Sidekiq::Middleware::Server::ActiveRecordCache
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
2
|
|
25
|
-
|
26
|
-
|
27
|
-
# from config/environment.rb back to sidekiq/cli.rb.
|
28
|
-
# We have to add the reloader after initialize to see if cache_classes has
|
29
|
-
# been turned on.
|
30
|
-
#
|
31
|
-
# None of this matters on the client-side, only within the Sidekiq process itself.
|
32
|
-
#
|
33
|
-
Sidekiq.configure_server do |_|
|
34
|
-
if ::Rails::VERSION::MAJOR >= 5
|
35
|
-
Sidekiq.options[:reloader] = Sidekiq::Rails::Reloader.new
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
3
|
+
require "sidekiq/job"
|
4
|
+
require "rails"
|
39
5
|
|
6
|
+
module Sidekiq
|
7
|
+
class Rails < ::Rails::Engine
|
40
8
|
class Reloader
|
41
9
|
def initialize(app = ::Rails.application)
|
42
10
|
@app = app
|
43
11
|
end
|
44
12
|
|
45
13
|
def call
|
46
|
-
|
14
|
+
params = (::Rails::VERSION::STRING >= "7.1") ? {source: "job.sidekiq"} : {}
|
15
|
+
@app.reloader.wrap(**params) do
|
47
16
|
yield
|
48
17
|
end
|
49
18
|
end
|
@@ -51,12 +20,54 @@ module Sidekiq
|
|
51
20
|
def inspect
|
52
21
|
"#<Sidekiq::Rails::Reloader @app=#{@app.class.name}>"
|
53
22
|
end
|
23
|
+
|
24
|
+
def to_json(*)
|
25
|
+
Sidekiq.dump_json(inspect)
|
26
|
+
end
|
54
27
|
end
|
55
|
-
end if defined?(::Rails)
|
56
|
-
end
|
57
28
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
29
|
+
# By including the Options module, we allow AJs to directly control sidekiq features
|
30
|
+
# via the *sidekiq_options* class method and, for instance, not use AJ's retry system.
|
31
|
+
# AJ retries don't show up in the Sidekiq UI Retries tab, don't save any error data, can't be
|
32
|
+
# manually retried, don't automatically die, etc.
|
33
|
+
#
|
34
|
+
# class SomeJob < ActiveJob::Base
|
35
|
+
# queue_as :default
|
36
|
+
# sidekiq_options retry: 3, backtrace: 10
|
37
|
+
# def perform
|
38
|
+
# end
|
39
|
+
# end
|
40
|
+
initializer "sidekiq.active_job_integration" do
|
41
|
+
ActiveSupport.on_load(:active_job) do
|
42
|
+
include ::Sidekiq::Job::Options unless respond_to?(:sidekiq_options)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
initializer "sidekiq.rails_logger" do
|
47
|
+
Sidekiq.configure_server do |config|
|
48
|
+
# This is the integration code necessary so that if a job uses `Rails.logger.info "Hello"`,
|
49
|
+
# it will appear in the Sidekiq console with all of the job context. See #5021 and
|
50
|
+
# https://github.com/rails/rails/blob/b5f2b550f69a99336482739000c58e4e04e033aa/railties/lib/rails/commands/server/server_command.rb#L82-L84
|
51
|
+
unless ::Rails.logger == config.logger || ::ActiveSupport::Logger.logger_outputs_to?(::Rails.logger, $stdout)
|
52
|
+
::Rails.logger.extend(::ActiveSupport::Logger.broadcast(config.logger))
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
initializer "sidekiq.backtrace_cleaner" do
|
58
|
+
Sidekiq.configure_server do |config|
|
59
|
+
config[:backtrace_cleaner] = ->(backtrace) { ::Rails.backtrace_cleaner.clean(backtrace) }
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# This hook happens after all initializers are run, just before returning
|
64
|
+
# from config/environment.rb back to sidekiq/cli.rb.
|
65
|
+
#
|
66
|
+
# None of this matters on the client-side, only within the Sidekiq process itself.
|
67
|
+
config.after_initialize do
|
68
|
+
Sidekiq.configure_server do |config|
|
69
|
+
config[:reloader] = Sidekiq::Rails::Reloader.new
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
62
73
|
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "set"
|
4
|
+
require "redis_client"
|
5
|
+
require "redis_client/decorator"
|
6
|
+
|
7
|
+
module Sidekiq
|
8
|
+
class RedisClientAdapter
|
9
|
+
BaseError = RedisClient::Error
|
10
|
+
CommandError = RedisClient::CommandError
|
11
|
+
|
12
|
+
# You can add/remove items or clear the whole thing if you don't want deprecation warnings.
|
13
|
+
DEPRECATED_COMMANDS = %i[rpoplpush zrangebyscore zrevrange zrevrangebyscore getset hmset setex setnx].to_set
|
14
|
+
|
15
|
+
module CompatMethods
|
16
|
+
def info
|
17
|
+
@client.call("INFO") { |i| i.lines(chomp: true).map { |l| l.split(":", 2) }.select { |l| l.size == 2 }.to_h }
|
18
|
+
end
|
19
|
+
|
20
|
+
def evalsha(sha, keys, argv)
|
21
|
+
@client.call("EVALSHA", sha, keys.size, *keys, *argv)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
# this allows us to use methods like `conn.hmset(...)` instead of having to use
|
27
|
+
# redis-client's native `conn.call("hmset", ...)`
|
28
|
+
def method_missing(*args, &block)
|
29
|
+
warn("[sidekiq#5788] Redis has deprecated the `#{args.first}`command, called at #{caller(1..1)}") if DEPRECATED_COMMANDS.include?(args.first)
|
30
|
+
@client.call(*args, *block)
|
31
|
+
end
|
32
|
+
ruby2_keywords :method_missing if respond_to?(:ruby2_keywords, true)
|
33
|
+
|
34
|
+
def respond_to_missing?(name, include_private = false)
|
35
|
+
super # Appease the linter. We can't tell what is a valid command.
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
CompatClient = RedisClient::Decorator.create(CompatMethods)
|
40
|
+
|
41
|
+
class CompatClient
|
42
|
+
def config
|
43
|
+
@client.config
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def initialize(options)
|
48
|
+
opts = client_opts(options)
|
49
|
+
@config = if opts.key?(:sentinels)
|
50
|
+
RedisClient.sentinel(**opts)
|
51
|
+
else
|
52
|
+
RedisClient.config(**opts)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def new_client
|
57
|
+
CompatClient.new(@config.new_client)
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def client_opts(options)
|
63
|
+
opts = options.dup
|
64
|
+
|
65
|
+
if opts[:namespace]
|
66
|
+
raise ArgumentError, "Your Redis configuration uses the namespace '#{opts[:namespace]}' but this feature isn't supported by redis-client. " \
|
67
|
+
"Either use the redis adapter or remove the namespace."
|
68
|
+
end
|
69
|
+
|
70
|
+
opts.delete(:size)
|
71
|
+
opts.delete(:pool_timeout)
|
72
|
+
|
73
|
+
if opts[:network_timeout]
|
74
|
+
opts[:timeout] = opts[:network_timeout]
|
75
|
+
opts.delete(:network_timeout)
|
76
|
+
end
|
77
|
+
|
78
|
+
if opts[:driver]
|
79
|
+
opts[:driver] = opts[:driver].to_sym
|
80
|
+
end
|
81
|
+
|
82
|
+
opts[:name] = opts.delete(:master_name) if opts.key?(:master_name)
|
83
|
+
opts[:role] = opts[:role].to_sym if opts.key?(:role)
|
84
|
+
opts.delete(:url) if opts.key?(:sentinels)
|
85
|
+
|
86
|
+
# Issue #3303, redis-rb will silently retry an operation.
|
87
|
+
# This can lead to duplicate jobs if Sidekiq::Client's LPUSH
|
88
|
+
# is performed twice but I believe this is much, much rarer
|
89
|
+
# than the reconnect silently fixing a problem; we keep it
|
90
|
+
# on by default.
|
91
|
+
opts[:reconnect_attempts] ||= 1
|
92
|
+
|
93
|
+
opts
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|