sidekiq 6.5.9 → 7.0.9

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 (104) hide show
  1. checksums.yaml +4 -4
  2. data/Changes.md +93 -17
  3. data/README.md +40 -32
  4. data/bin/sidekiq +3 -8
  5. data/bin/sidekiqload +186 -118
  6. data/bin/sidekiqmon +3 -0
  7. data/lib/sidekiq/api.rb +84 -121
  8. data/lib/sidekiq/capsule.rb +127 -0
  9. data/lib/sidekiq/cli.rb +55 -74
  10. data/lib/sidekiq/client.rb +29 -16
  11. data/lib/sidekiq/component.rb +3 -0
  12. data/lib/sidekiq/config.rb +270 -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 +375 -10
  17. data/lib/sidekiq/job_logger.rb +2 -2
  18. data/lib/sidekiq/job_retry.rb +13 -12
  19. data/lib/sidekiq/job_util.rb +48 -14
  20. data/lib/sidekiq/launcher.rb +65 -61
  21. data/lib/sidekiq/logger.rb +1 -26
  22. data/lib/sidekiq/manager.rb +9 -11
  23. data/lib/sidekiq/metrics/query.rb +3 -3
  24. data/lib/sidekiq/metrics/shared.rb +7 -6
  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 +8 -15
  28. data/lib/sidekiq/monitor.rb +16 -3
  29. data/lib/sidekiq/processor.rb +21 -27
  30. data/lib/sidekiq/rails.rb +4 -9
  31. data/lib/sidekiq/redis_client_adapter.rb +8 -47
  32. data/lib/sidekiq/redis_connection.rb +11 -111
  33. data/lib/sidekiq/scheduled.rb +20 -21
  34. data/lib/sidekiq/testing.rb +5 -33
  35. data/lib/sidekiq/transaction_aware_client.rb +4 -5
  36. data/lib/sidekiq/version.rb +2 -1
  37. data/lib/sidekiq/web/application.rb +21 -6
  38. data/lib/sidekiq/web/csrf_protection.rb +1 -1
  39. data/lib/sidekiq/web/helpers.rb +16 -15
  40. data/lib/sidekiq/web.rb +6 -17
  41. data/lib/sidekiq/worker_compatibility_alias.rb +13 -0
  42. data/lib/sidekiq.rb +76 -274
  43. data/sidekiq.gemspec +21 -10
  44. data/web/assets/javascripts/application.js +18 -0
  45. data/web/assets/javascripts/base-charts.js +106 -0
  46. data/web/assets/javascripts/dashboard-charts.js +166 -0
  47. data/web/assets/javascripts/dashboard.js +3 -223
  48. data/web/assets/javascripts/metrics.js +117 -115
  49. data/web/assets/stylesheets/application-dark.css +4 -0
  50. data/web/assets/stylesheets/application-rtl.css +2 -91
  51. data/web/assets/stylesheets/application.css +23 -298
  52. data/web/locales/ar.yml +70 -70
  53. data/web/locales/cs.yml +62 -62
  54. data/web/locales/da.yml +60 -53
  55. data/web/locales/de.yml +65 -65
  56. data/web/locales/el.yml +2 -7
  57. data/web/locales/en.yml +76 -70
  58. data/web/locales/es.yml +68 -68
  59. data/web/locales/fa.yml +65 -65
  60. data/web/locales/fr.yml +67 -67
  61. data/web/locales/gd.yml +99 -0
  62. data/web/locales/he.yml +65 -64
  63. data/web/locales/hi.yml +59 -59
  64. data/web/locales/it.yml +53 -53
  65. data/web/locales/ja.yml +67 -69
  66. data/web/locales/ko.yml +52 -52
  67. data/web/locales/lt.yml +66 -66
  68. data/web/locales/nb.yml +61 -61
  69. data/web/locales/nl.yml +52 -52
  70. data/web/locales/pl.yml +45 -45
  71. data/web/locales/pt-br.yml +59 -69
  72. data/web/locales/pt.yml +51 -51
  73. data/web/locales/ru.yml +67 -66
  74. data/web/locales/sv.yml +53 -53
  75. data/web/locales/ta.yml +60 -60
  76. data/web/locales/uk.yml +62 -61
  77. data/web/locales/ur.yml +64 -64
  78. data/web/locales/vi.yml +67 -67
  79. data/web/locales/zh-cn.yml +20 -18
  80. data/web/locales/zh-tw.yml +10 -1
  81. data/web/views/_footer.erb +5 -2
  82. data/web/views/_job_info.erb +18 -2
  83. data/web/views/_metrics_period_select.erb +12 -0
  84. data/web/views/_paging.erb +2 -0
  85. data/web/views/_poll_link.erb +1 -1
  86. data/web/views/busy.erb +39 -28
  87. data/web/views/dashboard.erb +36 -5
  88. data/web/views/metrics.erb +33 -20
  89. data/web/views/metrics_for_job.erb +24 -43
  90. data/web/views/morgue.erb +5 -9
  91. data/web/views/queue.erb +10 -14
  92. data/web/views/queues.erb +3 -1
  93. data/web/views/retries.erb +5 -9
  94. data/web/views/scheduled.erb +12 -13
  95. metadata +51 -39
  96. data/lib/sidekiq/delay.rb +0 -43
  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/metrics/deploy.rb +0 -47
  102. data/lib/sidekiq/worker.rb +0 -370
  103. data/web/assets/javascripts/graph.js +0 -16
  104. /data/{LICENSE → LICENSE.txt} +0 -0
