sidekiq 6.5.5 → 7.2.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 (108) hide show
  1. checksums.yaml +4 -4
  2. data/Changes.md +221 -13
  3. data/README.md +42 -34
  4. data/bin/sidekiq +3 -8
  5. data/bin/sidekiqload +204 -118
  6. data/bin/sidekiqmon +3 -0
  7. data/lib/sidekiq/api.rb +140 -134
  8. data/lib/sidekiq/capsule.rb +127 -0
  9. data/lib/sidekiq/cli.rb +57 -63
  10. data/lib/sidekiq/client.rb +68 -39
  11. data/lib/sidekiq/component.rb +4 -1
  12. data/lib/sidekiq/config.rb +287 -0
  13. data/lib/sidekiq/deploy.rb +62 -0
  14. data/lib/sidekiq/embedded.rb +61 -0
  15. data/lib/sidekiq/fetch.rb +11 -14
  16. data/lib/sidekiq/job.rb +371 -10
  17. data/lib/sidekiq/job_logger.rb +2 -2
  18. data/lib/sidekiq/job_retry.rb +35 -16
  19. data/lib/sidekiq/job_util.rb +51 -15
  20. data/lib/sidekiq/launcher.rb +68 -64
  21. data/lib/sidekiq/logger.rb +1 -26
  22. data/lib/sidekiq/manager.rb +9 -11
  23. data/lib/sidekiq/metrics/query.rb +7 -5
  24. data/lib/sidekiq/metrics/shared.rb +8 -7
  25. data/lib/sidekiq/metrics/tracking.rb +20 -18
  26. data/lib/sidekiq/middleware/chain.rb +19 -18
  27. data/lib/sidekiq/middleware/current_attributes.rb +53 -21
  28. data/lib/sidekiq/monitor.rb +17 -4
  29. data/lib/sidekiq/paginator.rb +11 -3
  30. data/lib/sidekiq/processor.rb +46 -51
  31. data/lib/sidekiq/rails.rb +16 -16
  32. data/lib/sidekiq/redis_client_adapter.rb +23 -66
  33. data/lib/sidekiq/redis_connection.rb +12 -113
  34. data/lib/sidekiq/scheduled.rb +60 -27
  35. data/lib/sidekiq/testing.rb +30 -39
  36. data/lib/sidekiq/transaction_aware_client.rb +4 -5
  37. data/lib/sidekiq/version.rb +2 -1
  38. data/lib/sidekiq/web/action.rb +3 -3
  39. data/lib/sidekiq/web/application.rb +96 -12
  40. data/lib/sidekiq/web/csrf_protection.rb +2 -2
  41. data/lib/sidekiq/web/helpers.rb +54 -55
  42. data/lib/sidekiq/web.rb +17 -16
  43. data/lib/sidekiq/worker_compatibility_alias.rb +13 -0
  44. data/lib/sidekiq.rb +76 -274
  45. data/sidekiq.gemspec +12 -10
  46. data/web/assets/javascripts/application.js +34 -0
  47. data/web/assets/javascripts/base-charts.js +106 -0
  48. data/web/assets/javascripts/dashboard-charts.js +182 -0
  49. data/web/assets/javascripts/dashboard.js +10 -232
  50. data/web/assets/javascripts/metrics.js +151 -115
  51. data/web/assets/stylesheets/application-dark.css +4 -0
  52. data/web/assets/stylesheets/application-rtl.css +2 -91
  53. data/web/assets/stylesheets/application.css +32 -298
  54. data/web/locales/ar.yml +70 -70
  55. data/web/locales/cs.yml +62 -62
  56. data/web/locales/da.yml +60 -53
  57. data/web/locales/de.yml +65 -65
  58. data/web/locales/el.yml +2 -7
  59. data/web/locales/en.yml +78 -70
  60. data/web/locales/es.yml +68 -68
  61. data/web/locales/fa.yml +65 -65
  62. data/web/locales/fr.yml +81 -67
  63. data/web/locales/gd.yml +99 -0
  64. data/web/locales/he.yml +65 -64
  65. data/web/locales/hi.yml +59 -59
  66. data/web/locales/it.yml +53 -53
  67. data/web/locales/ja.yml +73 -68
  68. data/web/locales/ko.yml +52 -52
  69. data/web/locales/lt.yml +66 -66
  70. data/web/locales/nb.yml +61 -61
  71. data/web/locales/nl.yml +52 -52
  72. data/web/locales/pl.yml +45 -45
  73. data/web/locales/pt-br.yml +79 -69
  74. data/web/locales/pt.yml +51 -51
  75. data/web/locales/ru.yml +67 -66
  76. data/web/locales/sv.yml +53 -53
  77. data/web/locales/ta.yml +60 -60
  78. data/web/locales/uk.yml +62 -61
  79. data/web/locales/ur.yml +64 -64
  80. data/web/locales/vi.yml +67 -67
  81. data/web/locales/zh-cn.yml +43 -16
  82. data/web/locales/zh-tw.yml +42 -8
  83. data/web/views/_footer.erb +5 -2
  84. data/web/views/_job_info.erb +18 -2
  85. data/web/views/_metrics_period_select.erb +12 -0
  86. data/web/views/_paging.erb +2 -0
  87. data/web/views/_poll_link.erb +1 -1
  88. data/web/views/_summary.erb +7 -7
  89. data/web/views/busy.erb +45 -29
  90. data/web/views/dashboard.erb +26 -5
  91. data/web/views/filtering.erb +7 -0
  92. data/web/views/metrics.erb +46 -24
  93. data/web/views/metrics_for_job.erb +41 -69
  94. data/web/views/morgue.erb +5 -9
  95. data/web/views/queue.erb +10 -14
  96. data/web/views/queues.erb +9 -3
  97. data/web/views/retries.erb +5 -9
  98. data/web/views/scheduled.erb +12 -13
  99. metadata +44 -27
  100. data/lib/sidekiq/delay.rb +0 -43
  101. data/lib/sidekiq/extensions/action_mailer.rb +0 -48
  102. data/lib/sidekiq/extensions/active_record.rb +0 -43
  103. data/lib/sidekiq/extensions/class_methods.rb +0 -43
  104. data/lib/sidekiq/extensions/generic_proxy.rb +0 -33
  105. data/lib/sidekiq/metrics/deploy.rb +0 -47
  106. data/lib/sidekiq/worker.rb +0 -367
  107. data/web/assets/javascripts/graph.js +0 -16
  108. /data/{LICENSE → LICENSE.txt} +0 -0
