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.
Files changed (150) hide show
  1. checksums.yaml +4 -4
  2. data/Changes.md +619 -8
  3. data/LICENSE.txt +9 -0
  4. data/README.md +47 -50
  5. data/bin/sidekiq +22 -3
  6. data/bin/sidekiqload +213 -115
  7. data/bin/sidekiqmon +11 -0
  8. data/lib/generators/sidekiq/job_generator.rb +57 -0
  9. data/lib/generators/sidekiq/templates/{worker.rb.erb → job.rb.erb} +2 -2
  10. data/lib/generators/sidekiq/templates/{worker_spec.rb.erb → job_spec.rb.erb} +1 -1
  11. data/lib/generators/sidekiq/templates/{worker_test.rb.erb → job_test.rb.erb} +1 -1
  12. data/lib/sidekiq/api.rb +558 -353
  13. data/lib/sidekiq/capsule.rb +127 -0
  14. data/lib/sidekiq/cli.rb +204 -226
  15. data/lib/sidekiq/client.rb +127 -102
  16. data/lib/sidekiq/component.rb +68 -0
  17. data/lib/sidekiq/config.rb +287 -0
  18. data/lib/sidekiq/deploy.rb +62 -0
  19. data/lib/sidekiq/embedded.rb +61 -0
  20. data/lib/sidekiq/fetch.rb +49 -42
  21. data/lib/sidekiq/job.rb +374 -0
  22. data/lib/sidekiq/job_logger.rb +33 -7
  23. data/lib/sidekiq/job_retry.rb +147 -108
  24. data/lib/sidekiq/job_util.rb +107 -0
  25. data/lib/sidekiq/launcher.rb +203 -105
  26. data/lib/sidekiq/logger.rb +131 -0
  27. data/lib/sidekiq/manager.rb +43 -46
  28. data/lib/sidekiq/metrics/query.rb +155 -0
  29. data/lib/sidekiq/metrics/shared.rb +95 -0
  30. data/lib/sidekiq/metrics/tracking.rb +136 -0
  31. data/lib/sidekiq/middleware/chain.rb +113 -56
  32. data/lib/sidekiq/middleware/current_attributes.rb +95 -0
  33. data/lib/sidekiq/middleware/i18n.rb +7 -7
  34. data/lib/sidekiq/middleware/modules.rb +21 -0
  35. data/lib/sidekiq/monitor.rb +146 -0
  36. data/lib/sidekiq/paginator.rb +28 -16
  37. data/lib/sidekiq/processor.rb +122 -120
  38. data/lib/sidekiq/rails.rb +48 -38
  39. data/lib/sidekiq/redis_client_adapter.rb +111 -0
  40. data/lib/sidekiq/redis_connection.rb +39 -107
  41. data/lib/sidekiq/ring_buffer.rb +29 -0
  42. data/lib/sidekiq/scheduled.rb +111 -49
  43. data/lib/sidekiq/sd_notify.rb +149 -0
  44. data/lib/sidekiq/systemd.rb +24 -0
  45. data/lib/sidekiq/testing/inline.rb +6 -5
  46. data/lib/sidekiq/testing.rb +90 -89
  47. data/lib/sidekiq/transaction_aware_client.rb +44 -0
  48. data/lib/sidekiq/version.rb +3 -1
  49. data/lib/sidekiq/web/action.rb +15 -11
  50. data/lib/sidekiq/web/application.rb +190 -80
  51. data/lib/sidekiq/web/csrf_protection.rb +180 -0
  52. data/lib/sidekiq/web/helpers.rb +154 -115
  53. data/lib/sidekiq/web/router.rb +23 -19
  54. data/lib/sidekiq/web.rb +68 -107
  55. data/lib/sidekiq/worker_compatibility_alias.rb +13 -0
  56. data/lib/sidekiq.rb +92 -182
  57. data/sidekiq.gemspec +25 -16
  58. data/web/assets/images/apple-touch-icon.png +0 -0
  59. data/web/assets/javascripts/application.js +146 -61
  60. data/web/assets/javascripts/base-charts.js +106 -0
  61. data/web/assets/javascripts/chart.min.js +13 -0
  62. data/web/assets/javascripts/chartjs-plugin-annotation.min.js +7 -0
  63. data/web/assets/javascripts/dashboard-charts.js +182 -0
  64. data/web/assets/javascripts/dashboard.js +35 -293
  65. data/web/assets/javascripts/metrics.js +298 -0
  66. data/web/assets/stylesheets/application-dark.css +147 -0
  67. data/web/assets/stylesheets/application-rtl.css +2 -95
  68. data/web/assets/stylesheets/application.css +111 -522
  69. data/web/locales/ar.yml +71 -65
  70. data/web/locales/cs.yml +62 -62
  71. data/web/locales/da.yml +60 -53
  72. data/web/locales/de.yml +65 -53
  73. data/web/locales/el.yml +43 -24
  74. data/web/locales/en.yml +86 -66
  75. data/web/locales/es.yml +70 -54
  76. data/web/locales/fa.yml +65 -65
  77. data/web/locales/fr.yml +83 -62
  78. data/web/locales/gd.yml +99 -0
  79. data/web/locales/he.yml +65 -64
  80. data/web/locales/hi.yml +59 -59
  81. data/web/locales/it.yml +53 -53
  82. data/web/locales/ja.yml +75 -64
  83. data/web/locales/ko.yml +52 -52
  84. data/web/locales/lt.yml +83 -0
  85. data/web/locales/nb.yml +61 -61
  86. data/web/locales/nl.yml +52 -52
  87. data/web/locales/pl.yml +45 -45
  88. data/web/locales/pt-br.yml +83 -55
  89. data/web/locales/pt.yml +51 -51
  90. data/web/locales/ru.yml +68 -63
  91. data/web/locales/sv.yml +53 -53
  92. data/web/locales/ta.yml +60 -60
  93. data/web/locales/uk.yml +62 -61
  94. data/web/locales/ur.yml +64 -64
  95. data/web/locales/vi.yml +83 -0
  96. data/web/locales/zh-cn.yml +43 -16
  97. data/web/locales/zh-tw.yml +42 -8
  98. data/web/views/_footer.erb +6 -3
  99. data/web/views/_job_info.erb +21 -4
  100. data/web/views/_metrics_period_select.erb +12 -0
  101. data/web/views/_nav.erb +1 -1
  102. data/web/views/_paging.erb +2 -0
  103. data/web/views/_poll_link.erb +3 -6
  104. data/web/views/_summary.erb +7 -7
  105. data/web/views/busy.erb +77 -27
  106. data/web/views/dashboard.erb +48 -18
  107. data/web/views/dead.erb +3 -3
  108. data/web/views/filtering.erb +7 -0
  109. data/web/views/layout.erb +3 -1
  110. data/web/views/metrics.erb +91 -0
  111. data/web/views/metrics_for_job.erb +59 -0
  112. data/web/views/morgue.erb +14 -15
  113. data/web/views/queue.erb +33 -24
  114. data/web/views/queues.erb +19 -5
  115. data/web/views/retries.erb +16 -17
  116. data/web/views/retry.erb +3 -3
  117. data/web/views/scheduled.erb +17 -15
  118. metadata +70 -70
  119. data/.circleci/config.yml +0 -61
  120. data/.github/contributing.md +0 -32
  121. data/.github/issue_template.md +0 -11
  122. data/.gitignore +0 -15
  123. data/.travis.yml +0 -11
  124. data/3.0-Upgrade.md +0 -70
  125. data/4.0-Upgrade.md +0 -53
  126. data/5.0-Upgrade.md +0 -56
  127. data/COMM-LICENSE +0 -97
  128. data/Ent-Changes.md +0 -238
  129. data/Gemfile +0 -23
  130. data/LICENSE +0 -9
  131. data/Pro-2.0-Upgrade.md +0 -138
  132. data/Pro-3.0-Upgrade.md +0 -44
  133. data/Pro-4.0-Upgrade.md +0 -35
  134. data/Pro-Changes.md +0 -759
  135. data/Rakefile +0 -9
  136. data/bin/sidekiqctl +0 -20
  137. data/code_of_conduct.md +0 -50
  138. data/lib/generators/sidekiq/worker_generator.rb +0 -49
  139. data/lib/sidekiq/core_ext.rb +0 -1
  140. data/lib/sidekiq/ctl.rb +0 -221
  141. data/lib/sidekiq/delay.rb +0 -42
  142. data/lib/sidekiq/exception_handler.rb +0 -29
  143. data/lib/sidekiq/extensions/action_mailer.rb +0 -57
  144. data/lib/sidekiq/extensions/active_record.rb +0 -40
  145. data/lib/sidekiq/extensions/class_methods.rb +0 -40
  146. data/lib/sidekiq/extensions/generic_proxy.rb +0 -31
  147. data/lib/sidekiq/logging.rb +0 -122
  148. data/lib/sidekiq/middleware/server/active_record.rb +0 -23
  149. data/lib/sidekiq/util.rb +0 -66
  150. data/lib/sidekiq/worker.rb +0 -220