@@ -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,9 @@ module Sidekiq
142
146
  end
143
147
  end
144
148
 
149
+ IGNORE_SHUTDOWN_INTERRUPTS = {Sidekiq::Shutdown => :never}
150
+ private_constant :IGNORE_SHUTDOWN_INTERRUPTS
151
+
145
152
  def process(uow)
146
153
  jobstr = uow.job
147
154
  queue = uow.queue_name
@@ -153,11 +160,11 @@ module Sidekiq
153
160
  rescue => ex
154
161
  handle_exception(ex, {context: "Invalid JSON for job", jobstr: jobstr})
155
162
  now = Time.now.to_f
156
- config.redis do |conn|
163
+ redis do |conn|
157
164
  conn.multi do |xa|
158
165
  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])
166
+ xa.zremrangebyscore("dead", "-inf", now - @capsule.config[:dead_timeout_in_seconds])
167
+ xa.zremrangebyrank("dead", 0, - @capsule.config[:dead_max_jobs])
161
168
  end
162
169
  end
163
170
  return uow.acknowledge
@@ -166,7 +173,7 @@ module Sidekiq
166
173
  ack = false
167
174
  begin
168
175
  dispatch(job_hash, queue, jobstr) do |inst|
169
- @config.server_middleware.invoke(inst, job_hash, queue) do
176
+ config.server_middleware.invoke(inst, job_hash, queue) do
170
177
  execute_job(inst, job_hash["args"])
171
178
  end
172
179
  end
@@ -191,7 +198,7 @@ module Sidekiq
191
198
  ensure
192
199
  if ack
193
200
  # We don't want a shutdown signal to interrupt job acknowledgment.
194
- Thread.handle_interrupt(Sidekiq::Shutdown => :never) do
201
+ Thread.handle_interrupt(IGNORE_SHUTDOWN_INTERRUPTS) do
195
202
  uow.acknowledge
196
203
  end
197
204
  end
@@ -269,18 +276,5 @@ module Sidekiq
269
276
  PROCESSED.incr
270
277
  end
271
278
  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
279
  end
286
280
  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
@@ -48,13 +50,6 @@ module Sidekiq
48
50
  end
49
51
  end
50
52
 
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
53
  # This hook happens after all initializers are run, just before returning
59
54
  # from config/environment.rb back to sidekiq/cli.rb.
60
55
  #
@@ -1,9 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "connection_pool"
4
3
  require "redis_client"
5
4
  require "redis_client/decorator"
6
- require "uri"
7
5
 
8
6
  module Sidekiq