@@ -7,27 +7,31 @@ module Sidekiq
7
7
  # This can be useful for multi-tenancy, i18n locale, timezone, any implicit
8
8
  # per-request attribute. See +ActiveSupport::CurrentAttributes+.
9
9
  #
10
+ # For multiple current attributes, pass an array of current attributes.
11
+ #
10
12
  # @example
11
13
  #
12
14
  # # in your initializer
13
15
  # require "sidekiq/middleware/current_attributes"
14
- # Sidekiq::CurrentAttributes.persist(Myapp::Current)
16
+ # Sidekiq::CurrentAttributes.persist("Myapp::Current")
17
+ # # or multiple current attributes
18
+ # Sidekiq::CurrentAttributes.persist(["Myapp::Current", "Myapp::OtherCurrent"])
15
19
  #
16
20
  module CurrentAttributes
17
21
  class Save
18
22
  include Sidekiq::ClientMiddleware
19
23
 
20
- def initialize(cattr)
21
- @klass = cattr
24
+ def initialize(cattrs)
25
+ @cattrs = cattrs
22
26
  end
23
27
 
24
28
  def call(_, job, _, _)
25
- attrs = @klass.attributes
26
- if attrs.any?
27
- if job.has_key?("cattr")
28
- job["cattr"].merge!(attrs)
29
- else
30
- job["cattr"] = attrs
29
+ @cattrs.each do |(key, strklass)|
30
+ if !job.has_key?(key)
31
+ attrs = strklass.constantize.attributes
32
+ # Retries can push the job N times, we don't
33
+ # want retries to reset cattr. #5692, #5090
34
+ job[key] = attrs if attrs.any?
31
35
  end
32
36
  end
33
37
  yield
@@ -37,26 +41,54 @@ module Sidekiq
37
41
  class Load
38
42
  include Sidekiq::ServerMiddleware
39
43
 
40
- def initialize(cattr)
41
- @klass = cattr
44
+ def initialize(cattrs)
45
+ @cattrs = cattrs
42
46
  end
43
47
 
44
48
  def call(_, job, _, &block)
45
- if job.has_key?("cattr")
46
- @klass.set(job["cattr"], &block)
47
- else
48
- yield
49
+ cattrs_to_reset = []
50
+
51
+ @cattrs.each do |(key, strklass)|
52
+ if job.has_key?(key)
53
+ constklass = strklass.constantize
54
+ cattrs_to_reset << constklass
55
+
56
+ job[key].each do |(attribute, value)|
57
+ constklass.public_send("#{attribute}=", value)
58
+ end
59
+ end
49
60
  end
