sidekiq 6.4.1 → 7.0.0

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.

Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. data/Changes.md +107 -5
  3. data/README.md +14 -13
  4. data/bin/sidekiq +3 -8
  5. data/bin/sidekiqload +26 -29
  6. data/lib/sidekiq/api.rb +232 -157
  7. data/lib/sidekiq/capsule.rb +110 -0
  8. data/lib/sidekiq/cli.rb +80 -86
  9. data/lib/sidekiq/client.rb +54 -42
  10. data/lib/sidekiq/component.rb +66 -0
  11. data/lib/sidekiq/config.rb +271 -0
  12. data/lib/sidekiq/deploy.rb +62 -0
  13. data/lib/sidekiq/embedded.rb +61 -0
  14. data/lib/sidekiq/fetch.rb +20 -19
  15. data/lib/sidekiq/job.rb +375 -10
  16. data/lib/sidekiq/job_logger.rb +1 -1
  17. data/lib/sidekiq/job_retry.rb +74 -53
  18. data/lib/sidekiq/job_util.rb +17 -11
  19. data/lib/sidekiq/launcher.rb +63 -69
  20. data/lib/sidekiq/logger.rb +6 -45
  21. data/lib/sidekiq/manager.rb +33 -32
  22. data/lib/sidekiq/metrics/query.rb +153 -0
  23. data/lib/sidekiq/metrics/shared.rb +95 -0
  24. data/lib/sidekiq/metrics/tracking.rb +134 -0
  25. data/lib/sidekiq/middleware/chain.rb +84 -42
  26. data/lib/sidekiq/middleware/current_attributes.rb +18 -17
  27. data/lib/sidekiq/middleware/i18n.rb +6 -4
  28. data/lib/sidekiq/middleware/modules.rb +21 -0
  29. data/lib/sidekiq/monitor.rb +1 -1
  30. data/lib/sidekiq/paginator.rb +10 -2
  31. data/lib/sidekiq/processor.rb +56 -59
  32. data/lib/sidekiq/rails.rb +10 -9
  33. data/lib/sidekiq/redis_client_adapter.rb +118 -0
  34. data/lib/sidekiq/redis_connection.rb +13 -82
  35. data/lib/sidekiq/ring_buffer.rb +29 -0
  36. data/lib/sidekiq/scheduled.rb +65 -37
  37. data/lib/sidekiq/testing/inline.rb +4 -4
  38. data/lib/sidekiq/testing.rb +41 -68
  39. data/lib/sidekiq/transaction_aware_client.rb +44 -0
  40. data/lib/sidekiq/version.rb +2 -1
  41. data/lib/sidekiq/web/action.rb +3 -3
  42. data/lib/sidekiq/web/application.rb +22 -6
  43. data/lib/sidekiq/web/csrf_protection.rb +3 -3
  44. data/lib/sidekiq/web/helpers.rb +21 -19
  45. data/lib/sidekiq/web.rb +3 -14
  46. data/lib/sidekiq/worker_compatibility_alias.rb +13 -0
  47. data/lib/sidekiq.rb +84 -207
  48. data/sidekiq.gemspec +29 -5
  49. data/web/assets/javascripts/application.js +58 -26
  50. data/web/assets/javascripts/base-charts.js +106 -0
  51. data/web/assets/javascripts/chart.min.js +13 -0
  52. data/web/assets/javascripts/chartjs-plugin-annotation.min.js +7 -0
  53. data/web/assets/javascripts/dashboard-charts.js +166 -0
  54. data/web/assets/javascripts/dashboard.js +3 -240
  55. data/web/assets/javascripts/metrics.js +236 -0
  56. data/web/assets/stylesheets/application-rtl.css +2 -91
  57. data/web/assets/stylesheets/application.css +64 -297
  58. data/web/locales/ar.yml +70 -70
  59. data/web/locales/cs.yml +62 -62
  60. data/web/locales/da.yml +52 -52
  61. data/web/locales/de.yml +65 -65
  62. data/web/locales/el.yml +43 -24
  63. data/web/locales/en.yml +82 -69
  64. data/web/locales/es.yml +68 -68
  65. data/web/locales/fa.yml +65 -65
  66. data/web/locales/fr.yml +67 -67
  67. data/web/locales/he.yml +65 -64
  68. data/web/locales/hi.yml +59 -59
  69. data/web/locales/it.yml +53 -53
  70. data/web/locales/ja.yml +71 -68
  71. data/web/locales/ko.yml +52 -52
  72. data/web/locales/lt.yml +66 -66
  73. data/web/locales/nb.yml +61 -61
  74. data/web/locales/nl.yml +52 -52
  75. data/web/locales/pl.yml +45 -45
  76. data/web/locales/pt-br.yml +63 -55
  77. data/web/locales/pt.yml +51 -51
  78. data/web/locales/ru.yml +67 -66
  79. data/web/locales/sv.yml +53 -53
  80. data/web/locales/ta.yml +60 -60
  81. data/web/locales/uk.yml +62 -61
  82. data/web/locales/ur.yml +64 -64
  83. data/web/locales/vi.yml +67 -67
  84. data/web/locales/zh-cn.yml +37 -11
  85. data/web/locales/zh-tw.yml +42 -8
  86. data/web/views/_footer.erb +5 -2
  87. data/web/views/_nav.erb +1 -1
  88. data/web/views/_summary.erb +1 -1
  89. data/web/views/busy.erb +9 -4
  90. data/web/views/dashboard.erb +36 -4
  91. data/web/views/metrics.erb +80 -0
  92. data/web/views/metrics_for_job.erb +69 -0
  93. data/web/views/queue.erb +5 -1
  94. metadata +69 -22
  95. data/lib/sidekiq/delay.rb +0 -43
  96. data/lib/sidekiq/exception_handler.rb +0 -27
  97. data/lib/sidekiq/extensions/action_mailer.rb +0 -48
  98. data/lib/sidekiq/extensions/active_record.rb +0 -43
  99. data/lib/sidekiq/extensions/class_methods.rb +0 -43
  100. data/lib/sidekiq/extensions/generic_proxy.rb +0 -33
  101. data/lib/sidekiq/util.rb +0 -108
  102. data/lib/sidekiq/worker.rb +0 -362
  103. /data/{LICENSE → LICENSE.txt} +0 -0
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "sidekiq/util"
4
3
  require "sidekiq/fetch"
