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.
- checksums.yaml +4 -4
- data/Changes.md +20 -0
- data/bin/sidekiqload +15 -3
- data/lib/sidekiq/api.rb +16 -7
- data/lib/sidekiq/cli.rb +32 -31
- data/lib/sidekiq/client.rb +4 -4
- data/lib/sidekiq/component.rb +64 -0
- data/lib/sidekiq/fetch.rb +15 -13
- data/lib/sidekiq/job_retry.rb +4 -3
- data/lib/sidekiq/job_util.rb +7 -3
- data/lib/sidekiq/launcher.rb +18 -17
- data/lib/sidekiq/logger.rb +1 -1
- data/lib/sidekiq/manager.rb +23 -20
- data/lib/sidekiq/middleware/chain.rb +20 -11
- data/lib/sidekiq/middleware/current_attributes.rb +4 -0
- data/lib/sidekiq/middleware/i18n.rb +2 -0
- data/lib/sidekiq/middleware/modules.rb +19 -0
- data/lib/sidekiq/paginator.rb +2 -2
- data/lib/sidekiq/processor.rb +12 -12
- data/lib/sidekiq/rails.rb +5 -5
- data/lib/sidekiq/redis_client_adapter.rb +154 -0
- data/lib/sidekiq/redis_connection.rb +80 -47
- data/lib/sidekiq/ring_buffer.rb +29 -0
- data/lib/sidekiq/scheduled.rb +11 -10
- data/lib/sidekiq/testing.rb +1 -1
- data/lib/sidekiq/transaction_aware_client.rb +45 -0
- data/lib/sidekiq/version.rb +1 -1
- data/lib/sidekiq/web/helpers.rb +1 -1
- data/lib/sidekiq/worker.rb +2 -1
- data/lib/sidekiq.rb +78 -17
- data/web/locales/pt-br.yml +27 -9
- metadata +7 -4
- data/lib/sidekiq/exception_handler.rb +0 -27
- data/lib/sidekiq/util.rb +0 -108
data/lib/sidekiq/launcher.rb
CHANGED
@@ -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
|
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) + @
|
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 = @
|
59
|
-
strategy.bulk_requeue([], @
|
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
|
-
|
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
|
-
|
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
|
-
|
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 =
|
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
|
-
|
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
|
-
|
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" => @
|
251
|
-
"concurrency" => @
|
252
|
-
"queues" => @
|
253
|
-
"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
|
data/lib/sidekiq/logger.rb
CHANGED
data/lib/sidekiq/manager.rb
CHANGED
@@ -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
|
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(
|
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
|
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
|
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
|
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(
|
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.
|
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 = @
|
119
|
-
strategy.bulk_requeue(jobs, @
|
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
|
-
#
|
51
|
+
# logger.info "Before job"
|
52
|
+
# redis {|conn| conn.get("foo") } # do something in Redis
|
49
53
|
# yield
|
50
|
-
#
|
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
|
-
#
|
65
|
+
# logger.info "Before push"
|
61
66
|
# result = yield
|
62
|
-
#
|
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
|
data/lib/sidekiq/paginator.rb
CHANGED
@@ -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,
|
22
|
+
transaction.zrevrange(key, starting, ending, withscores: true)
|
23
23
|
else
|
24
|
-
transaction.zrange(key, starting, ending,
|
24
|
+
transaction.zrange(key, starting, ending, withscores: true)
|
25
25
|
end
|
26
26
|
}
|
27
27
|
[current_page, total_size, items]
|
data/lib/sidekiq/processor.rb
CHANGED
@@ -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
|
19
|
-
#
|
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
|
25
|
+
include Sidekiq::Component
|
27
26
|
|
28
27
|
attr_reader :thread
|
29
28
|
attr_reader :job
|
30
29
|
|
31
|
-
def initialize(
|
32
|
-
@
|
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
|
-
@
|
69
|
+
@callback.call(self)
|
70
70
|
rescue Sidekiq::Shutdown
|
71
|
-
@
|
71
|
+
@callback.call(self)
|
72
72
|
rescue Exception => ex
|
73
|
-
@
|
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
|
-
|
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 ==
|
46
|
-
::Rails.logger.extend(::ActiveSupport::Logger.broadcast(
|
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
|
-
|
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
|