sidekiq 6.4.2 → 6.5.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.

@@ -3,11 +3,12 @@
3
3
  require "sidekiq/manager"
4
4
  require "sidekiq/fetch"
5
5
  require "sidekiq/scheduled"
6
+ require "sidekiq/ring_buffer"
6
7
 
7
8
  module Sidekiq
8
9
  # The Launcher starts the Manager and Poller threads and provides the process heartbeat.
9
10
  class Launcher
10
- include Util
11
+ include Sidekiq::Component
11
12
 
12
13
  STATS_TTL = 5 * 365 * 24 * 60 * 60 # 5 years
13
14
 
@@ -22,11 +23,11 @@ module Sidekiq
22
23
  attr_accessor :manager, :poller, :fetcher
23
24
 
24
25
  def initialize(options)
26
+ @config = options
25
27
  options[:fetch] ||= BasicFetch.new(options)
26
28
  @manager = Sidekiq::Manager.new(options)
27
- @poller = Sidekiq::Scheduled::Poller.new
29
+ @poller = Sidekiq::Scheduled::Poller.new(options)
28
30
  @done = false
29
- @options = options
30
31
  end
31
32
 
32
33
  def run
@@ -45,7 +46,7 @@ module Sidekiq
45
46
 
46
47
  # Shuts down this Sidekiq instance. Waits up to the deadline for all jobs to complete.
47
48
  def stop
48
- deadline = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC) + @options[:timeout]
49
+ deadline = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC) + @config[:timeout]
49
50
 
50
51
  @done = true
51
52
  @manager.quiet
@@ -55,8 +56,8 @@ module Sidekiq
55
56
 
56
57
  # Requeue everything in case there was a thread which fetched a job while the process was stopped.
57
58
  # This call is a no-op in Sidekiq but necessary for Sidekiq Pro.
58
- strategy = @options[:fetch]
59
- strategy.bulk_requeue([], @options)
59
+ strategy = @config[:fetch]
60
+ strategy.bulk_requeue([], @config)
60
61
 
61
62
  clear_heartbeat
62
63
  end
@@ -74,14 +75,14 @@ module Sidekiq
74
75
  heartbeat
75
76
  sleep BEAT_PAUSE
76
77
  end
77
- Sidekiq.logger.info("Heartbeat stopping...")
78
+ logger.info("Heartbeat stopping...")
78
79
  end
79
80
 
80
81
  def clear_heartbeat
81
82
  # Remove record from Redis since we are shutting down.
82
83
  # Note we don't stop the heartbeat thread; if the process
83
84
  # doesn't actually exit, it'll reappear in the Web UI.
84
- Sidekiq.redis do |conn|
85
+ redis do |conn|
85
86
  conn.pipelined do |pipeline|
86
87
  pipeline.srem("processes", identity)
87
88
  pipeline.unlink("#{identity}:work")
@@ -134,7 +135,7 @@ module Sidekiq
134
135
 
135
136
  nowdate = Time.now.utc.strftime("%Y-%m-%d")
136
137
 
137
- Sidekiq.redis do |conn|
138
+ redis do |conn|
138
139
  conn.multi do |transaction|
139
140
  transaction.incrby("stat:processed", procd)
140
141
  transaction.incrby("stat:processed:#{nowdate}", procd)
@@ -161,7 +162,7 @@ module Sidekiq
161
162
  fails = procd = 0
162
163
  kb = memory_usage(::Process.pid)
163
164
 
164
- _, exists, _, _, msg = Sidekiq.redis { |conn|
165
+ _, exists, _, _, msg = redis { |conn|
165
166
  conn.multi { |transaction|
166
167
  transaction.sadd("processes", key)
167
168
  transaction.exists?(key)
@@ -169,7 +170,7 @@ module Sidekiq
169
170
  "busy", curstate.size,
170
171
  "beat", Time.now.to_f,
171
172
  "rtt_us", rtt,
172
- "quiet", @done,
173
+ "quiet", @done.to_s,
173
174
  "rss", kb)
174
175
  transaction.expire(key, 60)
175
176
  transaction.rpop("#{key}-signals")
@@ -199,7 +200,7 @@ module Sidekiq
199
200
 
200
201
  def check_rtt
201
202
  a = b = 0
202
- Sidekiq.redis do |x|
203
+ redis do |x|
203
204
  a = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :microsecond)
204
205
  x.ping
205
206
  b = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :microsecond)