5
4
  require "sidekiq/job_logger"
6
5
  require "sidekiq/job_retry"
@@ -11,33 +10,34 @@ 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
- include Util
25
+ include Sidekiq::Component
27
26
 
28
27
  attr_reader :thread
29
28
  attr_reader :job
29
+ attr_reader :capsule
30
30
 
31
- def initialize(mgr, options)
32
- @mgr = mgr
31
+ def initialize(capsule, &block)
32
+ @config = @capsule = capsule
33
+ @callback = block
33
34
  @down = false
34
35
  @done = false
35
36
  @job = nil
36
37
  @thread = nil
37
- @strategy = options[:fetch]
38
- @reloader = options[:reloader] || proc { |&block| block.call }
39
- @job_logger = (options[:job_logger] || Sidekiq::JobLogger).new
40
- @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)
41
41
  end
42
42
 
43
43
  def terminate(wait = false)
@@ -59,33 +59,37 @@ module Sidekiq
59
59
  end
60
60
 
61
61
  def start
62
- @thread ||= safe_thread("processor", &method(:run))
62
+ @thread ||= safe_thread("#{config.name}/processor", &method(:run))
63
63
  end
64
64
 
65
65
  private unless $TESTING
66
66
 
67
67
  def run
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
+
68
72
  process_one until @done
69
- @mgr.processor_stopped(self)
73
+ @callback.call(self)
70
74
  rescue Sidekiq::Shutdown
