sidekiq 6.2.2 → 7.1.2
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 +299 -11
- data/LICENSE.txt +9 -0
- data/README.md +45 -32
- data/bin/sidekiq +4 -9
- data/bin/sidekiqload +207 -117
- data/bin/sidekiqmon +4 -1
- data/lib/generators/sidekiq/job_generator.rb +57 -0
- data/lib/generators/sidekiq/templates/{worker.rb.erb → job.rb.erb} +2 -2
- data/lib/generators/sidekiq/templates/{worker_spec.rb.erb → job_spec.rb.erb} +1 -1
- data/lib/generators/sidekiq/templates/{worker_test.rb.erb → job_test.rb.erb} +1 -1
- data/lib/sidekiq/api.rb +334 -190
- data/lib/sidekiq/capsule.rb +127 -0
- data/lib/sidekiq/cli.rb +95 -81
- data/lib/sidekiq/client.rb +102 -96
- data/lib/sidekiq/{util.rb → component.rb} +14 -41
- data/lib/sidekiq/config.rb +278 -0
- data/lib/sidekiq/deploy.rb +62 -0
- data/lib/sidekiq/embedded.rb +61 -0
- data/lib/sidekiq/fetch.rb +26 -26
- data/lib/sidekiq/job.rb +371 -5
- data/lib/sidekiq/job_logger.rb +16 -28
- data/lib/sidekiq/job_retry.rb +85 -59
- data/lib/sidekiq/job_util.rb +105 -0
- data/lib/sidekiq/launcher.rb +106 -94
- data/lib/sidekiq/logger.rb +9 -44
- data/lib/sidekiq/manager.rb +40 -41
- data/lib/sidekiq/metrics/query.rb +153 -0
- data/lib/sidekiq/metrics/shared.rb +95 -0
- data/lib/sidekiq/metrics/tracking.rb +136 -0
- data/lib/sidekiq/middleware/chain.rb +96 -51
- data/lib/sidekiq/middleware/current_attributes.rb +95 -0
- data/lib/sidekiq/middleware/i18n.rb +6 -4
- data/lib/sidekiq/middleware/modules.rb +21 -0
- data/lib/sidekiq/monitor.rb +17 -4
- data/lib/sidekiq/paginator.rb +17 -9
- data/lib/sidekiq/processor.rb +60 -60
- data/lib/sidekiq/rails.rb +29 -6
- data/lib/sidekiq/redis_client_adapter.rb +96 -0
- data/lib/sidekiq/redis_connection.rb +17 -88
- data/lib/sidekiq/ring_buffer.rb +29 -0
- data/lib/sidekiq/scheduled.rb +101 -44
- data/lib/sidekiq/testing/inline.rb +4 -4
- data/lib/sidekiq/testing.rb +41 -68
- data/lib/sidekiq/transaction_aware_client.rb +44 -0
- data/lib/sidekiq/version.rb +2 -1
- data/lib/sidekiq/web/action.rb +3 -3
- data/lib/sidekiq/web/application.rb +47 -13
- data/lib/sidekiq/web/csrf_protection.rb +3 -3
- data/lib/sidekiq/web/helpers.rb +36 -33
- data/lib/sidekiq/web.rb +10 -17
- data/lib/sidekiq/worker_compatibility_alias.rb +13 -0
- data/lib/sidekiq.rb +86 -201
- data/sidekiq.gemspec +12 -10
- data/web/assets/javascripts/application.js +131 -60
- data/web/assets/javascripts/base-charts.js +106 -0
- data/web/assets/javascripts/chart.min.js +13 -0
- data/web/assets/javascripts/chartjs-plugin-annotation.min.js +7 -0
- data/web/assets/javascripts/dashboard-charts.js +166 -0
- data/web/assets/javascripts/dashboard.js +36 -273
- data/web/assets/javascripts/metrics.js +264 -0
- data/web/assets/stylesheets/application-dark.css +23 -23
- data/web/assets/stylesheets/application-rtl.css +2 -95
- data/web/assets/stylesheets/application.css +73 -402
- 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 +43 -24
- data/web/locales/en.yml +82 -69
- data/web/locales/es.yml +68 -68
- data/web/locales/fa.yml +65 -65
- data/web/locales/fr.yml +81 -67
- data/web/locales/gd.yml +99 -0
- 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 +73 -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 +63 -55
- 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 +43 -16
- data/web/locales/zh-tw.yml +42 -8
- data/web/views/_footer.erb +6 -3
- data/web/views/_job_info.erb +18 -2
- data/web/views/_metrics_period_select.erb +12 -0
- data/web/views/_nav.erb +1 -1
- data/web/views/_paging.erb +2 -0
- data/web/views/_poll_link.erb +3 -6
- data/web/views/_summary.erb +7 -7
- data/web/views/busy.erb +44 -28
- data/web/views/dashboard.erb +44 -12
- data/web/views/layout.erb +1 -1
- data/web/views/metrics.erb +82 -0
- data/web/views/metrics_for_job.erb +68 -0
- data/web/views/morgue.erb +5 -9
- data/web/views/queue.erb +24 -24
- data/web/views/queues.erb +4 -2
- data/web/views/retries.erb +5 -9
- data/web/views/scheduled.erb +12 -13
- metadata +62 -31
- data/LICENSE +0 -9
- data/lib/generators/sidekiq/worker_generator.rb +0 -57
- data/lib/sidekiq/delay.rb +0 -41
- data/lib/sidekiq/exception_handler.rb +0 -27
- 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/worker.rb +0 -244
@@ -0,0 +1,95 @@
|
|
1
|
+
require "active_support/current_attributes"
|
2
|
+
|
3
|
+
module Sidekiq
|
4
|
+
##
|
5
|
+
# Automatically save and load any current attributes in the execution context
|
6
|
+
# so context attributes "flow" from Rails actions into any associated jobs.
|
7
|
+
# This can be useful for multi-tenancy, i18n locale, timezone, any implicit
|
8
|
+
# per-request attribute. See +ActiveSupport::CurrentAttributes+.
|
9
|
+
#
|
10
|
+
# For multiple current attributes, pass an array of current attributes.
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
#
|
14
|
+
# # in your initializer
|
15
|
+
# require "sidekiq/middleware/current_attributes"
|
16
|
+
# Sidekiq::CurrentAttributes.persist("Myapp::Current")
|
17
|
+
# # or multiple current attributes
|
18
|
+
# Sidekiq::CurrentAttributes.persist(["Myapp::Current", "Myapp::OtherCurrent"])
|
19
|
+
#
|
20
|
+
module CurrentAttributes
|
21
|
+
class Save
|
22
|
+
include Sidekiq::ClientMiddleware
|
23
|
+
|
24
|
+
def initialize(cattrs)
|
25
|
+
@cattrs = cattrs
|
26
|
+
end
|
27
|
+
|
28
|
+
def call(_, job, _, _)
|
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?
|
35
|
+
end
|
36
|
+
end
|
37
|
+
yield
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class Load
|
42
|
+
include Sidekiq::ServerMiddleware
|
43
|
+
|
44
|
+
def initialize(cattrs)
|
45
|
+
@cattrs = cattrs
|
46
|
+
end
|
47
|
+
|
48
|
+
def call(_, job, _, &block)
|
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
|
60
|
+
end
|
61
|
+
|
62
|
+
yield
|
63
|
+
ensure
|
64
|
+
cattrs_to_reset.each(&:reset)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
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
|
74
|
+
end
|
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}"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -10,16 +10,18 @@ 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
|
-
|
14
|
-
|
13
|
+
include Sidekiq::ClientMiddleware
|
14
|
+
def call(_jobclass, job, _queue, _redis)
|
15
|
+
job["locale"] ||= I18n.locale
|
15
16
|
yield
|
16
17
|
end
|
17
18
|
end
|
18
19
|
|
19
20
|
# Pull the msg locale out and set the current thread to use it.
|
20
21
|
class Server
|
21
|
-
|
22
|
-
|
22
|
+
include Sidekiq::ServerMiddleware
|
23
|
+
def call(_jobclass, job, _queue, &block)
|
24
|
+
I18n.with_locale(job.fetch("locale", I18n.default_locale), &block)
|
23
25
|
end
|
24
26
|
end
|
25
27
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Sidekiq
|
2
|
+
# Server-side middleware must import this Module in order
|
3
|
+
# to get access to server resources during `call`.
|
4
|
+
module ServerMiddleware
|
5
|
+
attr_accessor :config
|
6
|
+
def redis_pool
|
7
|
+
config.redis_pool
|
8
|
+
end
|
9
|
+
|
10
|
+
def logger
|
11
|
+
config.logger
|
12
|
+
end
|
13
|
+
|
14
|
+
def redis(&block)
|
15
|
+
config.redis(&block)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# no difference for now
|
20
|
+
ClientMiddleware = ServerMiddleware
|
21
|
+
end
|
data/lib/sidekiq/monitor.rb
CHANGED
@@ -16,8 +16,6 @@ class Sidekiq::Monitor
|
|
16
16
|
return
|
17
17
|
end
|
18
18
|
send(section)
|
19
|
-
rescue => e
|
20
|
-
puts "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(
|
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
|
data/lib/sidekiq/paginator.rb
CHANGED
@@ -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 = []
|
@@ -16,22 +16,22 @@ module Sidekiq
|
|
16
16
|
|
17
17
|
case type
|
18
18
|
when "zset"
|
19
|
-
total_size, items = conn.multi {
|
20
|
-
|
19
|
+
total_size, items = conn.multi { |transaction|
|
20
|
+
transaction.zcard(key)
|
21
21
|
if rev
|
22
|
-
|
22
|
+
transaction.zrange(key, starting, ending, "REV", withscores: true)
|
23
23
|
else
|
24
|
-
|
24
|
+
transaction.zrange(key, starting, ending, withscores: true)
|
25
25
|
end
|
26
26
|
}
|
27
27
|
[current_page, total_size, items]
|
28
28
|
when "list"
|
29
|
-
total_size, items = conn.multi {
|
30
|
-
|
29
|
+
total_size, items = conn.multi { |transaction|
|
30
|
+
transaction.llen(key)
|
31
31
|
if rev
|
32
|
-
|
32
|
+
transaction.lrange(key, -ending - 1, -starting - 1)
|
33
33
|
else
|
34
|
-
|
34
|
+
transaction.lrange(key, starting, ending)
|
35
35
|
end
|
36
36
|
}
|
37
37
|
items.reverse! if rev
|
@@ -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
|
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"
|
@@ -11,33 +10,34 @@ module Sidekiq
|
|
11
10
|
#
|
12
11
|
# 1. fetches a job from Redis
|
13
12
|
# 2. executes the job
|
14
|
-
# a. instantiate the
|
13
|
+
# a. instantiate the job class
|
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
|
29
|
+
attr_reader :capsule
|
30
30
|
|
31
|
-
def initialize(
|
32
|
-
@
|
31
|
+
def initialize(capsule, &block)
|
32
|
+
@config = @capsule = capsule
|
33
|
+
@callback = block
|
33
34
|
@down = false
|
34
35
|
@done = false
|
35
36
|
@job = nil
|
36
37
|
@thread = nil
|
37
|
-
@
|
38
|
-
@
|
39
|
-
@
|
40
|
-
@retrier = Sidekiq::JobRetry.new
|
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,33 +59,37 @@ 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
|
71
|
-
@
|
75
|
+
@callback.call(self)
|
72
76
|
rescue Exception => ex
|
73
|
-
@
|
77
|
+
@callback.call(self, ex)
|
74
78
|
end
|
75
79
|
|
76
|
-
def process_one
|
80
|
+
def process_one(&block)
|
77
81
|
@job = fetch
|
78
82
|
process(@job) if @job
|
79
83
|
@job = nil
|
80
84
|
end
|
81
85
|
|
82
86
|
def get_one
|
83
|
-
|
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
|
87
91
|
end
|
88
|
-
|
92
|
+
uow
|
89
93
|
rescue Sidekiq::Shutdown
|
90
94
|
rescue => ex
|
91
95
|
handle_fetch_exception(ex)
|
@@ -129,11 +133,11 @@ 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 =
|
133
|
-
|
134
|
-
|
135
|
-
@retrier.local(
|
136
|
-
yield
|
136
|
+
klass = Object.const_get(job_hash["class"])
|
137
|
+
inst = klass.new
|
138
|
+
inst.jid = job_hash["jid"]
|
139
|
+
@retrier.local(inst, jobstr, queue) do
|
140
|
+
yield inst
|
137
141
|
end
|
138
142
|
end
|
139
143
|
end
|
@@ -142,9 +146,12 @@ module Sidekiq
|
|
142
146
|
end
|
143
147
|
end
|
144
148
|
|
145
|
-
|
146
|
-
|
147
|
-
|
149
|
+
IGNORE_SHUTDOWN_INTERRUPTS = {Sidekiq::Shutdown => :never}
|
150
|
+
private_constant :IGNORE_SHUTDOWN_INTERRUPTS
|
151
|
+
|
152
|
+
def process(uow)
|
153
|
+
jobstr = uow.job
|
154
|
+
queue = uow.queue_name
|
148
155
|
|
149
156
|
# Treat malformed JSON as a special case: job goes straight to the morgue.
|
150
157
|
job_hash = nil
|
@@ -152,16 +159,22 @@ module Sidekiq
|
|
152
159
|
job_hash = Sidekiq.load_json(jobstr)
|
153
160
|
rescue => ex
|
154
161
|
handle_exception(ex, {context: "Invalid JSON for job", jobstr: jobstr})
|
155
|
-
|
156
|
-
|
157
|
-
|
162
|
+
now = Time.now.to_f
|
163
|
+
redis do |conn|
|
164
|
+
conn.multi do |xa|
|
165
|
+
xa.zadd("dead", now.to_s, jobstr)
|
166
|
+
xa.zremrangebyscore("dead", "-inf", now - @capsule.config[:dead_timeout_in_seconds])
|
167
|
+
xa.zremrangebyrank("dead", 0, - @capsule.config[:dead_max_jobs])
|
168
|
+
end
|
169
|
+
end
|
170
|
+
return uow.acknowledge
|
158
171
|
end
|
159
172
|
|
160
173
|
ack = false
|
161
174
|
begin
|
162
|
-
dispatch(job_hash, queue, jobstr) do |
|
163
|
-
|
164
|
-
execute_job(
|
175
|
+
dispatch(job_hash, queue, jobstr) do |inst|
|
176
|
+
config.server_middleware.invoke(inst, job_hash, queue) do
|
177
|
+
execute_job(inst, job_hash["args"])
|
165
178
|
end
|
166
179
|
end
|
167
180
|
ack = true
|
@@ -174,7 +187,7 @@ module Sidekiq
|
|
174
187
|
# signals that we created a retry successfully. We can acknowlege the job.
|
175
188
|
ack = true
|
176
189
|
e = h.cause || h
|
177
|
-
handle_exception(e, {context: "Job raised exception", job: job_hash
|
190
|
+
handle_exception(e, {context: "Job raised exception", job: job_hash})
|
178
191
|
raise e
|
179
192
|
rescue Exception => ex
|
180
193
|
# Unexpected error! This is very bad and indicates an exception that got past
|
@@ -185,15 +198,15 @@ module Sidekiq
|
|
185
198
|
ensure
|
186
199
|
if ack
|
187
200
|
# We don't want a shutdown signal to interrupt job acknowledgment.
|
188
|
-
Thread.handle_interrupt(
|
189
|
-
|
201
|
+
Thread.handle_interrupt(IGNORE_SHUTDOWN_INTERRUPTS) do
|
202
|
+
uow.acknowledge
|
190
203
|
end
|
191
204
|
end
|
192
205
|
end
|
193
206
|
end
|
194
207
|
|
195
|
-
def execute_job(
|
196
|
-
|
208
|
+
def execute_job(inst, cloned_args)
|
209
|
+
inst.perform(*cloned_args)
|
197
210
|
end
|
198
211
|
|
199
212
|
# Ruby doesn't provide atomic counters out of the box so we'll
|
@@ -219,39 +232,39 @@ module Sidekiq
|
|
219
232
|
end
|
220
233
|
|
221
234
|
# jruby's Hash implementation is not threadsafe, so we wrap it in a mutex here
|
222
|
-
class
|
235
|
+
class SharedWorkState
|
223
236
|
def initialize
|
224
|
-
@
|
237
|
+
@work_state = {}
|
225
238
|
@lock = Mutex.new
|
226
239
|
end
|
227
240
|
|
228
241
|
def set(tid, hash)
|
229
|
-
@lock.synchronize { @
|
242
|
+
@lock.synchronize { @work_state[tid] = hash }
|
230
243
|
end
|
231
244
|
|
232
245
|
def delete(tid)
|
233
|
-
@lock.synchronize { @
|
246
|
+
@lock.synchronize { @work_state.delete(tid) }
|
234
247
|
end
|
235
248
|
|
236
249
|
def dup
|
237
|
-
@lock.synchronize { @
|
250
|
+
@lock.synchronize { @work_state.dup }
|
238
251
|
end
|
239
252
|
|
240
253
|
def size
|
241
|
-
@lock.synchronize { @
|
254
|
+
@lock.synchronize { @work_state.size }
|
242
255
|
end
|
243
256
|
|
244
257
|
def clear
|
245
|
-
@lock.synchronize { @
|
258
|
+
@lock.synchronize { @work_state.clear }
|
246
259
|
end
|
247
260
|
end
|
248
261
|
|
249
262
|
PROCESSED = Counter.new
|
250
263
|
FAILURE = Counter.new
|
251
|
-
|
264
|
+
WORK_STATE = SharedWorkState.new
|
252
265
|
|
253
266
|
def stats(jobstr, queue)
|
254
|
-
|
267
|
+
WORK_STATE.set(tid, {queue: queue, payload: jobstr, run_at: Time.now.to_i})
|
255
268
|
|
256
269
|
begin
|
257
270
|
yield
|
@@ -259,22 +272,9 @@ module Sidekiq
|
|
259
272
|
FAILURE.incr
|
260
273
|
raise
|
261
274
|
ensure
|
262
|
-
|
275
|
+
WORK_STATE.delete(tid)
|
263
276
|
PROCESSED.incr
|
264
277
|
end
|
265
278
|
end
|
266
|
-
|
267
|
-
def constantize(str)
|
268
|
-
return Object.const_get(str) unless str.include?("::")
|
269
|
-
|
270
|
-
names = str.split("::")
|
271
|
-
names.shift if names.empty? || names.first.empty?
|
272
|
-
|
273
|
-
names.inject(Object) do |constant, name|
|
274
|
-
# the false flag limits search for name to under the constant namespace
|
275
|
-
# which mimics Rails' behaviour
|
276
|
-
constant.const_get(name, false)
|
277
|
-
end
|
278
|
-
end
|
279
279
|
end
|
280
280
|
end
|
data/lib/sidekiq/rails.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "sidekiq/
|
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
|
@@ -18,11 +20,15 @@ module Sidekiq
|
|
18
20
|
def inspect
|
19
21
|
"#<Sidekiq::Rails::Reloader @app=#{@app.class.name}>"
|
20
22
|
end
|
23
|
+
|
24
|
+
def to_json(*)
|
25
|
+
Sidekiq.dump_json(inspect)
|
26
|
+
end
|
21
27
|
end
|
22
28
|
|
23
29
|
# By including the Options module, we allow AJs to directly control sidekiq features
|
24
30
|
# 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
|
31
|
+
# AJ retries don't show up in the Sidekiq UI Retries tab, don't save any error data, can't be
|
26
32
|
# manually retried, don't automatically die, etc.
|
27
33
|
#
|
28
34
|
# class SomeJob < ActiveJob::Base
|
@@ -33,7 +39,24 @@ module Sidekiq
|
|
33
39
|
# end
|
34
40
|
initializer "sidekiq.active_job_integration" do
|
35
41
|
ActiveSupport.on_load(:active_job) do
|
36
|
-
include ::Sidekiq::
|
42
|
+
include ::Sidekiq::Job::Options unless respond_to?(:sidekiq_options)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
initializer "sidekiq.rails_logger" do
|
47
|
+
Sidekiq.configure_server do |config|
|
48
|
+
# This is the integration code necessary so that if a job uses `Rails.logger.info "Hello"`,
|
49
|
+
# it will appear in the Sidekiq console with all of the job context. See #5021 and
|
50
|
+
# https://github.com/rails/rails/blob/b5f2b550f69a99336482739000c58e4e04e033aa/railties/lib/rails/commands/server/server_command.rb#L82-L84
|
51
|
+
unless ::Rails.logger == config.logger || ::ActiveSupport::Logger.logger_outputs_to?(::Rails.logger, $stdout)
|
52
|
+
::Rails.logger.extend(::ActiveSupport::Logger.broadcast(config.logger))
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
initializer "sidekiq.backtrace_cleaner" do
|
58
|
+
Sidekiq.configure_server do |config|
|
59
|
+
config[:backtrace_cleaner] = ->(backtrace) { ::Rails.backtrace_cleaner.clean(backtrace) }
|
37
60
|
end
|
38
61
|
end
|
39
62
|
|
@@ -42,8 +65,8 @@ module Sidekiq
|
|
42
65
|
#
|
43
66
|
# None of this matters on the client-side, only within the Sidekiq process itself.
|
44
67
|
config.after_initialize do
|
45
|
-
Sidekiq.configure_server do |
|
46
|
-
|
68
|
+
Sidekiq.configure_server do |config|
|
69
|
+
config[:reloader] = Sidekiq::Rails::Reloader.new
|
47
70
|
end
|
48
71
|
end
|
49
72
|
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "set"
|
4
|
+
require "redis_client"
|
5
|
+
require "redis_client/decorator"
|
6
|
+
|
7
|
+
module Sidekiq
|
8
|
+
class RedisClientAdapter
|
9
|
+
BaseError = RedisClient::Error
|
10
|
+
CommandError = RedisClient::CommandError
|
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
|
+
|
15
|
+
module CompatMethods
|
16
|
+
def info
|
17
|
+
@client.call("INFO") { |i| i.lines(chomp: true).map { |l| l.split(":", 2) }.select { |l| l.size == 2 }.to_h }
|
18
|
+
end
|
19
|
+
|
20
|
+
def evalsha(sha, keys, argv)
|
21
|
+
@client.call("EVALSHA", sha, keys.size, *keys, *argv)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
# this allows us to use methods like `conn.hmset(...)` instead of having to use
|
27
|
+
# redis-client's native `conn.call("hmset", ...)`
|
28
|
+
def method_missing(*args, &block)
|
29
|
+
warn("[sidekiq#5788] Redis has deprecated the `#{args.first}`command, called at #{caller(1..1)}") if DEPRECATED_COMMANDS.include?(args.first)
|
30
|
+
@client.call(*args, *block)
|
31
|
+
end
|
32
|
+
ruby2_keywords :method_missing if respond_to?(:ruby2_keywords, true)
|
33
|
+
|
34
|
+
def respond_to_missing?(name, include_private = false)
|
35
|
+
super # Appease the linter. We can't tell what is a valid command.
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
CompatClient = RedisClient::Decorator.create(CompatMethods)
|
40
|
+
|
41
|
+
class CompatClient
|
42
|
+
def config
|
43
|
+
@client.config
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def initialize(options)
|
48
|
+
opts = client_opts(options)
|
49
|
+
@config = if opts.key?(:sentinels)
|
50
|
+
RedisClient.sentinel(**opts)
|
51
|
+
else
|
52
|
+
RedisClient.config(**opts)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def new_client
|
57
|
+
CompatClient.new(@config.new_client)
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def client_opts(options)
|
63
|
+
opts = options.dup
|
64
|
+
|
65
|
+
if opts[:namespace]
|
66
|
+
raise ArgumentError, "Your Redis configuration uses the namespace '#{opts[:namespace]}' but this feature isn't supported by redis-client. " \
|
67
|
+
"Either use the redis adapter or remove the namespace."
|
68
|
+
end
|
69
|
+
|
70
|
+
opts.delete(:size)
|
71
|
+
opts.delete(:pool_timeout)
|
72
|
+
|
73
|
+
if opts[:network_timeout]
|
74
|
+
opts[:timeout] = opts[:network_timeout]
|
75
|
+
opts.delete(:network_timeout)
|
76
|
+
end
|
77
|
+
|
78
|
+
if opts[:driver]
|
79
|
+
opts[:driver] = opts[:driver].to_sym
|
80
|
+
end
|
81
|
+
|
82
|
+
opts[:name] = opts.delete(:master_name) if opts.key?(:master_name)
|
83
|
+
opts[:role] = opts[:role].to_sym if opts.key?(:role)
|
84
|
+
opts.delete(:url) if opts.key?(:sentinels)
|
85
|
+
|
86
|
+
# Issue #3303, redis-rb will silently retry an operation.
|
87
|
+
# This can lead to duplicate jobs if Sidekiq::Client's LPUSH
|
88
|
+
# is performed twice but I believe this is much, much rarer
|
89
|
+
# than the reconnect silently fixing a problem; we keep it
|
90
|
+
# on by default.
|
91
|
+
opts[:reconnect_attempts] ||= 1
|
92
|
+
|
93
|
+
opts
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|