@@ -210,7 +211,7 @@ module Sidekiq
210
211
  # Workable is < 10,000µs
211
212
  # Log a warning if it's a disaster.
212
213
  if RTT_READINGS.all? { |x| x > RTT_WARNING_LEVEL }
213
- Sidekiq.logger.warn <<~EOM
214
+ logger.warn <<~EOM
214
215
  Your Redis network connection is performing extremely poorly.
215
216
  Last RTT readings were #{RTT_READINGS.buffer.inspect}, ideally these should be < 1000.
216
217
  Ensure Redis is running in the same AZ or datacenter as Sidekiq.
@@ -247,10 +248,10 @@ module Sidekiq
247
248
  "hostname" => hostname,
248
249
  "started_at" => Time.now.to_f,
249
250
  "pid" => ::Process.pid,
250
- "tag" => @options[:tag] || "",
251
- "concurrency" => @options[:concurrency],
252
- "queues" => @options[:queues].uniq,
253
- "labels" => @options[:labels],
251
+ "tag" => @config[:tag] || "",
252
+ "concurrency" => @config[:concurrency],
253
+ "queues" => @config[:queues].uniq,
254
+ "labels" => @config[:labels],
254
255
  "identity" => identity
255
256
  }
256
257
  end
@@ -18,7 +18,7 @@ module Sidekiq
18
18
  end
19
19
 
20
20
  def self.add(k, v)
21
- Thread.current[:sidekiq_context][k] = v
21
+ current[k] = v
22
22
  end
23
23
  end
24
24
 
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "sidekiq/util"
4
3
  require "sidekiq/processor"
5
4
  require "sidekiq/fetch"
6
5
  require "set"
@@ -21,29 +20,26 @@ module Sidekiq
21
20
  # the shutdown process. The other tasks are performed by other threads.
22
21
  #
23
22
  class Manager
24
- include Util
23
+ include Sidekiq::Component
25
24
 
26
25
  attr_reader :workers
27
- attr_reader :options
28
26
 
29
27
  def initialize(options = {})
28
+ @config = options
30
29
  logger.debug { options.inspect }
31
- @options = options
32
30
  @count = options[:concurrency] || 10
33
31
  raise ArgumentError, "Concurrency of #{@count} is not supported" if @count < 1
34
32
 
35
33
  @done = false
36
34
  @workers = Set.new
37
35
  @count.times do
38
- @workers << Processor.new(self, options)
36
+ @workers << Processor.new(@config, &method(:processor_result))
39
37
  end
40
38
  @plock = Mutex.new
41
39
  end
42
40
 
43
41
  def start
44
- @workers.each do |x|
45
- x.start
46
- end
42
+ @workers.each(&:start)
47
43
  end
48
44
 
49
45
  def quiet
@@ -51,7 +47,7 @@ module Sidekiq
51
47
  @done = true
52
48
 
53
49
  logger.info { "Terminating quiet threads" }
54
- @workers.each { |x| x.terminate }
50
+ @workers.each(&:terminate)
55
51
  fire_event(:quiet, reverse: true)
56
52
  end
57
53
 
@@ -72,17 +68,11 @@ module Sidekiq
72
68
  hard_shutdown
73
69
  end
74
70
 
75
- def processor_stopped(processor)
76
- @plock.synchronize do
77
- @workers.delete(processor)
78
- end
79
- end
80
-
81
- def processor_died(processor, reason)
71
+ def processor_result(processor, reason = nil)
82
72
  @plock.synchronize do