71
- @mgr.processor_stopped(self)
75
+ @callback.call(self)
72
76
  rescue Exception => ex
73
- @mgr.processor_died(self, ex)
77
+ @callback.call(self, ex)
74
78
  end
75
79
 
76
- def process_one
80
+ def process_one(&block)
77
81
  @job = fetch
78
82
  process(@job) if @job
79
83
  @job = nil
80
84
  end
81
85
 
82
86
  def get_one
83
- work = @strategy.retrieve_work
87
+ uow = capsule.fetcher.retrieve_work
84
88
  if @down
85
89
  logger.info { "Redis is online, #{::Process.clock_gettime(::Process::CLOCK_MONOTONIC) - @down} sec downtime" }
86
90
  @down = nil
87
91
  end
88
- work
92
+ uow
89
93
  rescue Sidekiq::Shutdown
90
94
  rescue => ex
91
95
  handle_fetch_exception(ex)
@@ -129,11 +133,11 @@ module Sidekiq
129
133
  # the Reloader. It handles code loading, db connection management, etc.
130
134
  # Effectively this block denotes a "unit of work" to Rails.
131
135
  @reloader.call do
132
- klass = constantize(job_hash["class"])
133
- worker = klass.new
134
- worker.jid = job_hash["jid"]
135
- @retrier.local(worker, jobstr, queue) do
136
- 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
137
141
  end
138
142
  end
139
143
  end
@@ -142,9 +146,9 @@ module Sidekiq
142
146
  end
143
147
  end
144
148
 
145
- def process(work)
146
- jobstr = work.job
147
- queue = work.queue_name
149
+ def process(uow)
150
+ jobstr = uow.job
151
+ queue = uow.queue_name
148
152
 
149
153
  # Treat malformed JSON as a special case: job goes straight to the morgue.
150
154
  job_hash = nil
@@ -152,16 +156,22 @@ module Sidekiq
152
156
  job_hash = Sidekiq.load_json(jobstr)
153
157
  rescue => ex
154
158
  handle_exception(ex, {context: "Invalid JSON for job", jobstr: jobstr})
155
- # we can't notify because the job isn't a valid hash payload.
156
- DeadSet.new.kill(jobstr, notify_failure: false)
157
- return work.acknowledge
159
+ now = Time.now.to_f
160
+ redis do |conn|
161
+ conn.multi do |xa|
162
+ xa.zadd("dead", now.to_s, jobstr)
163
+ xa.zremrangebyscore("dead", "-inf", now - @capsule.config[:dead_timeout_in_seconds])
164
+ xa.zremrangebyrank("dead", 0, - @capsule.config[:dead_max_jobs])
165
+ end
166
+ end
167
+ return uow.acknowledge
158
168
  end
159
169
 
160
170
  ack = false
161
171
  begin
162
- dispatch(job_hash, queue, jobstr) do |worker|
163
- Sidekiq.server_middleware.invoke(worker, job_hash, queue) do
164
- execute_job(worker, job_hash["args"])
172
+ dispatch(job_hash, queue, jobstr) do |inst|
173
+ config.server_middleware.invoke(inst, job_hash, queue) do
174
+ execute_job(inst, job_hash["args"])
165
175
  end
166
176
  end
167
177
  ack = true
@@ -174,7 +184,7 @@ module Sidekiq
174
184
  # signals that we created a retry successfully. We can acknowlege the job.
175
185
  ack = true
176
186
  e = h.cause || h
177
- handle_exception(e, {context: "Job raised exception", job: job_hash, jobstr: jobstr})
187
+ handle_exception(e, {context: "Job raised exception", job: job_hash})
178
188
  raise e
179
189
  rescue Exception => ex
180
190
  # Unexpected error! This is very bad and indicates an exception that got past
@@ -186,14 +196,14 @@ module Sidekiq
186
196
  if ack
187
197
  # We don't want a shutdown signal to interrupt job acknowledgment.