9
7
  class RedisClientAdapter
@@ -11,37 +9,20 @@ module Sidekiq
11
9
  CommandError = RedisClient::CommandError
12
10
 
13
11
  module CompatMethods
12
+ # TODO Deprecate and remove this
14
13
  def info
15
14
  @client.call("INFO") { |i| i.lines(chomp: true).map { |l| l.split(":", 2) }.select { |l| l.size == 2 }.to_h }
16
15
  end
17
16
 
17
+ # TODO Deprecate and remove this
18
18
  def evalsha(sha, keys, argv)
19
19
  @client.call("EVALSHA", sha, keys.size, *keys, *argv)
20
20
  end
21
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
22
  private
44
23
 
24
+ # this allows us to use methods like `conn.hmset(...)` instead of having to use
25
+ # redis-client's native `conn.call("hmset", ...)`
45
26
  def method_missing(*args, &block)
46
27
  @client.call(*args, *block)
47
28
  end
@@ -55,25 +36,8 @@ module Sidekiq
55
36
  CompatClient = RedisClient::Decorator.create(CompatMethods)
56
37
 
57
38
  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
39
+ def config
40
+ @client.config
77
41
  end
78
42
 
79
43
  def message
@@ -118,9 +82,8 @@ module Sidekiq
118
82
  opts = options.dup
119
83
 
120
84
  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)
85
+ raise ArgumentError, "Your Redis configuration uses the namespace '#{opts[:namespace]}' but this feature isn't supported by redis-client. " \
86
+ "Either use the redis adapter or remove the namespace."
124
87
  end
125
88
 
126
89
  opts.delete(:size)
@@ -150,5 +113,3 @@ module Sidekiq
150
113
  end
151
114
  end
152
115
  end
153
-
154
- Sidekiq::RedisConnection.adapter = Sidekiq::RedisClientAdapter
@@ -1,128 +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
8
  module RedisConnection
9
- class RedisAdapter
10
- BaseError = Redis::BaseError
11
- CommandError = Redis::CommandError
12
-
13
- def initialize(options)
14
- warn("Usage of the 'redis' gem within Sidekiq itself is deprecated, Sidekiq 7.0 will only use the new, simpler 'redis-client' gem", caller) if ENV["SIDEKIQ_REDIS_CLIENT"] == "1"
15
- @options = options
16
- end
17
-
18
- def new_client
19
- namespace = @options[:namespace]
20
-
21
- client = Redis.new client_opts(@options)
22
- if namespace
23
- begin
24
- require "redis/namespace"
25
- Redis::Namespace.new(namespace, redis: client)
26
- rescue LoadError
27
- Sidekiq.logger.error("Your Redis configuration uses the namespace '#{namespace}' but the redis-namespace gem is not included in the Gemfile." \
28
- "Add the gem to your Gemfile to continue using a namespace. Otherwise, remove the namespace parameter.")
29
- exit(-127)
30
- end
31
- else
32
- client
33
- end
34
- end
35
-
36
- private
37
-
38
- def client_opts(options)
39
- opts = options.dup
40
- if opts[:namespace]
41
- opts.delete(:namespace)
42
- end
43
-
44
- if opts[:network_timeout]
45
- opts[:timeout] = opts[:network_timeout]
46
- opts.delete(:network_timeout)
47
- end
48
-
49
- # Issue #3303, redis-rb will silently retry an operation.
50
- # This can lead to duplicate jobs if Sidekiq::Client's LPUSH
51
- # is performed twice but I believe this is much, much rarer
52
- # than the reconnect silently fixing a problem; we keep it
53
- # on by default.
54
- opts[:reconnect_attempts] ||= 1
55
-
56
- opts
57
- end
58
- end
59
-
60
- @adapter = RedisAdapter
61
-
62
9
  class << self