@@ -1,9 +1,8 @@
1
1
  # frozen_string_literal: true
2
- require 'sidekiq/util'
3
- require 'sidekiq/fetch'
4
- require 'sidekiq/job_logger'
5
- require 'sidekiq/job_retry'
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 Worker
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 (processor_stopped)
19
- # or due to an error during job execution (processor_died)
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(mgr)
33
- @mgr = mgr
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
- @strategy = (mgr.options[:fetch] || Sidekiq::BasicFetch).new(mgr.options)
39
- @reloader = Sidekiq.options[:reloader]
40
- @logging = (mgr.options[:job_logger] || Sidekiq::JobLogger).new
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 if !@thread
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 if !@thread
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
- begin
70
- while !@done
71
- process_one
72
- end
73
- @mgr.processor_stopped(self)
74
- rescue Sidekiq::Shutdown
75
- @mgr.processor_stopped(self)
76
- rescue Exception => ex
77
- @mgr.processor_died(self, ex)
78
- end
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
- begin
89
- work = @strategy.retrieve_work
90
- (logger.info { "Redis is online, #{::Process.clock_gettime(::Process::CLOCK_MONOTONIC) - @down} sec downtime" }; @down = nil) if @down
91
- work
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
- if !@down
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 here so we report the original
120
+ # we need to clone it to report the original
121
121
  # job structure to the Web UI