188
198
  Thread.handle_interrupt(Sidekiq::Shutdown => :never) do
189
- work.acknowledge
199
+ uow.acknowledge
190
200
  end
191
201
  end
192
202
  end
193
203
  end
194
204
 
195
- def execute_job(worker, cloned_args)
196
- worker.perform(*cloned_args)
205
+ def execute_job(inst, cloned_args)
206
+ inst.perform(*cloned_args)
197
207
  end
198
208
 
199
209
  # Ruby doesn't provide atomic counters out of the box so we'll
@@ -219,39 +229,39 @@ module Sidekiq
219
229
  end
220
230
 
221
231
  # jruby's Hash implementation is not threadsafe, so we wrap it in a mutex here
222
- class SharedWorkerState
232
+ class SharedWorkState
223
233
  def initialize
224
- @worker_state = {}
234
+ @work_state = {}
225
235
  @lock = Mutex.new
226
236
  end
227
237
 
228
238
  def set(tid, hash)
229
- @lock.synchronize { @worker_state[tid] = hash }
239
+ @lock.synchronize { @work_state[tid] = hash }
230
240
  end
231
241
 
232
242
  def delete(tid)
233
- @lock.synchronize { @worker_state.delete(tid) }
243
+ @lock.synchronize { @work_state.delete(tid) }
234
244
  end
235
245
 
236
246
  def dup
237
- @lock.synchronize { @worker_state.dup }
247
+ @lock.synchronize { @work_state.dup }
238
248
  end
239
249
 
240
250
  def size
241
- @lock.synchronize { @worker_state.size }
251
+ @lock.synchronize { @work_state.size }
242
252
  end
243
253
 
244
254
  def clear
245
- @lock.synchronize { @worker_state.clear }
255
+ @lock.synchronize { @work_state.clear }
246
256
  end
247
257
  end
248
258
 
249
259
  PROCESSED = Counter.new
250
260
  FAILURE = Counter.new
251
- WORKER_STATE = SharedWorkerState.new
261
+ WORK_STATE = SharedWorkState.new
252
262
 
253
263
  def stats(jobstr, queue)
254
- WORKER_STATE.set(tid, {queue: queue, payload: jobstr, run_at: Time.now.to_i})
264
+ WORK_STATE.set(tid, {queue: queue, payload: jobstr, run_at: Time.now.to_i})
255
265
 
256
266
  begin
257
267
  yield
@@ -259,22 +269,9 @@ module Sidekiq
259
269
  FAILURE.incr
260
270
  raise
261
271
  ensure
262
- WORKER_STATE.delete(tid)
272
+ WORK_STATE.delete(tid)
263
273
  PROCESSED.incr
264
274
  end
265
275
  end
266
-
267
- def constantize(str)
268
- return Object.const_get(str) unless str.include?("::")
269
-
270
- names = str.split("::")
271
- names.shift if names.empty? || names.first.empty?
272
-
273
- names.inject(Object) do |constant, name|
274
- # the false flag limits search for name to under the constant namespace
275
- # which mimics Rails' behaviour
276
- constant.const_get(name, false)
277
- end
278
- end
279
276
  end
280
277
  end
data/lib/sidekiq/rails.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "sidekiq/worker"
3
+ require "sidekiq/job"
4
+ require "rails"
4
5
 
5
6
  module Sidekiq
6
7
  class Rails < ::Rails::Engine
@@ -22,7 +23,7 @@ module Sidekiq
22
23
 
23
24
  # By including the Options module, we allow AJs to directly control sidekiq features
24
25
  # via the *sidekiq_options* class method and, for instance, not use AJ's retry system.
25
- # AJ retries don't show up in the Sidekiq UI Retries tab, save any error data, can't be
26
+ # AJ retries don't show up in the Sidekiq UI Retries tab, don't save any error data, can't be
26
27
  # manually retried, don't automatically die, etc.
27
28
  #
28
29
  # class SomeJob < ActiveJob::Base
