sidekiq 5.2.7 → 7.2.0
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 +619 -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 +558 -353
- data/lib/sidekiq/capsule.rb +127 -0
- data/lib/sidekiq/cli.rb +204 -226
- data/lib/sidekiq/client.rb +127 -102
- data/lib/sidekiq/component.rb +68 -0
- data/lib/sidekiq/config.rb +287 -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 +33 -7
- data/lib/sidekiq/job_retry.rb +147 -108
- data/lib/sidekiq/job_util.rb +107 -0
- data/lib/sidekiq/launcher.rb +203 -105
- data/lib/sidekiq/logger.rb +131 -0
- data/lib/sidekiq/manager.rb +43 -46
- data/lib/sidekiq/metrics/query.rb +155 -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 +122 -120
- data/lib/sidekiq/rails.rb +48 -38
- data/lib/sidekiq/redis_client_adapter.rb +111 -0
- data/lib/sidekiq/redis_connection.rb +39 -107
- data/lib/sidekiq/ring_buffer.rb +29 -0
- data/lib/sidekiq/scheduled.rb +111 -49
- 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 +90 -89
- 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 +190 -80
- data/lib/sidekiq/web/csrf_protection.rb +180 -0
- data/lib/sidekiq/web/helpers.rb +154 -115
- data/lib/sidekiq/web/router.rb +23 -19
- data/lib/sidekiq/web.rb +68 -107
- data/lib/sidekiq/worker_compatibility_alias.rb +13 -0
- data/lib/sidekiq.rb +92 -182
- data/sidekiq.gemspec +25 -16
- data/web/assets/images/apple-touch-icon.png +0 -0
- data/web/assets/javascripts/application.js +146 -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 +182 -0
- data/web/assets/javascripts/dashboard.js +35 -293
- data/web/assets/javascripts/metrics.js +298 -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 +111 -522
- data/web/locales/ar.yml +71 -65
- 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 +86 -66
- 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 +83 -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 +6 -3
- data/web/views/_job_info.erb +21 -4
- data/web/views/_metrics_period_select.erb +12 -0
- data/web/views/_nav.erb +1 -1
- 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 +77 -27
- data/web/views/dashboard.erb +48 -18
- data/web/views/dead.erb +3 -3
- data/web/views/filtering.erb +7 -0
- data/web/views/layout.erb +3 -1
- data/web/views/metrics.erb +91 -0
- data/web/views/metrics_for_job.erb +59 -0
- data/web/views/morgue.erb +14 -15
- data/web/views/queue.erb +33 -24
- data/web/views/queues.erb +19 -5
- data/web/views/retries.erb +16 -17
- data/web/views/retry.erb +3 -3
- data/web/views/scheduled.erb +17 -15
- metadata +70 -70
- data/.circleci/config.yml +0 -61
- data/.github/contributing.md +0 -32
- data/.github/issue_template.md +0 -11
- data/.gitignore +0 -15
- data/.travis.yml +0 -11
- 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 -97
- data/Ent-Changes.md +0 -238
- data/Gemfile +0 -23
- 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 -759
- data/Rakefile +0 -9
- data/bin/sidekiqctl +0 -20
- 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/ctl.rb +0 -221
- data/lib/sidekiq/delay.rb +0 -42
- 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 -23
- data/lib/sidekiq/util.rb +0 -66
- data/lib/sidekiq/worker.rb +0 -220
data/lib/sidekiq/processor.rb
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require
|
|
4
|
-
require
|
|
5
|
-
require
|
|
6
|
-
require 'thread'
|
|
2
|
+
|
|
3
|
+
require "sidekiq/fetch"
|
|
4
|
+
require "sidekiq/job_logger"
|
|
5
|
+
require "sidekiq/job_retry"
|
|
7
6
|
|
|
8
7
|
module Sidekiq
|
|
9
8
|
##
|
|
@@ -11,45 +10,45 @@ module Sidekiq
|
|
|
11
10
|
#
|
|
12
11
|
# 1. fetches a job from Redis
|
|
13
12
|
# 2. executes the job
|
|
14
|
-
# a. instantiate the
|
|
13
|
+
# a. instantiate the job class
|
|
15
14
|
# b. run the middleware chain
|
|
16
15
|
# c. call #perform
|
|
17
16
|
#
|
|
18
|
-
# A Processor can exit due to shutdown
|
|
19
|
-
#
|
|
17
|
+
# A Processor can exit due to shutdown or due to
|
|
18
|
+
# an error during job execution.
|
|
20
19
|
#
|
|
21
20
|
# If an error occurs in the job execution, the
|
|
22
21
|
# Processor calls the Manager to create a new one
|
|
23
22
|
# to replace itself and exits.
|
|
24
23
|
#
|
|
25
24
|
class Processor
|
|
26
|
-
|
|
27
|
-
include Util
|
|
25
|
+
include Sidekiq::Component
|
|
28
26
|
|
|
29
27
|
attr_reader :thread
|
|
30
28
|
attr_reader :job
|
|
29
|
+
attr_reader :capsule
|
|
31
30
|
|
|
32
|
-
def initialize(
|
|
33
|
-
@
|
|
31
|
+
def initialize(capsule, &block)
|
|
32
|
+
@config = @capsule = capsule
|
|
33
|
+
@callback = block
|
|
34
34
|
@down = false
|
|
35
35
|
@done = false
|
|
36
36
|
@job = nil
|
|
37
37
|
@thread = nil
|
|
38
|
-
@
|
|
39
|
-
@
|
|
40
|
-
@
|
|
41
|
-
@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)
|
|
42
41
|
end
|
|
43
42
|
|
|
44
|
-
def terminate(wait=false)
|
|
43
|
+
def terminate(wait = false)
|
|
45
44
|
@done = true
|
|
46
|
-
return
|
|
45
|
+
return unless @thread
|
|
47
46
|
@thread.value if wait
|
|
48
47
|
end
|
|
49
48
|
|
|
50
|
-
def kill(wait=false)
|
|
49
|
+
def kill(wait = false)
|
|
51
50
|
@done = true
|
|
52
|
-
return
|
|
51
|
+
return unless @thread
|
|
53
52
|
# unlike the other actors, terminate does not wait
|
|
54
53
|
# for the thread to finish because we don't know how
|
|
55
54
|
# long the job will take to finish. Instead we
|
|
@@ -60,39 +59,40 @@ module Sidekiq
|
|
|
60
59
|
end
|
|
61
60
|
|
|
62
61
|
def start
|
|
63
|
-
@thread ||= safe_thread("processor", &method(:run))
|
|
62
|
+
@thread ||= safe_thread("#{config.name}/processor", &method(:run))
|
|
64
63
|
end
|
|
65
64
|
|
|
66
65
|
private unless $TESTING
|
|
67
66
|
|
|
68
67
|
def run
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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)
|
|
79
78
|
end
|
|
80
79
|
|
|
81
|
-
def process_one
|
|
80
|
+
def process_one(&block)
|
|
82
81
|
@job = fetch
|
|
83
82
|
process(@job) if @job
|
|
84
83
|
@job = nil
|
|
85
84
|
end
|
|
86
85
|
|
|
87
86
|
def get_one
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
rescue Sidekiq::Shutdown
|
|
93
|
-
rescue => ex
|
|
94
|
-
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
|
|
95
91
|
end
|
|
92
|
+
uow
|
|
93
|
+
rescue Sidekiq::Shutdown
|
|
94
|
+
rescue => ex
|
|
95
|
+
handle_fetch_exception(ex)
|
|
96
96
|
end
|
|
97
97
|
|
|
98
98
|
def fetch
|
|
@@ -106,7 +106,7 @@ module Sidekiq
|
|
|
106
106
|
end
|
|
107
107
|
|
|
108
108
|
def handle_fetch_exception(ex)
|
|
109
|
-
|
|
109
|
+
unless @down
|
|
110
110
|
@down = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
|
111
111
|
logger.error("Error fetching job: #{ex}")
|
|
112
112
|
handle_exception(ex)
|
|
@@ -115,26 +115,29 @@ module Sidekiq
|
|
|
115
115
|
nil
|
|
116
116
|
end
|
|
117
117
|
|
|
118
|
-
def dispatch(job_hash, queue)
|
|
118
|
+
def dispatch(job_hash, queue, jobstr)
|
|
119
119
|
# since middleware can mutate the job hash
|
|
120
|
-
# we clone
|
|
120
|
+
# we need to clone it to report the original
|
|
121
121
|
# job structure to the Web UI
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
|
126
|
+
|
|
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
|
|
128
131
|
# Rails 5 requires a Reloader to wrap code execution. In order to
|
|
129
132
|
# constantize the worker and instantiate an instance, we have to call
|
|
130
133
|
# the Reloader. It handles code loading, db connection management, etc.
|
|
131
134
|
# Effectively this block denotes a "unit of work" to Rails.
|
|
132
135
|
@reloader.call do
|
|
133
|
-
klass
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
@retrier.local(
|
|
137
|
-
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
|
|
138
141
|
end
|
|
139
142
|
end
|
|
140
143
|
end
|
|
@@ -143,53 +146,68 @@ module Sidekiq
|
|
|
143
146
|
end
|
|
144
147
|
end
|
|
145
148
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
+
IGNORE_SHUTDOWN_INTERRUPTS = {Sidekiq::Shutdown => :never}
|
|
150
|
+
private_constant :IGNORE_SHUTDOWN_INTERRUPTS
|
|
151
|
+
ALLOW_SHUTDOWN_INTERRUPTS = {Sidekiq::Shutdown => :immediate}
|
|
152
|
+
private_constant :ALLOW_SHUTDOWN_INTERRUPTS
|
|
153
|
+
|
|
154
|
+
def process(uow)
|
|
155
|
+
jobstr = uow.job
|
|
156
|
+
queue = uow.queue_name
|
|
149
157
|
|
|
150
158
|
# Treat malformed JSON as a special case: job goes straight to the morgue.
|
|
151
159
|
job_hash = nil
|
|
152
160
|
begin
|
|
153
161
|
job_hash = Sidekiq.load_json(jobstr)
|
|
154
162
|
rescue => ex
|
|
155
|
-
handle_exception(ex, {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
163
|
+
handle_exception(ex, {context: "Invalid JSON for job", jobstr: jobstr})
|
|
164
|
+
now = Time.now.to_f
|
|
165
|
+
redis do |conn|
|
|
166
|
+
conn.multi do |xa|
|
|
167
|
+
xa.zadd("dead", now.to_s, jobstr)
|
|
168
|
+
xa.zremrangebyscore("dead", "-inf", now - @capsule.config[:dead_timeout_in_seconds])
|
|
169
|
+
xa.zremrangebyrank("dead", 0, - @capsule.config[:dead_max_jobs])
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
return uow.acknowledge
|
|
159
173
|
end
|
|
160
174
|
|
|
161
|
-
ack =
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
175
|
+
ack = false
|
|
176
|
+
Thread.handle_interrupt(IGNORE_SHUTDOWN_INTERRUPTS) do
|
|
177
|
+
Thread.handle_interrupt(ALLOW_SHUTDOWN_INTERRUPTS) do
|
|
178
|
+
dispatch(job_hash, queue, jobstr) do |inst|
|
|
179
|
+
config.server_middleware.invoke(inst, job_hash, queue) do
|
|
180
|
+
execute_job(inst, job_hash["args"])
|
|
181
|
+
end
|
|
166
182
|
end
|
|
183
|
+
ack = true
|
|
184
|
+
rescue Sidekiq::Shutdown
|
|
185
|
+
# Had to force kill this job because it didn't finish
|
|
186
|
+
# within the timeout. Don't acknowledge the work since
|
|
187
|
+
# we didn't properly finish it.
|
|
188
|
+
rescue Sidekiq::JobRetry::Handled => h
|
|
189
|
+
# this is the common case: job raised error and Sidekiq::JobRetry::Handled
|
|
190
|
+
# signals that we created a retry successfully. We can acknowlege the job.
|
|
191
|
+
ack = true
|
|
192
|
+
e = h.cause || h
|
|
193
|
+
handle_exception(e, {context: "Job raised exception", job: job_hash})
|
|
194
|
+
raise e
|
|
195
|
+
rescue Exception => ex
|
|
196
|
+
# Unexpected error! This is very bad and indicates an exception that got past
|
|
197
|
+
# the retry subsystem (e.g. network partition). We won't acknowledge the job
|
|
198
|
+
# so it can be rescued when using Sidekiq Pro.
|
|
199
|
+
handle_exception(ex, {context: "Internal exception!", job: job_hash, jobstr: jobstr})
|
|
200
|
+
raise ex
|
|
167
201
|
end
|
|
168
|
-
rescue Sidekiq::Shutdown
|
|
169
|
-
# Had to force kill this job because it didn't finish
|
|
170
|
-
# within the timeout. Don't acknowledge the work since
|
|
171
|
-
# we didn't properly finish it.
|
|
172
|
-
ack = false
|
|
173
|
-
rescue Sidekiq::JobRetry::Handled => h
|
|
174
|
-
# this is the common case: job raised error and Sidekiq::JobRetry::Handled
|
|
175
|
-
# signals that we created a retry successfully. We can acknowlege the job.
|
|
176
|
-
e = h.cause ? h.cause : h
|
|
177
|
-
handle_exception(e, { :context => "Job raised exception", :job => job_hash, :jobstr => jobstr })
|
|
178
|
-
raise e
|
|
179
|
-
rescue Exception => ex
|
|
180
|
-
# Unexpected error! This is very bad and indicates an exception that got past
|
|
181
|
-
# the retry subsystem (e.g. network partition). We won't acknowledge the job
|
|
182
|
-
# so it can be rescued when using Sidekiq Pro.
|
|
183
|
-
ack = false
|
|
184
|
-
handle_exception(ex, { :context => "Internal exception!", :job => job_hash, :jobstr => jobstr })
|
|
185
|
-
raise e
|
|
186
202
|
ensure
|
|
187
|
-
|
|
203
|
+
if ack
|
|
204
|
+
uow.acknowledge
|
|
205
|
+
end
|
|
188
206
|
end
|
|
189
207
|
end
|
|
190
208
|
|
|
191
|
-
def execute_job(
|
|
192
|
-
|
|
209
|
+
def execute_job(inst, cloned_args)
|
|
210
|
+
inst.perform(*cloned_args)
|
|
193
211
|
end
|
|
194
212
|
|
|
195
213
|
# Ruby doesn't provide atomic counters out of the box so we'll
|
|
@@ -201,50 +219,53 @@ module Sidekiq
|
|
|
201
219
|
@lock = Mutex.new
|
|
202
220
|
end
|
|
203
221
|
|
|
204
|
-
def incr(amount=1)
|
|
205
|
-
@lock.synchronize { @value
|
|
222
|
+
def incr(amount = 1)
|
|
223
|
+
@lock.synchronize { @value += amount }
|
|
206
224
|
end
|
|
207
225
|
|
|
208
226
|
def reset
|
|
209
|
-
@lock.synchronize {
|
|
227
|
+
@lock.synchronize {
|
|
228
|
+
val = @value
|
|
229
|
+
@value = 0
|
|
230
|
+
val
|
|
231
|
+
}
|
|
210
232
|
end
|
|
211
233
|
end
|
|
212
234
|
|
|
213
235
|
# jruby's Hash implementation is not threadsafe, so we wrap it in a mutex here
|
|
214
|
-
class
|
|
236
|
+
class SharedWorkState
|
|
215
237
|
def initialize
|
|
216
|
-
@
|
|
238
|
+
@work_state = {}
|
|
217
239
|
@lock = Mutex.new
|
|
218
240
|
end
|
|
219
241
|
|
|
220
242
|
def set(tid, hash)
|
|
221
|
-
@lock.synchronize { @
|
|
243
|
+
@lock.synchronize { @work_state[tid] = hash }
|
|
222
244
|
end
|
|
223
245
|
|
|
224
246
|
def delete(tid)
|
|
225
|
-
@lock.synchronize { @
|
|
247
|
+
@lock.synchronize { @work_state.delete(tid) }
|
|
226
248
|
end
|
|
227
249
|
|
|
228
250
|
def dup
|
|
229
|
-
@lock.synchronize { @
|
|
251
|
+
@lock.synchronize { @work_state.dup }
|
|
230
252
|
end
|
|
231
253
|
|
|
232
254
|
def size
|
|
233
|
-
@lock.synchronize { @
|
|
255
|
+
@lock.synchronize { @work_state.size }
|
|
234
256
|
end
|
|
235
257
|
|
|
236
258
|
def clear
|
|
237
|
-
@lock.synchronize { @
|
|
259
|
+
@lock.synchronize { @work_state.clear }
|
|
238
260
|
end
|
|
239
261
|
end
|
|
240
262
|
|
|
241
263
|
PROCESSED = Counter.new
|
|
242
264
|
FAILURE = Counter.new
|
|
243
|
-
|
|
265
|
+
WORK_STATE = SharedWorkState.new
|
|
244
266
|
|
|
245
|
-
def stats(
|
|
246
|
-
tid
|
|
247
|
-
WORKER_STATE.set(tid, {:queue => queue, :payload => job_hash, :run_at => Time.now.to_i })
|
|
267
|
+
def stats(jobstr, queue)
|
|
268
|
+
WORK_STATE.set(tid, {queue: queue, payload: jobstr, run_at: Time.now.to_i})
|
|
248
269
|
|
|
249
270
|
begin
|
|
250
271
|
yield
|
|
@@ -252,28 +273,9 @@ module Sidekiq
|
|
|
252
273
|
FAILURE.incr
|
|
253
274
|
raise
|
|
254
275
|
ensure
|
|
255
|
-
|
|
276
|
+
WORK_STATE.delete(tid)
|
|
256
277
|
PROCESSED.incr
|
|
257
278
|
end
|
|
258
279
|
end
|
|
259
|
-
|
|
260
|
-
# Deep clone the arguments passed to the worker so that if
|
|
261
|
-
# the job fails, what is pushed back onto Redis hasn't
|
|
262
|
-
# been mutated by the worker.
|
|
263
|
-
def cloned(thing)
|
|
264
|
-
Marshal.load(Marshal.dump(thing))
|
|
265
|
-
end
|
|
266
|
-
|
|
267
|
-
def constantize(str)
|
|
268
|
-
names = str.split('::')
|
|
269
|
-
names.shift if names.empty? || names.first.empty?
|
|
270
|
-
|
|
271
|
-
names.inject(Object) do |constant, name|
|
|
272
|
-
# the false flag limits search for name to under the constant namespace
|
|
273
|
-
# which mimics Rails' behaviour
|
|
274
|
-
constant.const_defined?(name, false) ? constant.const_get(name, false) : constant.const_missing(name)
|
|
275
|
-
end
|
|
276
|
-
end
|
|
277
|
-
|
|
278
280
|
end
|
|
279
281
|
end
|
data/lib/sidekiq/rails.rb
CHANGED
|
@@ -1,45 +1,18 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "sidekiq/job"
|
|
4
|
+
require "rails"
|
|
5
|
+
|
|
3
6
|
module Sidekiq
|
|
4
7
|
class Rails < ::Rails::Engine
|
|
5
|
-
# We need to setup this up before any application configuration which might
|
|
6
|
-
# change Sidekiq middleware.
|
|
7
|
-
#
|
|
8
|
-
# This hook happens after `Rails::Application` is inherited within
|
|
9
|
-
# config/application.rb and before config is touched, usually within the
|
|
10
|
-
# class block. Definitely before config/environments/*.rb and
|
|
11
|
-
# config/initializers/*.rb.
|
|
12
|
-
config.before_configuration do
|
|
13
|
-
if ::Rails::VERSION::MAJOR < 5 && defined?(::ActiveRecord)
|
|
14
|
-
Sidekiq.server_middleware do |chain|
|
|
15
|
-
require 'sidekiq/middleware/server/active_record'
|
|
16
|
-
chain.add Sidekiq::Middleware::Server::ActiveRecord
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
config.after_initialize do
|
|
22
|
-
# This hook happens after all initializers are run, just before returning
|
|
23
|
-
# from config/environment.rb back to sidekiq/cli.rb.
|
|
24
|
-
# We have to add the reloader after initialize to see if cache_classes has
|
|
25
|
-
# been turned on.
|
|
26
|
-
#
|
|
27
|
-
# None of this matters on the client-side, only within the Sidekiq process itself.
|
|
28
|
-
#
|
|
29
|
-
Sidekiq.configure_server do |_|
|
|
30
|
-
if ::Rails::VERSION::MAJOR >= 5
|
|
31
|
-
Sidekiq.options[:reloader] = Sidekiq::Rails::Reloader.new
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
|
|
36
8
|
class Reloader
|
|
37
9
|
def initialize(app = ::Rails.application)
|
|
38
10
|
@app = app
|
|
39
11
|
end
|
|
40
12
|
|
|
41
13
|
def call
|
|
42
|
-
|
|
14
|
+
params = (::Rails::VERSION::STRING >= "7.1") ? {source: "job.sidekiq"} : {}
|
|
15
|
+
@app.reloader.wrap(**params) do
|
|
43
16
|
yield
|
|
44
17
|
end
|
|
45
18
|
end
|
|
@@ -48,11 +21,48 @@ module Sidekiq
|
|
|
48
21
|
"#<Sidekiq::Rails::Reloader @app=#{@app.class.name}>"
|
|
49
22
|
end
|
|
50
23
|
end
|
|
51
|
-
end if defined?(::Rails)
|
|
52
|
-
end
|
|
53
24
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
25
|
+
# By including the Options module, we allow AJs to directly control sidekiq features
|
|
26
|
+
# via the *sidekiq_options* class method and, for instance, not use AJ's retry system.
|
|
27
|
+
# AJ retries don't show up in the Sidekiq UI Retries tab, don't save any error data, can't be
|
|
28
|
+
# manually retried, don't automatically die, etc.
|
|
29
|
+
#
|
|
30
|
+
# class SomeJob < ActiveJob::Base
|
|
31
|
+
# queue_as :default
|
|
32
|
+
# sidekiq_options retry: 3, backtrace: 10
|
|
33
|
+
# def perform
|
|
34
|
+
# end
|
|
35
|
+
# end
|
|
36
|
+
initializer "sidekiq.active_job_integration" do
|
|
37
|
+
ActiveSupport.on_load(:active_job) do
|
|
38
|
+
include ::Sidekiq::Job::Options unless respond_to?(:sidekiq_options)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
initializer "sidekiq.backtrace_cleaner" do
|
|
43
|
+
Sidekiq.configure_server do |config|
|
|
44
|
+
config[:backtrace_cleaner] = ->(backtrace) { ::Rails.backtrace_cleaner.clean(backtrace) }
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# This hook happens after all initializers are run, just before returning
|
|
49
|
+
# from config/environment.rb back to sidekiq/cli.rb.
|
|
50
|
+
#
|
|
51
|
+
# None of this matters on the client-side, only within the Sidekiq process itself.
|
|
52
|
+
config.after_initialize do
|
|
53
|
+
Sidekiq.configure_server do |config|
|
|
54
|
+
config[:reloader] = Sidekiq::Rails::Reloader.new
|
|
55
|
+
|
|
56
|
+
# This is the integration code necessary so that if a job uses `Rails.logger.info "Hello"`,
|
|
57
|
+
# it will appear in the Sidekiq console with all of the job context.
|
|
58
|
+
unless ::Rails.logger == config.logger || ::ActiveSupport::Logger.logger_outputs_to?(::Rails.logger, $stdout)
|
|
59
|
+
if ::Rails::VERSION::STRING < "7.1"
|
|
60
|
+
::Rails.logger.extend(::ActiveSupport::Logger.broadcast(config.logger))
|
|
61
|
+
else
|
|
62
|
+
::Rails.logger.broadcast_to(config.logger)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
58
68
|
end
|
|
@@ -0,0 +1,111 @@
|
|
|
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
|
+
# this is the set of Redis commands used by Sidekiq. Not guaranteed
|
|
25
|
+
# to be comprehensive, we use this as a performance enhancement to
|
|
26
|
+
# avoid calling method_missing on most commands
|
|
27
|
+
USED_COMMANDS = %w[bitfield bitfield_ro del exists expire flushdb
|
|
28
|
+
get hdel hget hgetall hincrby hlen hmget hset hsetnx incr incrby
|
|
29
|
+
lindex llen lmove lpop lpush lrange lrem mget mset ping pttl
|
|
30
|
+
publish rpop rpush sadd scard script set sismember smembers
|
|
31
|
+
srem ttl type unlink zadd zcard zincrby zrange zrem
|
|
32
|
+
zremrangebyrank zremrangebyscore]
|
|
33
|
+
|
|
34
|
+
USED_COMMANDS.each do |name|
|
|
35
|
+
define_method(name) do |*args|
|
|
36
|
+
@client.call(name, *args)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
# this allows us to use methods like `conn.hmset(...)` instead of having to use
|
|
43
|
+
# redis-client's native `conn.call("hmset", ...)`
|
|
44
|
+
def method_missing(*args, &block)
|
|
45
|
+
warn("[sidekiq#5788] Redis has deprecated the `#{args.first}`command, called at #{caller(1..1)}") if DEPRECATED_COMMANDS.include?(args.first)
|
|
46
|
+
@client.call(*args, *block)
|
|
47
|
+
end
|
|
48
|
+
ruby2_keywords :method_missing if respond_to?(:ruby2_keywords, true)
|
|
49
|
+
|
|
50
|
+
def respond_to_missing?(name, include_private = false)
|
|
51
|
+
super # Appease the linter. We can't tell what is a valid command.
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
CompatClient = RedisClient::Decorator.create(CompatMethods)
|
|
56
|
+
|
|
57
|
+
class CompatClient
|
|
58
|
+
def config
|
|
59
|
+
@client.config
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def initialize(options)
|
|
64
|
+
opts = client_opts(options)
|
|
65
|
+
@config = if opts.key?(:sentinels)
|
|
66
|
+
RedisClient.sentinel(**opts)
|
|
67
|
+
else
|
|
68
|
+
RedisClient.config(**opts)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def new_client
|
|
73
|
+
CompatClient.new(@config.new_client)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
private
|
|
77
|
+
|
|
78
|
+
def client_opts(options)
|
|
79
|
+
opts = options.dup
|
|
80
|
+
|
|
81
|
+
if opts[:namespace]
|
|
82
|
+
raise ArgumentError, "Your Redis configuration uses the namespace '#{opts[:namespace]}' but this feature is no longer supported in Sidekiq 7+. See https://github.com/sidekiq/sidekiq/blob/main/docs/7.0-Upgrade.md#redis-namespace."
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
opts.delete(:size)
|
|
86
|
+
opts.delete(:pool_timeout)
|
|
87
|
+
|
|
88
|
+
if opts[:network_timeout]
|
|
89
|
+
opts[:timeout] = opts[:network_timeout]
|
|
90
|
+
opts.delete(:network_timeout)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
if opts[:driver]
|
|
94
|
+
opts[:driver] = opts[:driver].to_sym
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
opts[:name] = opts.delete(:master_name) if opts.key?(:master_name)
|
|
98
|
+
opts[:role] = opts[:role].to_sym if opts.key?(:role)
|
|
99
|
+
opts.delete(:url) if opts.key?(:sentinels)
|
|
100
|
+
|
|
101
|
+
# Issue #3303, redis-rb will silently retry an operation.
|
|
102
|
+
# This can lead to duplicate jobs if Sidekiq::Client's LPUSH
|
|
103
|
+
# is performed twice but I believe this is much, much rarer
|
|
104
|
+
# than the reconnect silently fixing a problem; we keep it
|
|
105
|
+
# on by default.
|
|
106
|
+
opts[:reconnect_attempts] ||= 1
|
|
107
|
+
|
|
108
|
+
opts
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|