122
- pristine = cloned(job_hash)
123
-
124
- Sidekiq::Logging.with_job_hash_context(job_hash) do
125
- @retrier.global(pristine, queue) do
126
- @logging.call(job_hash, queue) do
127
- stats(pristine, queue) do
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 = constantize(job_hash['class'])
134
- worker = klass.new
135
- worker.jid = job_hash['jid']
136
- @retrier.local(worker, pristine, queue) do
137
- yield worker
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
- def process(work)
147
- jobstr = work.job
148
- queue = work.queue_name
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, { :context => "Invalid JSON for job", :jobstr => jobstr })
156
- # we can't notify because the job isn't a valid hash payload.
157
- DeadSet.new.kill(jobstr, notify_failure: false)
158
- return work.acknowledge
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 = true
162
- begin
163
- dispatch(job_hash, queue) do |worker|
164
- Sidekiq.server_middleware.invoke(worker, job_hash, queue) do
165
- execute_job(worker, cloned(job_hash['args']))
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
- work.acknowledge if ack
203
+ if ack
204
+ uow.acknowledge
205
+ end
188
206
  end
189
207
  end
190
208
 
191
- def execute_job(worker, cloned_args)
192
- worker.perform(*cloned_args)
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 = @value + amount }
222
+ def incr(amount = 1)
223
+ @lock.synchronize { @value += amount }
206
224
  end
207
225
 
208
226
  def reset
209
- @lock.synchronize { val = @value; @value = 0; val }
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 SharedWorkerState
236
+ class SharedWorkState
215
237
  def initialize
216
- @worker_state = {}
238
+ @work_state = {}
217
239
  @lock = Mutex.new
218
240
  end
219
241
 
220
242
  def set(tid, hash)
221
- @lock.synchronize { @worker_state[tid] = hash }
243
+ @lock.synchronize { @work_state[tid] = hash }
222
244
  end
223
245
 
224
246
  def delete(tid)
225
- @lock.synchronize { @worker_state.delete(tid) }
247
+ @lock.synchronize { @work_state.delete(tid) }
226
248
  end
227
249
 
228
250
  def dup
229
- @lock.synchronize { @worker_state.dup }
251
+ @lock.synchronize { @work_state.dup }
230
252
  end
231
253
 
232
254
  def size
233
- @lock.synchronize { @worker_state.size }
255
+ @lock.synchronize { @work_state.size }
234
256
  end
235
257
 
236
258
  def clear
237
- @lock.synchronize { @worker_state.clear }
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
- WORKER_STATE = SharedWorkerState.new
265
+ WORK_STATE = SharedWorkState.new
244
266
 
245
- def stats(job_hash, queue)
246
- tid = Sidekiq::Logging.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
- WORKER_STATE.delete(tid)
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
- @app.reloader.wrap do
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
- if defined?(::Rails) && ::Rails::VERSION::MAJOR < 4
55
- $stderr.puts("**************************************************")
56
- $stderr.puts("⛔️ WARNING: Sidekiq server is no longer supported by Rails 3.2 - please ensure your server/workers are updated")
57
- $stderr.puts("**************************************************")
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