@@ -33,17 +34,17 @@ module Sidekiq
33
34
  # end
34
35
  initializer "sidekiq.active_job_integration" do
35
36
  ActiveSupport.on_load(:active_job) do
36
- include ::Sidekiq::Worker::Options unless respond_to?(:sidekiq_options)
37
+ include ::Sidekiq::Job::Options unless respond_to?(:sidekiq_options)
37
38
  end
38
39
  end
39
40
 
40
41
  initializer "sidekiq.rails_logger" do
41
- Sidekiq.configure_server do |_|
42
- # This is the integration code necessary so that if code uses `Rails.logger.info "Hello"`,
42
+ Sidekiq.configure_server do |config|
43
+ # This is the integration code necessary so that if a job uses `Rails.logger.info "Hello"`,
43
44
  # it will appear in the Sidekiq console with all of the job context. See #5021 and
44
45
  # https://github.com/rails/rails/blob/b5f2b550f69a99336482739000c58e4e04e033aa/railties/lib/rails/commands/server/server_command.rb#L82-L84
45
- unless ::Rails.logger == ::Sidekiq.logger || ::ActiveSupport::Logger.logger_outputs_to?(::Rails.logger, $stdout)
46
- ::Rails.logger.extend(::ActiveSupport::Logger.broadcast(::Sidekiq.logger))
46
+ unless ::Rails.logger == config.logger || ::ActiveSupport::Logger.logger_outputs_to?(::Rails.logger, $stdout)
47
+ ::Rails.logger.extend(::ActiveSupport::Logger.broadcast(config.logger))
47
48
  end
48
49
  end
49
50
  end
@@ -53,8 +54,8 @@ module Sidekiq
53
54
  #
54
55
  # None of this matters on the client-side, only within the Sidekiq process itself.
55
56
  config.after_initialize do
56
- Sidekiq.configure_server do |_|
57
- Sidekiq.options[:reloader] = Sidekiq::Rails::Reloader.new
57
+ Sidekiq.configure_server do |config|
58
+ config[:reloader] = Sidekiq::Rails::Reloader.new
58
59
  end
59
60
  end
60
61
  end