61
+
62
+ yield
63
+ ensure
64
+ cattrs_to_reset.each(&:reset)
50
65
  end
51
66
  end
52
67
 
53
- def self.persist(klass)
54
- Sidekiq.configure_client do |config|
55
- config.client_middleware.add Save, klass
68
+ class << self
69
+ def persist(klass_or_array, config = Sidekiq.default_configuration)
70
+ cattrs = build_cattrs_hash(klass_or_array)
71
+
72
+ config.client_middleware.add Save, cattrs
73
+ config.server_middleware.add Load, cattrs
56
74
  end
57
- Sidekiq.configure_server do |config|
58
- config.client_middleware.add Save, klass
59
- config.server_middleware.add Load, klass
75
+
76
+ private
77
+
78
+ def build_cattrs_hash(klass_or_array)
79
+ if klass_or_array.is_a?(Array)
80
+ {}.tap do |hash|
81
+ klass_or_array.each_with_index do |klass, index|
82
+ hash[key_at(index)] = klass.to_s
83
+ end
84
+ end
85
+ else
86
+ {key_at(0) => klass_or_array.to_s}
87
+ end
88
+ end
89
+
90
+ def key_at(index)
91
+ (index == 0) ? "cattr" : "cattr_#{index}"
60
92
  end
61
93
  end
62
94
  end
@@ -16,8 +16,6 @@ class Sidekiq::Monitor
16
16
  return
17
17
  end
18
18
  send(section)
19
- rescue => e
20
- abort "Couldn't get status: #{e}"
21
19
  end
22
20
 
23
21
  def all
@@ -49,10 +47,25 @@ class Sidekiq::Monitor
49
47
  def processes
50
48
  puts "---- Processes (#{process_set.size}) ----"
51
49
  process_set.each_with_index do |process, index|
50
+ # Keep compatibility with legacy versions since we don't want to break sidekiqmon during rolling upgrades or downgrades.
51
+ #
52
+ # Before:
53
+ # ["default", "critical"]
54
+ #
55
+ # After:
56
+ # {"default" => 1, "critical" => 10}
57
+ queues =
58
+ if process["weights"]
59
+ process["weights"].sort_by { |queue| queue[0] }.map { |capsule| capsule.map { |name, weight| (weight > 0) ? "#{name}: #{weight}" : name }.join(", ") }
60
+ else
61
+ process["queues"].sort
62
+ end
63
+
52
64
  puts "#{process["identity"]} #{tags_for(process)}"
53
65
  puts " Started: #{Time.at(process["started_at"])} (#{time_ago(process["started_at"])})"
54
66
  puts " Threads: #{process["concurrency"]} (#{process["busy"]} busy)"
55
- puts " Queues: #{split_multiline(process["queues"].sort, pad: 11)}"
67
+ puts " Queues: #{split_multiline(queues, pad: 11)}"
68
+ puts " Version: #{process["version"] || "Unknown"}" if process["version"] != Sidekiq::VERSION
56
69
  puts "" unless (index + 1) == process_set.size
57
70
  end
58
71
  end
@@ -101,7 +114,7 @@ class Sidekiq::Monitor
101
114
  tags = [
102
115
  process["tag"],
103
116
  process["labels"],
104
- (process["quiet"] == "true" ? "quiet" : nil)
117
+ ((process["quiet"] == "true") ? "quiet" : nil)
105
118
  ].flatten.compact
106
119
  tags.any? ? "[#{tags.join("] [")}]" : nil
107
120
  end
@@ -3,7 +3,7 @@
3
3
  module Sidekiq
4
4
  module Paginator
5
5
  def page(key, pageidx = 1, page_size = 25, opts = nil)
6
- current_page = pageidx.to_i < 1 ? 1 : pageidx.to_i
6
+ current_page = (pageidx.to_i < 1) ? 1 : pageidx.to_i
7
7
  pageidx = current_page - 1
8
8
  total_size = 0
9
9
  items = []
@@ -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, withscores: true)
22
+ transaction.zrange(key, starting, ending, "REV", "withscores")
23
23
  else
24
- transaction.zrange(key, starting, ending, withscores: true)
24
+ transaction.zrange(key, starting, ending, "withscores")
25
25
  end
26
26
  }
27
27
  [current_page, total_size, items]
@@ -43,5 +43,13 @@ module Sidekiq
43
43
  end
44
44
  end
45
45
  end