63
- attr_reader :adapter
64
-
65
- # RedisConnection.adapter = :redis
66
- # RedisConnection.adapter = :redis_client
67
- def adapter=(adapter)
68
- raise "no" if adapter == self
69
- result = case adapter
70
- when :redis
71
- RedisAdapter
72
- when Class
73
- adapter
74
- else
75
- require "sidekiq/#{adapter}_adapter"
76
- nil
77
- end
78
- @adapter = result if result
79
- end
80
-
81
10
  def create(options = {})
82
11
  symbolized_options = options.transform_keys(&:to_sym)
12
+ symbolized_options[:url] ||= determine_redis_provider
83
13
 
84
- if !symbolized_options[:url] && (u = determine_redis_provider)
85
- symbolized_options[:url] = u
86
- end
87
-
88
- size = if symbolized_options[:size]
89
- symbolized_options[:size]
90
- elsif Sidekiq.server?
91
- # Give ourselves plenty of connections. pool is lazy
92
- # so we won't create them until we need them.
93
- Sidekiq[:concurrency] + 5
94
- elsif ENV["RAILS_MAX_THREADS"]
95
- Integer(ENV["RAILS_MAX_THREADS"])
96
- else
97
- 5
98
- end
99
-
100
- verify_sizing(size, Sidekiq[:concurrency]) if Sidekiq.server?
14
+ logger = symbolized_options.delete(:logger)
15
+ logger&.info { "Sidekiq #{Sidekiq::VERSION} connecting to Redis with options #{scrub(symbolized_options)}" }
101
16
 
102
- pool_timeout = symbolized_options[:pool_timeout] || 1
103
- log_info(symbolized_options)
17
+ size = symbolized_options.delete(:size) || 5
18
+ pool_timeout = symbolized_options.delete(:pool_timeout) || 1
19
+ pool_name = symbolized_options.delete(:pool_name)
104
20
 
105
- redis_config = adapter.new(symbolized_options)
106
- ConnectionPool.new(timeout: pool_timeout, size: size) do
21
+ redis_config = Sidekiq::RedisClientAdapter.new(symbolized_options)
22
+ ConnectionPool.new(timeout: pool_timeout, size: size, name: pool_name) do
107
23
  redis_config.new_client
108
24
  end
109
25
  end
110
26
 
111
27
  private
112
28
 
113
- # Sidekiq needs many concurrent Redis connections.
114
- #
115
- # We need a connection for each Processor.
116
- # We need a connection for Pro's real-time change listener
117
- # We need a connection to various features to call Redis every few seconds:
118
- # - the process heartbeat.
119
- # - enterprise's leader election
120
- # - enterprise's cron support
121
- def verify_sizing(size, concurrency)
122
- raise ArgumentError, "Your Redis connection pool is too small for Sidekiq. Your pool has #{size} connections but must have at least #{concurrency + 2}" if size < (concurrency + 2)
123
- end
124
-
125
- def log_info(options)
29
+ def scrub(options)
126
30
  redacted = "REDACTED"
127
31
 
128
32
  # Deep clone so we can muck with these options all we want and exclude
@@ -140,11 +44,7 @@ module Sidekiq
140
44
  scrubbed_options[:sentinels]&.each do |sentinel|
141
45
  sentinel[:password] = redacted if sentinel[:password]
142
46
  end
143
- if Sidekiq.server?
144
- Sidekiq.logger.info("Booting Sidekiq #{Sidekiq::VERSION} with #{adapter.name} options #{scrubbed_options}")
145
- else
146
- Sidekiq.logger.debug("#{Sidekiq::NAME} client with #{adapter.name} options #{scrubbed_options}")
147
- end
47
+ scrubbed_options
148
48
  end
149
49
 
150
50
  def determine_redis_provider
@@ -8,6 +8,8 @@ module Sidekiq
8
8
  SETS = %w[retry schedule]
9
9
 
10
10
  class Enq
11
+ include Sidekiq::Component
12
+
11
13
  LUA_ZPOPBYSCORE = <<~LUA
12
14
  local key, now = KEYS[1], ARGV[1]
13
15
  local jobs = redis.call("zrangebyscore", key, "-inf", now, "limit", 0, 1)
@@ -17,7 +19,9 @@ module Sidekiq
17
19
  end