@@ -0,0 +1,118 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "redis_client"
4
+ require "redis_client/decorator"
5
+
6
+ module Sidekiq
7
+ class RedisClientAdapter
8
+ BaseError = RedisClient::Error
9
+ CommandError = RedisClient::CommandError
10
+
11
+ module CompatMethods
12
+ def info
13
+ @client.call("INFO") { |i| i.lines(chomp: true).map { |l| l.split(":", 2) }.select { |l| l.size == 2 }.to_h }
14
+ end
15
+
16
+ def evalsha(sha, keys, argv)
17
+ @client.call("EVALSHA", sha, keys.size, *keys, *argv)
18
+ end
19
+
20
+ private
21
+
22
+ # this allows us to use methods like `conn.hmset(...)` instead of having to use
23
+ # redis-client's native `conn.call("hmset", ...)`
24
+ def method_missing(*args, &block)
25
+ @client.call(*args, *block)
26
+ end
27
+ ruby2_keywords :method_missing if respond_to?(:ruby2_keywords, true)
28
+
29
+ def respond_to_missing?(name, include_private = false)
30
+ super # Appease the linter. We can't tell what is a valid command.
31
+ end
32
+ end
33
+
34
+ CompatClient = RedisClient::Decorator.create(CompatMethods)
35
+
36
+ class CompatClient
37
+ # underscore methods are not official API
38
+ def _client
39
+ @client
40
+ end
41
+
42
+ def _config
43
+ @client.config
44
+ end
45
+
46
+ def message
47
+ yield nil, @queue.pop
48
+ end
49
+
50
+ # NB: this method does not return
51
+ def subscribe(chan)
52
+ @queue = ::Queue.new
53
+
54
+ pubsub = @client.pubsub
55
+ pubsub.call("subscribe", chan)
56
+
57
+ loop do
58
+ evt = pubsub.next_event
59
+ next if evt.nil?
60
+ next unless evt[0] == "message" && evt[1] == chan
61
+
62
+ (_, _, msg) = evt
63
+ @queue << msg
64
+ yield self
65
+ end
66
+ end
67
+ end
68
+
69
+ def initialize(options)
70
+ opts = client_opts(options)
71
+ @config = if opts.key?(:sentinels)
72
+ RedisClient.sentinel(**opts)
73
+ else
74
+ RedisClient.config(**opts)
75
+ end
76
+ end
77
+
78
+ def new_client
79
+ CompatClient.new(@config.new_client)
80
+ end
81
+
82
+ private
83
+
84
+ def client_opts(options)
85
+ opts = options.dup
86
+
87
+ if opts[:namespace]
88
+ raise ArgumentError, "Your Redis configuration uses the namespace '#{opts[:namespace]}' but this feature isn't supported by redis-client. " \
89
+ "Either use the redis adapter or remove the namespace."
90
+ end
91
+
92
+ opts.delete(:size)
93
+ opts.delete(:pool_timeout)
94
+
95
+ if opts[:network_timeout]
96
+ opts[:timeout] = opts[:network_timeout]
97
+ opts.delete(:network_timeout)
98
+ end
99
+
100
+ if opts[:driver]
101
+ opts[:driver] = opts[:driver].to_sym
102
+ end
103
+
104
+ opts[:name] = opts.delete(:master_name) if opts.key?(:master_name)
105
+ opts[:role] = opts[:role].to_sym if opts.key?(:role)
106
+ opts.delete(:url) if opts.key?(:sentinels)
107
+
108
+ # Issue #3303, redis-rb will silently retry an operation.
109
+ # This can lead to duplicate jobs if Sidekiq::Client's LPUSH
110
+ # is performed twice but I believe this is much, much rarer
111
+ # than the reconnect silently fixing a problem; we keep it
112
+ # on by default.
113
+ opts[:reconnect_attempts] ||= 1
114
+
115
+ opts
116
+ end
117
+ end
118
+ end
@@ -1,97 +1,32 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "connection_pool"
4
- require "redis"
5
4
  require "uri"
5
+ require "sidekiq/redis_client_adapter"
6
6
 
7
7
  module Sidekiq
8
- class RedisConnection
8
+ module RedisConnection
9
9
  class << self
10
10
  def create(options = {})
11
11
  symbolized_options = options.transform_keys(&:to_sym)
12
+ symbolized_options[:url] ||= determine_redis_provider
12
13
 
13
- if !symbolized_options[:url] && (u = determine_redis_provider)
14
- symbolized_options[:url] = u
15
- end
16
-
17
- size = if symbolized_options[:size]
18
- symbolized_options[:size]
19
- elsif Sidekiq.server?
20
- # Give ourselves plenty of connections. pool is lazy
21
- # so we won't create them until we need them.
22
- Sidekiq.options[:concurrency] + 5
23
- elsif ENV["RAILS_MAX_THREADS"]
24
- Integer(ENV["RAILS_MAX_THREADS"])
25
- else
26
- 5
27
- end
14
+ logger = symbolized_options.delete(:logger)
15
+ logger&.info { "Sidekiq #{Sidekiq::VERSION} connecting to Redis with options #{scrub(symbolized_options)}" }
28
16
 
29
- verify_sizing(size, Sidekiq.options[:concurrency]) if Sidekiq.server?
17
+ size = symbolized_options.delete(:size) || 5
18
+ pool_timeout = symbolized_options.delete(:pool_timeout) || 1
19
+ pool_name = symbolized_options.delete(:pool_name)
30
20
 