83
73
  @workers.delete(processor)
84
74
  unless @done
85
- p = Processor.new(self, options)
75
+ p = Processor.new(@config, &method(:processor_result))
86
76
  @workers << p
87
77
  p.start
88
78
  end
@@ -107,7 +97,7 @@ module Sidekiq
107
97
  jobs = cleanup.map { |p| p.job }.compact
108
98
 
109
99
  logger.warn { "Terminating #{cleanup.size} busy threads" }
110
- logger.warn { "Jobs still in progress #{jobs.inspect}" }
100
+ logger.debug { "Jobs still in progress #{jobs.inspect}" }
111
101
 
112
102
  # Re-enqueue unfinished jobs
113
103
  # NOTE: You may notice that we may push a job back to redis before
@@ -115,8 +105,8 @@ module Sidekiq
115
105
  # contract says that jobs are run AT LEAST once. Process termination
116
106
  # is delayed until we're certain the jobs are back in Redis because
117
107
  # it is worse to lose a job than to run it twice.
118
- strategy = @options[:fetch]
119
- strategy.bulk_requeue(jobs, @options)
108
+ strategy = @config[:fetch]
109
+ strategy.bulk_requeue(jobs, @config)
120
110
  end
121
111
 
122
112
  cleanup.each do |processor|
@@ -129,5 +119,18 @@ module Sidekiq
129
119
  deadline = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC) + 3
130
120
  wait_for(deadline) { @workers.empty? }
131
121
  end
122
+
123
+ # hack for quicker development / testing environment #2774
124
+ PAUSE_TIME = $stdout.tty? ? 0.1 : 0.5
125
+
126
+ # Wait for the orblock to be true or the deadline passed.
127
+ def wait_for(deadline, &condblock)
128
+ remaining = deadline - ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
129
+ while remaining > PAUSE_TIME
130
+ return if condblock.call
131
+ sleep PAUSE_TIME
132
+ remaining = deadline - ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
133
+ end
134
+ end
132
135
  end
133
136
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "sidekiq/middleware/modules"
4
+
3
5
  module Sidekiq
4
6
  # Middleware is code configured to run before/after
5
7
  # a message is processed. It is patterned after Rack
@@ -44,10 +46,12 @@ module Sidekiq
44
46
  # This is an example of a minimal server middleware:
45
47
  #
46
48
  # class MyServerHook
49
+ # include Sidekiq::ServerMiddleware
47
50
  # def call(job_instance, msg, queue)
48
- # puts "Before job"
51
+ # logger.info "Before job"
52
+ # redis {|conn| conn.get("foo") } # do something in Redis
49
53
  # yield
50
- # puts "After job"
54
+ # logger.info "After job"
51
55
  # end
52
56
  # end
53
57
  #
@@ -56,10 +60,11 @@ module Sidekiq
56
60
  # to Redis:
57
61
  #
58
62
  # class MyClientHook
63
+ # include Sidekiq::ClientMiddleware
59
64
  # def call(job_class, msg, queue, redis_pool)
60
- # puts "Before push"
65
+ # logger.info "Before push"
61
66
  # result = yield
62
- # puts "After push"
67
+ # logger.info "After push"
63
68
  # result
64
69
  # end
65
70
  # end
@@ -76,7 +81,8 @@ module Sidekiq
76
81
  entries.each(&block)
77
82
  end
78
83
 
79
- def initialize
84
+ def initialize(config = nil)
85
+ @config = config
80
86
  @entries = nil
81
87
  yield self if block_given?
82
88
  end
@@ -91,24 +97,24 @@ module Sidekiq
91
97
 
92
98
  def add(klass, *args)
93
99
  remove(klass)
94
- entries << Entry.new(klass, *args)
100
+ entries << Entry.new(@config, klass, *args)
95
101
  end
96
102
 
97
103
  def prepend(klass, *args)
98
104
  remove(klass)