18
20
  LUA
19
21
 
20
- def initialize
22
+ def initialize(container)
23
+ @config = container
24
+ @client = Sidekiq::Client.new(config: container)
21
25
  @done = false
22
26
  @lua_zpopbyscore_sha = nil
23
27
  end
@@ -25,15 +29,15 @@ module Sidekiq
25
29
  def enqueue_jobs(sorted_sets = SETS)
26
30
  # A job's "score" in Redis is the time at which it should be processed.
27
31
  # Just check Redis for the set of jobs with a timestamp before now.
28
- Sidekiq.redis do |conn|
32
+ redis do |conn|
29
33
  sorted_sets.each do |sorted_set|
30
34
  # Get next item in the queue with score (time to execute) <= now.
31
35
  # We need to go through the list one at a time to reduce the risk of something
32
36
  # going wrong between the time jobs are popped from the scheduled queue and when
33
37
  # they are pushed onto a work queue and losing the jobs.
34
38
  while !@done && (job = zpopbyscore(conn, keys: [sorted_set], argv: [Time.now.to_f.to_s]))
35
- Sidekiq::Client.push(Sidekiq.load_json(job))
36
- Sidekiq.logger.debug { "enqueued #{sorted_set}: #{job}" }
39
+ @client.push(Sidekiq.load_json(job))
40
+ logger.debug { "enqueued #{sorted_set}: #{job}" }
37
41
  end
38
42
  end
39
43
  end
@@ -47,12 +51,11 @@ module Sidekiq
47
51
 
48
52
  def zpopbyscore(conn, keys: nil, argv: nil)
49
53
  if @lua_zpopbyscore_sha.nil?
50
- raw_conn = conn.respond_to?(:redis) ? conn.redis : conn
51
- @lua_zpopbyscore_sha = raw_conn.script(:load, LUA_ZPOPBYSCORE)
54
+ @lua_zpopbyscore_sha = conn.script(:load, LUA_ZPOPBYSCORE)
52
55
  end
53
56
 
54
- conn.evalsha(@lua_zpopbyscore_sha, keys, argv)
55
- rescue RedisConnection.adapter::CommandError => e
57
+ conn.call("EVALSHA", @lua_zpopbyscore_sha, keys.size, *keys, *argv)
58
+ rescue RedisClient::CommandError => e
56
59
  raise unless e.message.start_with?("NOSCRIPT")
57
60
 
58
61
  @lua_zpopbyscore_sha = nil
@@ -70,9 +73,9 @@ module Sidekiq
70
73
 
71
74
  INITIAL_WAIT = 10
72
75
 
73
- def initialize(options)
74
- @config = options
75
- @enq = (options[:scheduled_enq] || Sidekiq::Scheduled::Enq).new
76
+ def initialize(config)
77
+ @config = config
78
+ @enq = (config[:scheduled_enq] || Sidekiq::Scheduled::Enq).new(config)
76
79
  @sleeper = ConnectionPool::TimedStack.new
77
80
  @done = false
78
81
  @thread = nil
@@ -82,14 +85,10 @@ module Sidekiq
82
85
  # Shut down this instance, will pause until the thread is dead.
83
86
  def terminate
84
87
  @done = true
85
- @enq.terminate if @enq.respond_to?(:terminate)
88
+ @enq.terminate
86
89
 
87
- if @thread
88
- t = @thread
89
- @thread = nil
90
- @sleeper << 0
91
- t.value
92
- end
90
+ @sleeper << 0
91
+ @thread&.value
93
92
  end
94
93
 
95
94
  def start
@@ -194,11 +193,11 @@ module Sidekiq
194
193
  # should never depend on sidekiq/api.
195
194
  def cleanup
196
195
  # dont run cleanup more than once per minute
197
- return 0 unless Sidekiq.redis { |conn| conn.set("process_cleanup", "1", nx: true, ex: 60) }
196
+ return 0 unless redis { |conn| conn.set("process_cleanup", "1", nx: true, ex: 60) }
198
197
 
199
198
  count = 0