46
+
47
+ def page_items(items, pageidx = 1, page_size = 25)
48
+ current_page = (pageidx.to_i < 1) ? 1 : pageidx.to_i
49
+ pageidx = current_page - 1
50
+ starting = pageidx * page_size
51
+ items = items.to_a
52
+ [current_page, items.size, items[starting, page_size]]
53
+ end
46
54
  end
47
55
  end
@@ -26,18 +26,18 @@ module Sidekiq
26
26
 
27
27
  attr_reader :thread
28
28
  attr_reader :job
29
+ attr_reader :capsule
29
30
 
30
- def initialize(options, &block)
31
+ def initialize(capsule, &block)
32
+ @config = @capsule = capsule
31
33
  @callback = block
32
34
  @down = false
33
35
  @done = false
34
36
  @job = nil
35
37
  @thread = nil
36
- @config = options
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(options)
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,12 +59,16 @@ 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
73
  @callback.call(self)
70
74
  rescue Sidekiq::Shutdown
@@ -80,7 +84,7 @@ module Sidekiq
80
84
  end
81
85
 
82
86
  def get_one
83
- uow = @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
@@ -129,7 +133,7 @@ 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"])
136
+ klass = Object.const_get(job_hash["class"])
133
137
  inst = klass.new
134
138
  inst.jid = job_hash["jid"]
135
139
  @retrier.local(inst, jobstr, queue) do
@@ -142,6 +146,11 @@ module Sidekiq
142
146
  end
143
147
  end
144
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
+
145
154
  def process(uow)
146
155
  jobstr = uow.job
147
156
  queue = uow.queue_name
@@ -153,47 +162,46 @@ module Sidekiq
153
162
  rescue => ex
154
163
  handle_exception(ex, {context: "Invalid JSON for job", jobstr: jobstr})
155
164
  now = Time.now.to_f
156
- config.redis do |conn|
165
+ redis do |conn|
157
166
  conn.multi do |xa|
158
167
  xa.zadd("dead", now.to_s, jobstr)
159
- xa.zremrangebyscore("dead", "-inf", now - config[:dead_timeout_in_seconds])
160
- xa.zremrangebyrank("dead", 0, - config[:dead_max_jobs])
168
+ xa.zremrangebyscore("dead", "-inf", now - @capsule.config[:dead_timeout_in_seconds])
169
+ xa.zremrangebyrank("dead", 0, - @capsule.config[:dead_max_jobs])
161
170
  end
162
171
  end
163
172
  return uow.acknowledge
164
173
  end
165
174
 
166
175
  ack = false
167
- begin
168
- dispatch(job_hash, queue, jobstr) do |inst|
169
- @config.server_middleware.invoke(inst, job_hash, queue) do
170
- execute_job(inst, job_hash["args"])
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
171
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
172
201
  end
173
- ack = true
174
- rescue Sidekiq::Shutdown
175
- # Had to force kill this job because it didn't finish
176
- # within the timeout. Don't acknowledge the work since
177
- # we didn't properly finish it.
178
- rescue Sidekiq::JobRetry::Handled => h
179
- # this is the common case: job raised error and Sidekiq::JobRetry::Handled
180
- # signals that we created a retry successfully. We can acknowlege the job.
181
- ack = true
182
- e = h.cause || h
183
- handle_exception(e, {context: "Job raised exception", job: job_hash})
184
- raise e
185
- rescue Exception => ex
186
- # Unexpected error! This is very bad and indicates an exception that got past
187
- # the retry subsystem (e.g. network partition). We won't acknowledge the job
188
- # so it can be rescued when using Sidekiq Pro.
189
- handle_exception(ex, {context: "Internal exception!", job: job_hash, jobstr: jobstr})
190
- raise ex
191
202
  ensure
192
203
  if ack
193
- # We don't want a shutdown signal to interrupt job acknowledgment.
194
- Thread.handle_interrupt(Sidekiq::Shutdown => :never) do
195
- uow.acknowledge
196
- end
204
+ uow.acknowledge
197
205
  end
198
206
  end
199
207
  end
@@ -269,18 +277,5 @@ module Sidekiq
269
277
  PROCESSED.incr
270
278
  end
271
279
  end
272
-
273
- def constantize(str)
274
- return Object.const_get(str) unless str.include?("::")
275
-
276
- names = str.split("::")
277
- names.shift if names.empty? || names.first.empty?
278
-
279
- names.inject(Object) do |constant, name|
280
- # the false flag limits search for name to under the constant namespace
281
- # which mimics Rails' behaviour
282
- constant.const_get(name, false)
283
- end
284
- end
285
280
  end
286
281
  end
data/lib/sidekiq/rails.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "sidekiq/job"
4
+ require "rails"
4
5
 
5
6
  module Sidekiq