99
- entries.insert(0, Entry.new(klass, *args))
105
+ entries.insert(0, Entry.new(@config, klass, *args))
100
106
  end
101
107
 
102
108
  def insert_before(oldklass, newklass, *args)
103
109
  i = entries.index { |entry| entry.klass == newklass }
104
- new_entry = i.nil? ? Entry.new(newklass, *args) : entries.delete_at(i)
110
+ new_entry = i.nil? ? Entry.new(@config, newklass, *args) : entries.delete_at(i)
105
111
  i = entries.index { |entry| entry.klass == oldklass } || 0
106
112
  entries.insert(i, new_entry)
107
113
  end
108
114
 
109
115
  def insert_after(oldklass, newklass, *args)
110
116
  i = entries.index { |entry| entry.klass == newklass }
111
- new_entry = i.nil? ? Entry.new(newklass, *args) : entries.delete_at(i)
117
+ new_entry = i.nil? ? Entry.new(@config, newklass, *args) : entries.delete_at(i)
112
118
  i = entries.index { |entry| entry.klass == oldklass } || entries.count - 1
113
119
  entries.insert(i + 1, new_entry)
114
120
  end
@@ -149,13 +155,16 @@ module Sidekiq
149
155
  class Entry
150
156
  attr_reader :klass
151
157
 
152
- def initialize(klass, *args)
158
+ def initialize(config, klass, *args)
159
+ @config = config
153
160
  @klass = klass
154
161
  @args = args
155
162
  end
156
163
 
157
164
  def make_new
158
- @klass.new(*@args)
165
+ x = @klass.new(*@args)
166
+ x.config = @config if @config && x.respond_to?(:config=)
167
+ x
159
168
  end
160
169
  end
161
170
  end
@@ -15,6 +15,8 @@ module Sidekiq
15
15
  #
16
16
  module CurrentAttributes
17
17
  class Save
18
+ include Sidekiq::ClientMiddleware
19
+
18
20
  def initialize(cattr)
19
21
  @klass = cattr
20
22
  end
@@ -31,6 +33,8 @@ module Sidekiq
31
33
  end
32
34
 
33
35
  class Load
36
+ include Sidekiq::ServerMiddleware
37
+
34
38
  def initialize(cattr)
35
39
  @klass = cattr
36
40
  end
@@ -10,6 +10,7 @@ module Sidekiq::Middleware::I18n
10
10
  # Get the current locale and store it in the message
11
11
  # to be sent to Sidekiq.
12
12
  class Client
13
+ include Sidekiq::ClientMiddleware
13
14
  def call(_jobclass, job, _queue, _redis)
14
15
  job["locale"] ||= I18n.locale
15
16
  yield
@@ -18,6 +19,7 @@ module Sidekiq::Middleware::I18n
18
19
 
19
20
  # Pull the msg locale out and set the current thread to use it.
20
21
  class Server
22
+ include Sidekiq::ServerMiddleware
21
23
  def call(_jobclass, job, _queue, &block)
22
24
  I18n.with_locale(job.fetch("locale", I18n.default_locale), &block)
23
25
  end
@@ -0,0 +1,19 @@
1
+ module Sidekiq
2
+ module ServerMiddleware
3
+ attr_accessor :config
4
+ def redis_pool
5
+ config.redis_pool
6
+ end
7
+
8
+ def logger
9
+ config.logger
10
+ end
11
+
12
+ def redis(&block)
13
+ config.redis(&block)
14
+ end
15
+ end
16
+
17
+ # no difference for now
18
+ ClientMiddleware = ServerMiddleware
19
+ end
@@ -19,9 +19,9 @@ module Sidekiq
19
19
  total_size, items = conn.multi { |transaction|
20
20
  transaction.zcard(key)
21
21
  if rev
22
- transaction.zrevrange(key, starting, ending, with_scores: true)
22
+ transaction.zrevrange(key, starting, ending, withscores: true)
23
23
  else
24
- transaction.zrange(key, starting, ending, with_scores: true)
24
+ transaction.zrange(key, starting, ending, withscores: true)
25
25
  end
26
26
  }
