sidekiq 6.5.12 → 7.0.6
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.
- checksums.yaml +4 -4
- data/Changes.md +63 -22
- data/README.md +40 -32
- data/bin/sidekiq +3 -8
- data/bin/sidekiqload +186 -118
- data/bin/sidekiqmon +3 -0
- data/lib/sidekiq/api.rb +84 -121
- data/lib/sidekiq/capsule.rb +127 -0
- data/lib/sidekiq/cli.rb +55 -74
- data/lib/sidekiq/client.rb +29 -16
- data/lib/sidekiq/component.rb +3 -0
- data/lib/sidekiq/config.rb +270 -0
- data/lib/sidekiq/deploy.rb +62 -0
- data/lib/sidekiq/embedded.rb +61 -0
- data/lib/sidekiq/fetch.rb +11 -14
- data/lib/sidekiq/job.rb +375 -10
- data/lib/sidekiq/job_logger.rb +2 -2
- data/lib/sidekiq/job_retry.rb +9 -9
- data/lib/sidekiq/job_util.rb +48 -14
- data/lib/sidekiq/launcher.rb +64 -61
- data/lib/sidekiq/logger.rb +1 -26
- data/lib/sidekiq/manager.rb +9 -11
- data/lib/sidekiq/metrics/query.rb +2 -2
- data/lib/sidekiq/metrics/shared.rb +4 -3
- data/lib/sidekiq/metrics/tracking.rb +20 -18
- data/lib/sidekiq/middleware/chain.rb +19 -18
- data/lib/sidekiq/middleware/current_attributes.rb +8 -15
- data/lib/sidekiq/monitor.rb +16 -3
- data/lib/sidekiq/processor.rb +21 -27
- data/lib/sidekiq/rails.rb +13 -17
- data/lib/sidekiq/redis_client_adapter.rb +8 -47
- data/lib/sidekiq/redis_connection.rb +11 -111
- data/lib/sidekiq/scheduled.rb +20 -21
- data/lib/sidekiq/testing.rb +5 -33
- data/lib/sidekiq/transaction_aware_client.rb +4 -5
- data/lib/sidekiq/version.rb +2 -1
- data/lib/sidekiq/web/application.rb +21 -6
- data/lib/sidekiq/web/csrf_protection.rb +1 -1
- data/lib/sidekiq/web/helpers.rb +16 -15
- data/lib/sidekiq/web.rb +6 -17
- data/lib/sidekiq/worker_compatibility_alias.rb +13 -0
- data/lib/sidekiq.rb +76 -274
- data/sidekiq.gemspec +20 -10
- data/web/assets/javascripts/application.js +18 -1
- data/web/assets/javascripts/base-charts.js +106 -0
- data/web/assets/javascripts/dashboard-charts.js +166 -0
- data/web/assets/javascripts/dashboard.js +3 -223
- data/web/assets/javascripts/metrics.js +117 -115
- data/web/assets/stylesheets/application-dark.css +4 -0
- data/web/assets/stylesheets/application-rtl.css +2 -91
- data/web/assets/stylesheets/application.css +23 -298
- data/web/locales/ar.yml +70 -70
- data/web/locales/cs.yml +62 -62
- data/web/locales/da.yml +60 -53
- data/web/locales/de.yml +65 -65
- data/web/locales/el.yml +2 -7
- data/web/locales/en.yml +76 -70
- data/web/locales/es.yml +68 -68
- data/web/locales/fa.yml +65 -65
- data/web/locales/fr.yml +67 -67
- data/web/locales/he.yml +65 -64
- data/web/locales/hi.yml +59 -59
- data/web/locales/it.yml +53 -53
- data/web/locales/ja.yml +64 -68
- data/web/locales/ko.yml +52 -52
- data/web/locales/lt.yml +66 -66
- data/web/locales/nb.yml +61 -61
- data/web/locales/nl.yml +52 -52
- data/web/locales/pl.yml +45 -45
- data/web/locales/pt-br.yml +59 -69
- data/web/locales/pt.yml +51 -51
- data/web/locales/ru.yml +67 -66
- data/web/locales/sv.yml +53 -53
- data/web/locales/ta.yml +60 -60
- data/web/locales/uk.yml +62 -61
- data/web/locales/ur.yml +64 -64
- data/web/locales/vi.yml +67 -67
- data/web/locales/zh-cn.yml +20 -18
- data/web/locales/zh-tw.yml +10 -1
- data/web/views/_footer.erb +5 -2
- data/web/views/_job_info.erb +18 -2
- data/web/views/_metrics_period_select.erb +12 -0
- data/web/views/_paging.erb +2 -0
- data/web/views/_poll_link.erb +1 -1
- data/web/views/busy.erb +37 -26
- data/web/views/dashboard.erb +36 -5
- data/web/views/metrics.erb +33 -20
- data/web/views/metrics_for_job.erb +22 -38
- data/web/views/morgue.erb +5 -9
- data/web/views/queue.erb +10 -14
- data/web/views/queues.erb +3 -1
- data/web/views/retries.erb +5 -9
- data/web/views/scheduled.erb +12 -13
- metadata +50 -40
- data/lib/sidekiq/delay.rb +0 -43
- data/lib/sidekiq/extensions/action_mailer.rb +0 -48
- data/lib/sidekiq/extensions/active_record.rb +0 -43
- data/lib/sidekiq/extensions/class_methods.rb +0 -43
- data/lib/sidekiq/extensions/generic_proxy.rb +0 -33
- data/lib/sidekiq/metrics/deploy.rb +0 -47
- data/lib/sidekiq/worker.rb +0 -370
- data/web/assets/javascripts/graph.js +0 -16
- /data/{LICENSE → LICENSE.txt} +0 -0
data/lib/sidekiq/processor.rb
CHANGED
@@ -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(
|
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
|
-
@
|
37
|
-
@
|
38
|
-
@
|
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 =
|
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 =
|
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
|
-
|
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
|
-
|
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(
|
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
|
-
|
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,11 +39,15 @@ module Sidekiq
|
|
37
39
|
end
|
38
40
|
end
|
39
41
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
42
|
+
initializer "sidekiq.rails_logger" do
|
43
|
+
Sidekiq.configure_server do |config|
|
44
|
+
# This is the integration code necessary so that if a job uses `Rails.logger.info "Hello"`,
|
45
|
+
# it will appear in the Sidekiq console with all of the job context. See #5021 and
|
46
|
+
# https://github.com/rails/rails/blob/b5f2b550f69a99336482739000c58e4e04e033aa/railties/lib/rails/commands/server/server_command.rb#L82-L84
|
47
|
+
unless ::Rails.logger == config.logger || ::ActiveSupport::Logger.logger_outputs_to?(::Rails.logger, $stdout)
|
48
|
+
::Rails.logger.extend(::ActiveSupport::Logger.broadcast(config.logger))
|
49
|
+
end
|
50
|
+
end
|
45
51
|
end
|
46
52
|
|
47
53
|
# This hook happens after all initializers are run, just before returning
|
@@ -51,16 +57,6 @@ module Sidekiq
|
|
51
57
|
config.after_initialize do
|
52
58
|
Sidekiq.configure_server do |config|
|
53
59
|
config[:reloader] = Sidekiq::Rails::Reloader.new
|
54
|
-
|
55
|
-
# This is the integration code necessary so that if a job uses `Rails.logger.info "Hello"`,
|
56
|
-
# it will appear in the Sidekiq console with all of the job context.
|
57
|
-
unless ::Rails.logger == config.logger || ::ActiveSupport::Logger.logger_outputs_to?(::Rails.logger, $stdout)
|
58
|
-
if ::Rails::VERSION::STRING < "7.1"
|
59
|
-
::Rails.logger.extend(::ActiveSupport::Logger.broadcast(config.logger))
|
60
|
-
else
|
61
|
-
::Rails.logger.broadcast_to(config.logger)
|
62
|
-
end
|
63
|
-
end
|
64
60
|
end
|
65
61
|
end
|
66
62
|
end
|
@@ -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
|
-
|
59
|
-
|
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
|
-
|
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
|
-
|
85
|
-
|
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
|
-
|
103
|
-
|
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 =
|
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
|
-
|
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
|
-
|
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
|
data/lib/sidekiq/scheduled.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
36
|
-
|
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
|
-
|
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.
|
55
|
-
rescue
|
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(
|
74
|
-
@config =
|
75
|
-
@enq = (
|
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
|
88
|
+
@enq.terminate
|
86
89
|
|
87
|
-
|
88
|
-
|
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
|
196
|
+
return 0 unless redis { |conn| conn.set("process_cleanup", "1", nx: true, ex: 60) }
|
198
197
|
|
199
198
|
count = 0
|
200
|
-
|
201
|
-
procs = conn.
|
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")
|
data/lib/sidekiq/testing.rb
CHANGED
@@ -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 =
|
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
|
-
#
|
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
|
-
|
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.
|
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(
|
9
|
-
@redis_client = Client.new(
|
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
|
-
|
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
|