sidekiq 6.2.0 → 7.0.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 +203 -5
- data/LICENSE.txt +9 -0
- data/README.md +20 -14
- data/bin/sidekiq +4 -9
- data/bin/sidekiqload +71 -76
- data/bin/sidekiqmon +1 -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 -229
- data/lib/sidekiq/capsule.rb +110 -0
- data/lib/sidekiq/cli.rb +91 -79
- data/lib/sidekiq/client.rb +74 -85
- data/lib/sidekiq/{util.rb → component.rb} +13 -14
- data/lib/sidekiq/config.rb +271 -0
- data/lib/sidekiq/deploy.rb +62 -0
- data/lib/sidekiq/embedded.rb +61 -0
- data/lib/sidekiq/fetch.rb +26 -24
- data/lib/sidekiq/{worker.rb → job.rb} +162 -28
- data/lib/sidekiq/job_logger.rb +17 -29
- data/lib/sidekiq/job_retry.rb +79 -60
- data/lib/sidekiq/job_util.rb +71 -0
- data/lib/sidekiq/launcher.rb +96 -96
- 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 +134 -0
- data/lib/sidekiq/middleware/chain.rb +89 -45
- data/lib/sidekiq/middleware/current_attributes.rb +58 -0
- data/lib/sidekiq/middleware/i18n.rb +6 -4
- data/lib/sidekiq/middleware/modules.rb +21 -0
- data/lib/sidekiq/monitor.rb +1 -1
- data/lib/sidekiq/paginator.rb +16 -8
- data/lib/sidekiq/processor.rb +56 -59
- data/lib/sidekiq/rails.rb +17 -5
- data/lib/sidekiq/redis_client_adapter.rb +118 -0
- data/lib/sidekiq/redis_connection.rb +17 -88
- data/lib/sidekiq/ring_buffer.rb +29 -0
- data/lib/sidekiq/scheduled.rb +102 -39
- data/lib/sidekiq/testing/inline.rb +4 -4
- data/lib/sidekiq/testing.rb +42 -71
- 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 +33 -12
- data/lib/sidekiq/web/csrf_protection.rb +12 -9
- data/lib/sidekiq/web/helpers.rb +30 -40
- data/lib/sidekiq/web.rb +9 -19
- data/lib/sidekiq/worker_compatibility_alias.rb +13 -0
- data/lib/sidekiq.rb +86 -201
- data/sidekiq.gemspec +30 -6
- data/web/assets/javascripts/application.js +113 -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 +236 -0
- data/web/assets/stylesheets/application-dark.css +28 -45
- data/web/assets/stylesheets/application-rtl.css +2 -95
- data/web/assets/stylesheets/application.css +84 -529
- data/web/locales/ar.yml +71 -65
- data/web/locales/cs.yml +62 -62
- data/web/locales/da.yml +52 -52
- data/web/locales/de.yml +65 -65
- data/web/locales/el.yml +43 -24
- data/web/locales/en.yml +83 -67
- data/web/locales/es.yml +70 -54
- data/web/locales/fa.yml +65 -65
- data/web/locales/fr.yml +69 -62
- 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 +72 -66
- 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 +37 -11
- data/web/locales/zh-tw.yml +42 -8
- data/web/views/_footer.erb +6 -3
- data/web/views/_job_info.erb +1 -1
- data/web/views/_nav.erb +1 -1
- data/web/views/_poll_link.erb +2 -5
- data/web/views/_summary.erb +7 -7
- data/web/views/busy.erb +17 -11
- data/web/views/dashboard.erb +58 -18
- data/web/views/dead.erb +1 -1
- data/web/views/layout.erb +1 -1
- data/web/views/metrics.erb +80 -0
- data/web/views/metrics_for_job.erb +69 -0
- data/web/views/morgue.erb +6 -6
- data/web/views/queue.erb +15 -11
- data/web/views/queues.erb +3 -3
- data/web/views/retries.erb +7 -7
- data/web/views/retry.erb +1 -1
- data/web/views/scheduled.erb +1 -1
- metadata +78 -28
- 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 -31
@@ -1,82 +1,93 @@
|
|
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
|
-
# a
|
7
|
+
# a job is processed. It is patterned after Rack
|
6
8
|
# middleware. Middleware exists for the client side
|
7
9
|
# (pushing jobs onto the queue) as well as the server
|
8
10
|
# side (when jobs are actually processed).
|
9
11
|
#
|
12
|
+
# Callers will register middleware Classes and Sidekiq will
|
13
|
+
# create new instances of the middleware for every job. This
|
14
|
+
# is important so that instance state is not shared accidentally
|
15
|
+
# between job executions.
|
16
|
+
#
|
10
17
|
# To add middleware for the client:
|
11
18
|
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
19
|
+
# Sidekiq.configure_client do |config|
|
20
|
+
# config.client_middleware do |chain|
|
21
|
+
# chain.add MyClientHook
|
22
|
+
# end
|
15
23
|
# end
|
16
|
-
# end
|
17
24
|
#
|
18
25
|
# To modify middleware for the server, just call
|
19
26
|
# with another block:
|
20
27
|
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
28
|
+
# Sidekiq.configure_server do |config|
|
29
|
+
# config.server_middleware do |chain|
|
30
|
+
# chain.add MyServerHook
|
31
|
+
# chain.remove ActiveRecord
|
32
|
+
# end
|
25
33
|
# end
|
26
|
-
# end
|
27
34
|
#
|
28
35
|
# To insert immediately preceding another entry:
|
29
36
|
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
#
|
37
|
+
# Sidekiq.configure_client do |config|
|
38
|
+
# config.client_middleware do |chain|
|
39
|
+
# chain.insert_before ActiveRecord, MyClientHook
|
40
|
+
# end
|
33
41
|
# end
|
34
|
-
# end
|
35
42
|
#
|
36
43
|
# To insert immediately after another entry:
|
37
44
|
#
|
38
|
-
#
|
39
|
-
#
|
40
|
-
#
|
45
|
+
# Sidekiq.configure_client do |config|
|
46
|
+
# config.client_middleware do |chain|
|
47
|
+
# chain.insert_after ActiveRecord, MyClientHook
|
48
|
+
# end
|
41
49
|
# end
|
42
|
-
# end
|
43
50
|
#
|
44
51
|
# This is an example of a minimal server middleware:
|
45
52
|
#
|
46
|
-
#
|
47
|
-
#
|
48
|
-
#
|
49
|
-
#
|
50
|
-
#
|
53
|
+
# class MyServerHook
|
54
|
+
# include Sidekiq::ServerMiddleware
|
55
|
+
#
|
56
|
+
# def call(job_instance, msg, queue)
|
57
|
+
# logger.info "Before job"
|
58
|
+
# redis {|conn| conn.get("foo") } # do something in Redis
|
59
|
+
# yield
|
60
|
+
# logger.info "After job"
|
61
|
+
# end
|
51
62
|
# end
|
52
|
-
# end
|
53
63
|
#
|
54
64
|
# This is an example of a minimal client middleware, note
|
55
65
|
# the method must return the result or the job will not push
|
56
66
|
# to Redis:
|
57
67
|
#
|
58
|
-
#
|
59
|
-
#
|
60
|
-
#
|
61
|
-
#
|
62
|
-
#
|
63
|
-
#
|
68
|
+
# class MyClientHook
|
69
|
+
# include Sidekiq::ClientMiddleware
|
70
|
+
#
|
71
|
+
# def call(job_class, msg, queue, redis_pool)
|
72
|
+
# logger.info "Before push"
|
73
|
+
# result = yield
|
74
|
+
# logger.info "After push"
|
75
|
+
# result
|
76
|
+
# end
|
64
77
|
# end
|
65
|
-
# end
|
66
78
|
#
|
67
79
|
module Middleware
|
68
80
|
class Chain
|
69
81
|
include Enumerable
|
70
82
|
|
71
|
-
|
72
|
-
copy.instance_variable_set(:@entries, entries.dup)
|
73
|
-
end
|
74
|
-
|
83
|
+
# Iterate through each middleware in the chain
|
75
84
|
def each(&block)
|
76
85
|
entries.each(&block)
|
77
86
|
end
|
78
87
|
|
79
|
-
|
88
|
+
# @api private
|
89
|
+
def initialize(config = nil) # :nodoc:
|
90
|
+
@config = config
|
80
91
|
@entries = nil
|
81
92
|
yield self if block_given?
|
82
93
|
end
|
@@ -85,38 +96,62 @@ module Sidekiq
|
|
85
96
|
@entries ||= []
|
86
97
|
end
|
87
98
|
|
99
|
+
def copy_for(capsule)
|
100
|
+
chain = Sidekiq::Middleware::Chain.new(capsule)
|
101
|
+
chain.instance_variable_set(:@entries, entries.dup)
|
102
|
+
chain
|
103
|
+
end
|
104
|
+
|
105
|
+
# Remove all middleware matching the given Class
|
106
|
+
# @param klass [Class]
|
88
107
|
def remove(klass)
|
89
108
|
entries.delete_if { |entry| entry.klass == klass }
|
90
109
|
end
|
91
110
|
|
111
|
+
# Add the given middleware to the end of the chain.
|
112
|
+
# Sidekiq will call `klass.new(*args)` to create a clean
|
113
|
+
# copy of your middleware for every job executed.
|
114
|
+
#
|
115
|
+
# chain.add(Statsd::Metrics, { collector: "localhost:8125" })
|
116
|
+
#
|
117
|
+
# @param klass [Class] Your middleware class
|
118
|
+
# @param *args [Array<Object>] Set of arguments to pass to every instance of your middleware
|
92
119
|
def add(klass, *args)
|
93
|
-
remove(klass)
|
94
|
-
entries << Entry.new(klass, *args)
|
120
|
+
remove(klass)
|
121
|
+
entries << Entry.new(@config, klass, *args)
|
95
122
|
end
|
96
123
|
|
124
|
+
# Identical to {#add} except the middleware is added to the front of the chain.
|
97
125
|
def prepend(klass, *args)
|
98
|
-
remove(klass)
|
99
|
-
entries.insert(0, Entry.new(klass, *args))
|
126
|
+
remove(klass)
|
127
|
+
entries.insert(0, Entry.new(@config, klass, *args))
|
100
128
|
end
|
101
129
|
|
130
|
+
# Inserts +newklass+ before +oldklass+ in the chain.
|
131
|
+
# Useful if one middleware must run before another middleware.
|
102
132
|
def insert_before(oldklass, newklass, *args)
|
103
133
|
i = entries.index { |entry| entry.klass == newklass }
|
104
|
-
new_entry = i.nil? ? Entry.new(newklass, *args) : entries.delete_at(i)
|
134
|
+
new_entry = i.nil? ? Entry.new(@config, newklass, *args) : entries.delete_at(i)
|
105
135
|
i = entries.index { |entry| entry.klass == oldklass } || 0
|
106
136
|
entries.insert(i, new_entry)
|
107
137
|
end
|
108
138
|
|
139
|
+
# Inserts +newklass+ after +oldklass+ in the chain.
|
140
|
+
# Useful if one middleware must run after another middleware.
|
109
141
|
def insert_after(oldklass, newklass, *args)
|
110
142
|
i = entries.index { |entry| entry.klass == newklass }
|
111
|
-
new_entry = i.nil? ? Entry.new(newklass, *args) : entries.delete_at(i)
|
143
|
+
new_entry = i.nil? ? Entry.new(@config, newklass, *args) : entries.delete_at(i)
|
112
144
|
i = entries.index { |entry| entry.klass == oldklass } || entries.count - 1
|
113
145
|
entries.insert(i + 1, new_entry)
|
114
146
|
end
|
115
147
|
|
148
|
+
# @return [Boolean] if the given class is already in the chain
|
116
149
|
def exists?(klass)
|
117
150
|
any? { |entry| entry.klass == klass }
|
118
151
|
end
|
152
|
+
alias_method :include?, :exists?
|
119
153
|
|
154
|
+
# @return [Boolean] if the chain contains no middleware
|
120
155
|
def empty?
|
121
156
|
@entries.nil? || @entries.empty?
|
122
157
|
end
|
@@ -129,10 +164,12 @@ module Sidekiq
|
|
129
164
|
entries.clear
|
130
165
|
end
|
131
166
|
|
167
|
+
# Used by Sidekiq to execute the middleware at runtime
|
168
|
+
# @api private
|
132
169
|
def invoke(*args)
|
133
170
|
return yield if empty?
|
134
171
|
|
135
|
-
chain = retrieve
|
172
|
+
chain = retrieve
|
136
173
|
traverse_chain = proc do
|
137
174
|
if chain.empty?
|
138
175
|
yield
|
@@ -144,16 +181,23 @@ module Sidekiq
|
|
144
181
|
end
|
145
182
|
end
|
146
183
|
|
184
|
+
private
|
185
|
+
|
186
|
+
# Represents each link in the middleware chain
|
187
|
+
# @api private
|
147
188
|
class Entry
|
148
189
|
attr_reader :klass
|
149
190
|
|
150
|
-
def initialize(klass, *args)
|
191
|
+
def initialize(config, klass, *args)
|
192
|
+
@config = config
|
151
193
|
@klass = klass
|
152
194
|
@args = args
|
153
195
|
end
|
154
196
|
|
155
197
|
def make_new
|
156
|
-
@klass.new(*@args)
|
198
|
+
x = @klass.new(*@args)
|
199
|
+
x.config = @config if @config && x.respond_to?(:config=)
|
200
|
+
x
|
157
201
|
end
|
158
202
|
end
|
159
203
|
end
|
@@ -0,0 +1,58 @@
|
|
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
|
+
# @example
|
11
|
+
#
|
12
|
+
# # in your initializer
|
13
|
+
# require "sidekiq/middleware/current_attributes"
|
14
|
+
# Sidekiq::CurrentAttributes.persist("Myapp::Current")
|
15
|
+
#
|
16
|
+
module CurrentAttributes
|
17
|
+
class Save
|
18
|
+
include Sidekiq::ClientMiddleware
|
19
|
+
|
20
|
+
def initialize(cattr)
|
21
|
+
@strklass = cattr
|
22
|
+
end
|
23
|
+
|
24
|
+
def call(_, job, _, _)
|
25
|
+
attrs = @strklass.constantize.attributes
|
26
|
+
if attrs.any?
|
27
|
+
if job.has_key?("cattr")
|
28
|
+
job["cattr"].merge!(attrs)
|
29
|
+
else
|
30
|
+
job["cattr"] = attrs
|
31
|
+
end
|
32
|
+
end
|
33
|
+
yield
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class Load
|
38
|
+
include Sidekiq::ServerMiddleware
|
39
|
+
|
40
|
+
def initialize(cattr)
|
41
|
+
@strklass = cattr
|
42
|
+
end
|
43
|
+
|
44
|
+
def call(_, job, _, &block)
|
45
|
+
if job.has_key?("cattr")
|
46
|
+
@strklass.constantize.set(job["cattr"], &block)
|
47
|
+
else
|
48
|
+
yield
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.persist(klass, config = Sidekiq.default_configuration)
|
54
|
+
config.client_middleware.add Save, klass.to_s
|
55
|
+
config.server_middleware.add Load, klass.to_s
|
56
|
+
end
|
57
|
+
end
|
58
|
+
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
data/lib/sidekiq/paginator.rb
CHANGED
@@ -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.zrevrange(key, starting, ending, 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,9 @@ module Sidekiq
|
|
142
146
|
end
|
143
147
|
end
|
144
148
|
|
145
|
-
def process(
|
146
|
-
jobstr =
|
147
|
-
queue =
|
149
|
+
def process(uow)
|
150
|
+
jobstr = uow.job
|
151
|
+
queue = uow.queue_name
|
148
152
|
|
149
153
|
# Treat malformed JSON as a special case: job goes straight to the morgue.
|
150
154
|
job_hash = nil
|
@@ -152,16 +156,22 @@ module Sidekiq
|
|
152
156
|
job_hash = Sidekiq.load_json(jobstr)
|
153
157
|
rescue => ex
|
154
158
|
handle_exception(ex, {context: "Invalid JSON for job", jobstr: jobstr})
|
155
|
-
|
156
|
-
|
157
|
-
|
159
|
+
now = Time.now.to_f
|
160
|
+
redis do |conn|
|
161
|
+
conn.multi do |xa|
|
162
|
+
xa.zadd("dead", now.to_s, jobstr)
|
163
|
+
xa.zremrangebyscore("dead", "-inf", now - @capsule.config[:dead_timeout_in_seconds])
|
164
|
+
xa.zremrangebyrank("dead", 0, - @capsule.config[:dead_max_jobs])
|
165
|
+
end
|
166
|
+
end
|
167
|
+
return uow.acknowledge
|
158
168
|
end
|
159
169
|
|
160
170
|
ack = false
|
161
171
|
begin
|
162
|
-
dispatch(job_hash, queue, jobstr) do |
|
163
|
-
|
164
|
-
execute_job(
|
172
|
+
dispatch(job_hash, queue, jobstr) do |inst|
|
173
|
+
config.server_middleware.invoke(inst, job_hash, queue) do
|
174
|
+
execute_job(inst, job_hash["args"])
|
165
175
|
end
|
166
176
|
end
|
167
177
|
ack = true
|
@@ -174,7 +184,7 @@ module Sidekiq
|
|
174
184
|
# signals that we created a retry successfully. We can acknowlege the job.
|
175
185
|
ack = true
|
176
186
|
e = h.cause || h
|
177
|
-
handle_exception(e, {context: "Job raised exception", job: job_hash
|
187
|
+
handle_exception(e, {context: "Job raised exception", job: job_hash})
|
178
188
|
raise e
|
179
189
|
rescue Exception => ex
|
180
190
|
# Unexpected error! This is very bad and indicates an exception that got past
|
@@ -186,14 +196,14 @@ module Sidekiq
|
|
186
196
|
if ack
|
187
197
|
# We don't want a shutdown signal to interrupt job acknowledgment.
|
188
198
|
Thread.handle_interrupt(Sidekiq::Shutdown => :never) do
|
189
|
-
|
199
|
+
uow.acknowledge
|
190
200
|
end
|
191
201
|
end
|
192
202
|
end
|
193
203
|
end
|
194
204
|
|
195
|
-
def execute_job(
|
196
|
-
|
205
|
+
def execute_job(inst, cloned_args)
|
206
|
+
inst.perform(*cloned_args)
|
197
207
|
end
|
198
208
|
|
199
209
|
# Ruby doesn't provide atomic counters out of the box so we'll
|
@@ -219,39 +229,39 @@ module Sidekiq
|
|
219
229
|
end
|
220
230
|
|
221
231
|
# jruby's Hash implementation is not threadsafe, so we wrap it in a mutex here
|
222
|
-
class
|
232
|
+
class SharedWorkState
|
223
233
|
def initialize
|
224
|
-
@
|
234
|
+
@work_state = {}
|
225
235
|
@lock = Mutex.new
|
226
236
|
end
|
227
237
|
|
228
238
|
def set(tid, hash)
|
229
|
-
@lock.synchronize { @
|
239
|
+
@lock.synchronize { @work_state[tid] = hash }
|
230
240
|
end
|
231
241
|
|
232
242
|
def delete(tid)
|
233
|
-
@lock.synchronize { @
|
243
|
+
@lock.synchronize { @work_state.delete(tid) }
|
234
244
|
end
|
235
245
|
|
236
246
|
def dup
|
237
|
-
@lock.synchronize { @
|
247
|
+
@lock.synchronize { @work_state.dup }
|
238
248
|
end
|
239
249
|
|
240
250
|
def size
|
241
|
-
@lock.synchronize { @
|
251
|
+
@lock.synchronize { @work_state.size }
|
242
252
|
end
|
243
253
|
|
244
254
|
def clear
|
245
|
-
@lock.synchronize { @
|
255
|
+
@lock.synchronize { @work_state.clear }
|
246
256
|
end
|
247
257
|
end
|
248
258
|
|
249
259
|
PROCESSED = Counter.new
|
250
260
|
FAILURE = Counter.new
|
251
|
-
|
261
|
+
WORK_STATE = SharedWorkState.new
|
252
262
|
|
253
263
|
def stats(jobstr, queue)
|
254
|
-
|
264
|
+
WORK_STATE.set(tid, {queue: queue, payload: jobstr, run_at: Time.now.to_i})
|
255
265
|
|
256
266
|
begin
|
257
267
|
yield
|
@@ -259,22 +269,9 @@ module Sidekiq
|
|
259
269
|
FAILURE.incr
|
260
270
|
raise
|
261
271
|
ensure
|
262
|
-
|
272
|
+
WORK_STATE.delete(tid)
|
263
273
|
PROCESSED.incr
|
264
274
|
end
|
265
275
|
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
276
|
end
|
280
277
|
end
|