6
7
  class Rails < ::Rails::Engine
@@ -10,7 +11,8 @@ module Sidekiq
10
11
  end
11
12
 
12
13
  def call
13
- @app.reloader.wrap do
14
+ params = (::Rails::VERSION::STRING >= "7.1") ? {source: "job.sidekiq"} : {}
15
+ @app.reloader.wrap(**params) do
14
16
  yield
15
17
  end
16
18
  end
@@ -22,7 +24,7 @@ module Sidekiq
22
24
 
23
25
  # By including the Options module, we allow AJs to directly control sidekiq features
24
26
  # 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
27
+ # AJ retries don't show up in the Sidekiq UI Retries tab, don't save any error data, can't be
26
28
  # manually retried, don't automatically die, etc.
27
29
  #
28
30
  # class SomeJob < ActiveJob::Base
@@ -37,24 +39,12 @@ module Sidekiq
37
39
  end
38
40
  end
39
41
 
40
- initializer "sidekiq.rails_logger" do
42
+ initializer "sidekiq.backtrace_cleaner" do
41
43
  Sidekiq.configure_server do |config|
42
- # This is the integration code necessary so that if a job uses `Rails.logger.info "Hello"`,
43
- # it will appear in the Sidekiq console with all of the job context. See #5021 and
44
- # https://github.com/rails/rails/blob/b5f2b550f69a99336482739000c58e4e04e033aa/railties/lib/rails/commands/server/server_command.rb#L82-L84
45
- unless ::Rails.logger == config.logger || ::ActiveSupport::Logger.logger_outputs_to?(::Rails.logger, $stdout)
46
- ::Rails.logger.extend(::ActiveSupport::Logger.broadcast(config.logger))
47
- end
44
+ config[:backtrace_cleaner] = ->(backtrace) { ::Rails.backtrace_cleaner.clean(backtrace) }
48
45
  end
49
46
  end
50
47
 
51
- config.before_configuration do
52
- dep = ActiveSupport::Deprecation.new("7.0", "Sidekiq")
53
- dep.deprecate_methods(Sidekiq.singleton_class,
54
- default_worker_options: :default_job_options,
55
- "default_worker_options=": :default_job_options=)
56
- end
57
-
58
48
  # This hook happens after all initializers are run, just before returning
59
49
  # from config/environment.rb back to sidekiq/cli.rb.
60
50
  #
@@ -62,6 +52,16 @@ module Sidekiq
62
52
  config.after_initialize do
63
53
  Sidekiq.configure_server do |config|
64
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
65
  end
66
66
  end
67
67
  end
@@ -1,15 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "connection_pool"
3
+ require "set"
4
4
  require "redis_client"
5
5
  require "redis_client/decorator"
6
- require "uri"
7
6
 
8
7
  module Sidekiq
9
8
  class RedisClientAdapter
10
9
  BaseError = RedisClient::Error
11
10
  CommandError = RedisClient::CommandError
12
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
+
13
15
  module CompatMethods
14
16
  def info
15
17
  @client.call("INFO") { |i| i.lines(chomp: true).map { |l| l.split(":", 2) }.select { |l| l.size == 2 }.to_h }
@@ -19,30 +21,28 @@ module Sidekiq
19
21
  @client.call("EVALSHA", sha, keys.size, *keys, *argv)
20
22
  end
21
23
 
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
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]
29
33
 
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 }
34
+ USED_COMMANDS.each do |name|
35
+ define_method(name) do |*args|
36
+ @client.call(name, *args)
37
+ end
41
38
  end
42
39
 
43
40
  private
44
41
 
42
+ # this allows us to use methods like `conn.hmset(...)` instead of having to use
43
+ # redis-client's native `conn.call("hmset", ...)`
45
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
46
  @client.call(*args, *block)
47
47
  end
48
48
  ruby2_keywords :method_missing if respond_to?(:ruby2_keywords, true)
@@ -55,47 +55,8 @@ module Sidekiq
55
55
  CompatClient = RedisClient::Decorator.create(CompatMethods)
56
56
 
57
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
58
+ def config
59
+ @client.config
99
60
  end
100
61
  end
101
62
 
@@ -118,9 +79,7 @@ module Sidekiq
118
79
  opts = options.dup
119
80
 
120
81
  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)
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."
124
83
  end
125
84
 
126
85
  opts.delete(:size)
@@ -150,5 +109,3 @@ module Sidekiq
150
109
  end
151
110
  end
152
111
  end
153
-
154
- Sidekiq::RedisConnection.adapter = Sidekiq::RedisClientAdapter