27
27
  [current_page, total_size, items]
@@ -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"
@@ -15,29 +14,30 @@ module Sidekiq
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
30
29
 
31
- def initialize(mgr, options)
32
- @mgr = mgr
30
+ def initialize(options, &block)
31
+ @callback = block
33
32
  @down = false
34
33
  @done = false
35
34
  @job = nil
36
35
  @thread = nil
36
+ @config = options
37
37
  @strategy = options[:fetch]
38
38
  @reloader = options[:reloader] || proc { |&block| block.call }
39
39
  @job_logger = (options[:job_logger] || Sidekiq::JobLogger).new
40
- @retrier = Sidekiq::JobRetry.new
40
+ @retrier = Sidekiq::JobRetry.new(options)
41
41
  end
42
42
 
43
43
  def terminate(wait = false)
@@ -66,14 +66,14 @@ module Sidekiq
66
66
 
67
67
  def run
68
68
  process_one until @done
69
- @mgr.processor_stopped(self)
69
+ @callback.call(self)
70
70
  rescue Sidekiq::Shutdown
71
- @mgr.processor_stopped(self)
71
+ @callback.call(self)
72
72
  rescue Exception => ex
73
- @mgr.processor_died(self, ex)
73
+ @callback.call(self, ex)
74
74
  end
75
75
 
76
- def process_one
76
+ def process_one(&block)
77
77
  @job = fetch
78
78
  process(@job) if @job
79
79
  @job = nil
@@ -160,7 +160,7 @@ module Sidekiq
160
160
  ack = false
161
161
  begin
162
162
  dispatch(job_hash, queue, jobstr) do |inst|
163
- Sidekiq.server_middleware.invoke(inst, job_hash, queue) do
163
+ @config.server_middleware.invoke(inst, job_hash, queue) do
164
164
  execute_job(inst, job_hash["args"])
165
165
  end
166
166
  end
data/lib/sidekiq/rails.rb CHANGED
@@ -38,12 +38,12 @@ module Sidekiq
38
38
  end
39
39
 
40
40
  initializer "sidekiq.rails_logger" do
41
- Sidekiq.configure_server do |_|
41
+ Sidekiq.configure_server do |config|
42
42
  # This is the integration code necessary so that if a job uses `Rails.logger.info "Hello"`,
43
43
  # it will appear in the Sidekiq console with all of the job context. See #5021 and
44
44
  # 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))
45
+ unless ::Rails.logger == config.logger || ::ActiveSupport::Logger.logger_outputs_to?(::Rails.logger, $stdout)
46
+ ::Rails.logger.extend(::ActiveSupport::Logger.broadcast(config.logger))
47
47
  end
48
48
  end
49
49
  end
@@ -60,8 +60,8 @@ module Sidekiq
60
60
  #
61
61
  # None of this matters on the client-side, only within the Sidekiq process itself.
62
62
  config.after_initialize do
63
- Sidekiq.configure_server do |_|
64
- Sidekiq.options[:reloader] = Sidekiq::Rails::Reloader.new
63
+ Sidekiq.configure_server do |config|
64
+ config[:reloader] = Sidekiq::Rails::Reloader.new
65
65
  end
66
66
  end
67
67
  end