31
- pool_timeout = symbolized_options[:pool_timeout] || 1
32
- log_info(symbolized_options)
33
-
34
- ConnectionPool.new(timeout: pool_timeout, size: size) do
35
- build_client(symbolized_options)
21
+ redis_config = Sidekiq::RedisClientAdapter.new(symbolized_options)
22
+ ConnectionPool.new(timeout: pool_timeout, size: size, name: pool_name) do
23
+ redis_config.new_client
36
24
  end
37
25
  end
38
26
 
39
27
  private
40
28
 
41
- # Sidekiq needs a lot of concurrent Redis connections.
42
- #
43
- # We need a connection for each Processor.
44
- # We need a connection for Pro's real-time change listener
45
- # We need a connection to various features to call Redis every few seconds:
46
- # - the process heartbeat.
47
- # - enterprise's leader election
48
- # - enterprise's cron support
49
- def verify_sizing(size, concurrency)
50
- raise ArgumentError, "Your Redis connection pool is too small for Sidekiq to work. Your pool has #{size} connections but must have at least #{concurrency + 2}" if size < (concurrency + 2)
51
- end
52
-
53
- def build_client(options)
54
- namespace = options[:namespace]
55
-
56
- client = Redis.new client_opts(options)
57
- if namespace
58
- begin
59
- require "redis/namespace"
60
- Redis::Namespace.new(namespace, redis: client)
61
- rescue LoadError
62
- Sidekiq.logger.error("Your Redis configuration uses the namespace '#{namespace}' but the redis-namespace gem is not included in the Gemfile." \
63
- "Add the gem to your Gemfile to continue using a namespace. Otherwise, remove the namespace parameter.")
64
- exit(-127)
65
- end
66
- else
67
- client
68
- end
69
- end
70
-
71
- def client_opts(options)
72
- opts = options.dup
73
- if opts[:namespace]
74
- opts.delete(:namespace)
75
- end
76
-
77
- if opts[:network_timeout]
78
- opts[:timeout] = opts[:network_timeout]
79
- opts.delete(:network_timeout)
80
- end
81
-
82
- opts[:driver] ||= Redis::Connection.drivers.last || "ruby"
83
-
84
- # Issue #3303, redis-rb will silently retry an operation.
85
- # This can lead to duplicate jobs if Sidekiq::Client's LPUSH
86
- # is performed twice but I believe this is much, much rarer
87
- # than the reconnect silently fixing a problem; we keep it
88
- # on by default.
89
- opts[:reconnect_attempts] ||= 1
90
-
91
- opts
92
- end
93
-
94
- def log_info(options)
29
+ def scrub(options)
95
30
  redacted = "REDACTED"
96
31
 
97
32
  # Deep clone so we can muck with these options all we want and exclude
@@ -109,11 +44,7 @@ module Sidekiq
109
44
  scrubbed_options[:sentinels]&.each do |sentinel|
110
45
  sentinel[:password] = redacted if sentinel[:password]
111
46
  end
112
- if Sidekiq.server?
113
- Sidekiq.logger.info("Booting Sidekiq #{Sidekiq::VERSION} with redis options #{scrubbed_options}")
114
- else
115
- Sidekiq.logger.debug("#{Sidekiq::NAME} client with redis options #{scrubbed_options}")
116
- end
47
+ scrubbed_options
117
48
  end
118
49
 
119
50
  def determine_redis_provider
@@ -0,0 +1,29 @@
1
+ require "forwardable"
2
+
3
+ module Sidekiq
4
+ class RingBuffer
5
+ include Enumerable
6
+ extend Forwardable
7
+ def_delegators :@buf, :[], :each, :size
8
+
9
+ def initialize(size, default = 0)
10
+ @size = size
11
+ @buf = Array.new(size, default)
12
+ @index = 0
13
+ end
14
+
15
+ def <<(element)
16
+ @buf[@index % @size] = element
17
+ @index += 1
18
+ element
19
+ end
20
+
21
+ def buffer
22
+ @buf
23
+ end
24
+
25
+ def reset(default = 0)
26
+ @buf.fill(default)
27
+ end
28
+ end
29
+ end