200
- Sidekiq.redis do |conn|
201
- procs = conn.sscan_each("processes").to_a
199
+ redis do |conn|
200
+ procs = conn.sscan("processes").to_a
202
201
  heartbeats = conn.pipelined { |pipeline|
203
202
  procs.each do |key|
204
203
  pipeline.hget(key, "info")
@@ -51,19 +51,10 @@ module Sidekiq
51
51
  end
52
52
 
53
53
  def server_middleware
54
- @server_chain ||= Middleware::Chain.new
54
+ @server_chain ||= Middleware::Chain.new(Sidekiq.default_configuration)
55
55
  yield @server_chain if block_given?
56
56
  @server_chain
57
57
  end
58
-
59
- def constantize(str)
60
- names = str.split("::")
61
- names.shift if names.empty? || names.first.empty?
62
-
63
- names.inject(Object) do |constant, name|
64
- constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
65
- end
66
- end
67
58
  end
68
59
  end
69
60
 
@@ -83,7 +74,7 @@ module Sidekiq
83
74
  true
84
75
  elsif Sidekiq::Testing.inline?
85
76
  payloads.each do |job|
86
- klass = Sidekiq::Testing.constantize(job["class"])
77
+ klass = Object.const_get(job["class"])
87
78
  job["id"] ||= SecureRandom.hex(12)
88
79
  job_hash = Sidekiq.load_json(Sidekiq.dump_json(job))
89
80
  klass.process_job(job_hash)
@@ -218,25 +209,9 @@ module Sidekiq
218
209
  # assert_equal 1, HardJob.jobs.size
219
210
  # assert_equal :something, HardJob.jobs[0]['args'][0]
220
211
  #
221
- # assert_equal 0, Sidekiq::Extensions::DelayedMailer.jobs.size
222
- # MyMailer.delay.send_welcome_email('foo@example.com')
223
- # assert_equal 1, Sidekiq::Extensions::DelayedMailer.jobs.size
224
- #
225
212
  # You can also clear and drain all job types:
226
213
  #
227
- # assert_equal 0, Sidekiq::Extensions::DelayedMailer.jobs.size
228
- # assert_equal 0, Sidekiq::Extensions::DelayedModel.jobs.size
229
- #
230
- # MyMailer.delay.send_welcome_email('foo@example.com')
231
- # MyModel.delay.do_something_hard
232
- #
233
- # assert_equal 1, Sidekiq::Extensions::DelayedMailer.jobs.size
234
- # assert_equal 1, Sidekiq::Extensions::DelayedModel.jobs.size
235
- #
236
- # Sidekiq::Worker.clear_all # or .drain_all
237
- #
238
- # assert_equal 0, Sidekiq::Extensions::DelayedMailer.jobs.size
239
- # assert_equal 0, Sidekiq::Extensions::DelayedModel.jobs.size
214
+ # Sidekiq::Job.clear_all # or .drain_all
240
215
  #
241
216
  # This can be useful to make sure jobs don't linger between tests:
242
217
  #
@@ -318,7 +293,7 @@ module Sidekiq
318
293
  job_classes = jobs.map { |job| job["class"] }.uniq
319
294
 
320
295
  job_classes.each do |job_class|
321
- Sidekiq::Testing.constantize(job_class).drain
296
+ Object.const_get(job_class).drain
322
297
  end
323
298
  end
324
299
  end
@@ -329,13 +304,10 @@ module Sidekiq
329
304
  def jobs_for(klass)
330
305
  jobs.select do |job|
331
306
  marshalled = job["args"][0]
332
- marshalled.index(klass.to_s) && YAML.load(marshalled)[0] == klass
307
+ marshalled.index(klass.to_s) && YAML.safe_load(marshalled)[0] == klass
333
308
  end
334
309
  end
335
310
  end
336
-
337
- Sidekiq::Extensions::DelayedMailer.extend(TestingExtensions) if defined?(Sidekiq::Extensions::DelayedMailer)
338
- Sidekiq::Extensions::DelayedModel.extend(TestingExtensions) if defined?(Sidekiq::Extensions::DelayedModel)
339
311
  end
340
312
 
341
313
  if defined?(::Rails) && Rails.respond_to?(:env) && !Rails.env.test? && !$TESTING
@@ -5,8 +5,8 @@ require "sidekiq/client"
5
5
 
6
6
  module Sidekiq
7
7
  class TransactionAwareClient
8
- def initialize(redis_pool)
9
- @redis_client = Client.new(redis_pool)
8
+ def initialize(pool: nil, config: nil)
9
+ @redis_client = Client.new(pool: pool, config: config)
10
10
  end
11
11
 
12
12
  def push(item)
@@ -34,11 +34,10 @@ module Sidekiq
34
34
  begin
35
35
  require "after_commit_everywhere"
36
36
  rescue LoadError
37
- Sidekiq.logger.error("You need to add after_commit_everywhere to your Gemfile to use Sidekiq's transactional client")
38
- raise
37
+ raise %q(You need to add `gem "after_commit_everywhere"` to your Gemfile to use Sidekiq's transactional client)
39
38
  end
40
39
 
41
- default_job_options["client_class"] = Sidekiq::TransactionAwareClient
40
+ Sidekiq.default_job_options["client_class"] = Sidekiq::TransactionAwareClient
42
41
  Sidekiq::JobUtil::TRANSIENT_ATTRIBUTES << "client_class"
43
42
  true
44
43
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sidekiq
4
- VERSION = "6.5.9"
4
+ VERSION = "7.0.9"
5
+ MAJOR = 7
5
6
  end
@@ -20,6 +20,12 @@ module Sidekiq
20
20
  "worker-src 'self'",
21
21
  "base-uri 'self'"
22
22
  ].join("; ").freeze
23
+ METRICS_PERIODS = {
24
+ "1h" => 60,
25
+ "2h" => 120,
26
+ "4h" => 240,
27
+ "8h" => 480
28
+ }
23
29
 
24
30
  def initialize(klass)
25
31
  @klass = klass
@@ -62,14 +68,20 @@ module Sidekiq
62
68
 
63
69
  get "/metrics" do
64
70
  q = Sidekiq::Metrics::Query.new
65
- @query_result = q.top_jobs
71
+ @period = h((params[:period] || "")[0..1])
72
+ @periods = METRICS_PERIODS
73
+ minutes = @periods.fetch(@period, @periods.values.first)
74
+ @query_result = q.top_jobs(minutes: minutes)
66
75
  erb(:metrics)
67
76
  end
68
77
 
69
78
  get "/metrics/:name" do
70
79
  @name = route_params[:name]
80
+ @period = h((params[:period] || "")[0..1])
71
81
  q = Sidekiq::Metrics::Query.new
72
- @query_result = q.for_job(@name)
82
+ @periods = METRICS_PERIODS
83
+ minutes = @periods.fetch(@period, @periods.values.first)
84
+ @query_result = q.for_job(@name, minutes: minutes)
73
85
  erb(:metrics_for_job)
74
86
  end
75
87
 
@@ -82,11 +94,14 @@ module Sidekiq
82
94
 
83
95
  post "/busy" do
84
96
  if params["identity"]
85
- p = Sidekiq::Process.new("identity" => params["identity"])
86
- p.quiet! if params["quiet"]
87
- p.stop! if params["stop"]
97
+ pro = Sidekiq::ProcessSet[params["identity"]]
98
+
99
+ pro.quiet! if params["quiet"]
100
+ pro.stop! if params["stop"]
88
101
  else
89
102
  processes.each do |pro|
103
+ next if pro.embedded?
104
+
90
105
  pro.quiet! if params["quiet"]
91
106
  pro.stop! if params["stop"]
92
107
  end
@@ -310,7 +325,7 @@ module Sidekiq
310
325
  end
311
326
 
312
327
  get "/stats/queues" do
313
- json Sidekiq::Stats::Queues.new.lengths
328
+ json Sidekiq::Stats.new.queues
314
329
  end
315
330
 
316
331
  def call(env)