@@ -0,0 +1,154 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "connection_pool"
4
+ require "redis_client"
5
+ require "redis_client/decorator"
6
+ require "uri"
7
+
8
+ module Sidekiq
9
+ class RedisClientAdapter
10
+ BaseError = RedisClient::Error
11
+ CommandError = RedisClient::CommandError
12
+
13
+ module CompatMethods
14
+ def info
15
+ @client.call("INFO") { |i| i.lines(chomp: true).map { |l| l.split(":", 2) }.select { |l| l.size == 2 }.to_h }
16
+ end
17
+
18
+ def evalsha(sha, keys, argv)
19
+ @client.call("EVALSHA", sha, keys.size, *keys, *argv)
20
+ end
21
+
22
+ def brpoplpush(*args)
23
+ @client.blocking_call(false, "BRPOPLPUSH", *args)
24
+ end
25
+
26
+ def brpop(*args)
27
+ @client.blocking_call(false, "BRPOP", *args)
28
+ end
29
+
30
+ def set(*args)
31
+ @client.call("SET", *args) { |r| r == "OK" }
32
+ end
33
+ ruby2_keywords :set if respond_to?(:ruby2_keywords, true)
34
+
35
+ def sismember(*args)
36
+ @client.call("SISMEMBER", *args) { |c| c > 0 }
37
+ end
38
+
39
+ def exists?(key)
40
+ @client.call("EXISTS", key) { |c| c > 0 }
41
+ end
42
+
43
+ private
44
+
45
+ def method_missing(*args, &block)
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
+ %i[scan sscan zscan hscan].each do |method|
59
+ alias_method :"#{method}_each", method
60
+ undef_method method
61
+ end
62
+
63
+ def disconnect!
64
+ @client.close
65
+ end
66
+
67
+ def connection
68
+ {id: @client.id}
69
+ end
70
+
71
+ def redis
72
+ self
73
+ end
74
+
75
+ def _client
76
+ @client
77
+ end
78
+
79
+ def message
80
+ yield nil, @queue.pop
81
+ end
82
+
83
+ # NB: this method does not return
84
+ def subscribe(chan)
85
+ @queue = ::Queue.new
86
+
87
+ pubsub = @client.pubsub
88
+ pubsub.call("subscribe", chan)
89
+
90
+ loop do
91
+ evt = pubsub.next_event
92
+ next if evt.nil?
93
+ next unless evt[0] == "message" && evt[1] == chan
94
+
95
+ (_, _, msg) = evt
96
+ @queue << msg
97
+ yield self
98
+ end
99
+ end
100
+ end
101
+
102
+ def initialize(options)
103
+ opts = client_opts(options)
104
+ @config = if opts.key?(:sentinels)
105
+ RedisClient.sentinel(**opts)
106
+ else
107
+ RedisClient.config(**opts)
108
+ end
109
+ end
110
+
111
+ def new_client
112
+ CompatClient.new(@config.new_client)
113
+ end
114
+
115
+ private
116
+
117
+ def client_opts(options)
118
+ opts = options.dup
119
+
120
+ if opts[:namespace]
121
+ Sidekiq.logger.error("Your Redis configuration uses the namespace '#{opts[:namespace]}' but this feature isn't supported by redis-client. " \
122
+ "Either use the redis adapter or remove the namespace.")
123
+ Kernel.exit(-127)
124
+ end
125
+
126
+ opts.delete(:size)
127
+ opts.delete(:pool_timeout)
128
+
129
+ if opts[:network_timeout]
130
+ opts[:timeout] = opts[:network_timeout]
131
+ opts.delete(:network_timeout)
132
+ end
133
+
134
+ if opts[:driver]
135
+ opts[:driver] = opts[:driver].to_sym
136
+ end
137
+
138
+ opts[:name] = opts.delete(:master_name) if opts.key?(:master_name)
139
+ opts[:role] = opts[:role].to_sym if opts.key?(:role)
140
+ opts.delete(:url) if opts.key?(:sentinels)
141
+
142
+ # Issue #3303, redis-rb will silently retry an operation.
143
+ # This can lead to duplicate jobs if Sidekiq::Client's LPUSH
144
+ # is performed twice but I believe this is much, much rarer
145
+ # than the reconnect silently fixing a problem; we keep it
146
+ # on by default.
147
+ opts[:reconnect_attempts] ||= 1
148
+
149
+ opts
150
+ end
151
+ end
152
+ end
153
+
154
+ Sidekiq::RedisConnection.adapter = Sidekiq::RedisClientAdapter