sidekiq 6.3.1 → 6.5.9
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of sidekiq might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/Changes.md +134 -0
- data/LICENSE +3 -3
- data/README.md +7 -2
- data/bin/sidekiq +3 -3
- data/bin/sidekiqload +70 -66
- 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 +261 -104
- data/lib/sidekiq/cli.rb +62 -38
- data/lib/sidekiq/client.rb +47 -67
- data/lib/sidekiq/{util.rb → component.rb} +12 -42
- data/lib/sidekiq/delay.rb +3 -1
- data/lib/sidekiq/extensions/generic_proxy.rb +1 -1
- data/lib/sidekiq/fetch.rb +20 -18
- data/lib/sidekiq/job_logger.rb +15 -27
- data/lib/sidekiq/job_retry.rb +78 -55
- data/lib/sidekiq/job_util.rb +71 -0
- data/lib/sidekiq/launcher.rb +58 -54
- data/lib/sidekiq/logger.rb +8 -18
- data/lib/sidekiq/manager.rb +35 -34
- data/lib/sidekiq/metrics/deploy.rb +47 -0
- data/lib/sidekiq/metrics/query.rb +153 -0
- data/lib/sidekiq/metrics/shared.rb +94 -0
- data/lib/sidekiq/metrics/tracking.rb +134 -0
- data/lib/sidekiq/middleware/chain.rb +82 -38
- data/lib/sidekiq/middleware/current_attributes.rb +19 -8
- data/lib/sidekiq/middleware/i18n.rb +6 -4
- data/lib/sidekiq/middleware/modules.rb +21 -0
- data/lib/sidekiq/monitor.rb +2 -2
- data/lib/sidekiq/paginator.rb +17 -9
- data/lib/sidekiq/processor.rb +47 -41
- data/lib/sidekiq/rails.rb +15 -8
- data/lib/sidekiq/redis_client_adapter.rb +154 -0
- data/lib/sidekiq/redis_connection.rb +80 -49
- data/lib/sidekiq/ring_buffer.rb +29 -0
- data/lib/sidekiq/scheduled.rb +66 -27
- data/lib/sidekiq/testing/inline.rb +4 -4
- data/lib/sidekiq/testing.rb +37 -36
- data/lib/sidekiq/transaction_aware_client.rb +45 -0
- data/lib/sidekiq/version.rb +1 -1
- data/lib/sidekiq/web/action.rb +3 -3
- data/lib/sidekiq/web/application.rb +26 -7
- data/lib/sidekiq/web/csrf_protection.rb +2 -2
- data/lib/sidekiq/web/helpers.rb +21 -8
- data/lib/sidekiq/web.rb +8 -4
- data/lib/sidekiq/worker.rb +78 -19
- data/lib/sidekiq.rb +111 -30
- data/sidekiq.gemspec +2 -2
- data/web/assets/javascripts/application.js +58 -26
- 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.js +0 -17
- data/web/assets/javascripts/graph.js +16 -0
- data/web/assets/javascripts/metrics.js +262 -0
- data/web/assets/stylesheets/application-dark.css +13 -17
- data/web/assets/stylesheets/application.css +48 -6
- data/web/locales/el.yml +43 -19
- data/web/locales/en.yml +7 -0
- data/web/locales/ja.yml +7 -0
- data/web/locales/pt-br.yml +27 -9
- data/web/locales/zh-cn.yml +36 -11
- data/web/locales/zh-tw.yml +32 -7
- data/web/views/_nav.erb +1 -1
- data/web/views/_summary.erb +1 -1
- data/web/views/busy.erb +9 -4
- data/web/views/dashboard.erb +1 -0
- data/web/views/metrics.erb +69 -0
- data/web/views/metrics_for_job.erb +87 -0
- data/web/views/queue.erb +5 -1
- metadata +39 -13
- data/lib/generators/sidekiq/worker_generator.rb +0 -57
- data/lib/sidekiq/exception_handler.rb +0 -27
data/lib/sidekiq/testing.rb
CHANGED
@@ -101,20 +101,20 @@ module Sidekiq
|
|
101
101
|
##
|
102
102
|
# The Queues class is only for testing the fake queue implementation.
|
103
103
|
# There are 2 data structures involved in tandem. This is due to the
|
104
|
-
# Rspec syntax of change(
|
104
|
+
# Rspec syntax of change(HardJob.jobs, :size). It keeps a reference
|
105
105
|
# to the array. Because the array was dervied from a filter of the total
|
106
106
|
# jobs enqueued, it appeared as though the array didn't change.
|
107
107
|
#
|
108
108
|
# To solve this, we'll keep 2 hashes containing the jobs. One with keys based
|
109
|
-
# on the queue, and another with keys of the
|
110
|
-
#
|
109
|
+
# on the queue, and another with keys of the job type, so the array for
|
110
|
+
# HardJob.jobs is a straight reference to a real array.
|
111
111
|
#
|
112
112
|
# Queue-based hash:
|
113
113
|
#
|
114
114
|
# {
|
115
115
|
# "default"=>[
|
116
116
|
# {
|
117
|
-
# "class"=>"TestTesting::
|
117
|
+
# "class"=>"TestTesting::HardJob",
|
118
118
|
# "args"=>[1, 2],
|
119
119
|
# "retry"=>true,
|
120
120
|
# "queue"=>"default",
|
@@ -124,12 +124,12 @@ module Sidekiq
|
|
124
124
|
# ]
|
125
125
|
# }
|
126
126
|
#
|
127
|
-
#
|
127
|
+
# Job-based hash:
|
128
128
|
#
|
129
129
|
# {
|
130
|
-
# "TestTesting::
|
130
|
+
# "TestTesting::HardJob"=>[
|
131
131
|
# {
|
132
|
-
# "class"=>"TestTesting::
|
132
|
+
# "class"=>"TestTesting::HardJob",
|
133
133
|
# "args"=>[1, 2],
|
134
134
|
# "retry"=>true,
|
135
135
|
# "queue"=>"default",
|
@@ -144,14 +144,14 @@ module Sidekiq
|
|
144
144
|
# require 'sidekiq/testing'
|
145
145
|
#
|
146
146
|
# assert_equal 0, Sidekiq::Queues["default"].size
|
147
|
-
#
|
147
|
+
# HardJob.perform_async(:something)
|
148
148
|
# assert_equal 1, Sidekiq::Queues["default"].size
|
149
149
|
# assert_equal :something, Sidekiq::Queues["default"].first['args'][0]
|
150
150
|
#
|
151
|
-
# You can also clear all
|
151
|
+
# You can also clear all jobs:
|
152
152
|
#
|
153
153
|
# assert_equal 0, Sidekiq::Queues["default"].size
|
154
|
-
#
|
154
|
+
# HardJob.perform_async(:something)
|
155
155
|
# Sidekiq::Queues.clear_all
|
156
156
|
# assert_equal 0, Sidekiq::Queues["default"].size
|
157
157
|
#
|
@@ -170,35 +170,36 @@ module Sidekiq
|
|
170
170
|
|
171
171
|
def push(queue, klass, job)
|
172
172
|
jobs_by_queue[queue] << job
|
173
|
-
|
173
|
+
jobs_by_class[klass] << job
|
174
174
|
end
|
175
175
|
|
176
176
|
def jobs_by_queue
|
177
177
|
@jobs_by_queue ||= Hash.new { |hash, key| hash[key] = [] }
|
178
178
|
end
|
179
179
|
|
180
|
-
def
|
181
|
-
@
|
180
|
+
def jobs_by_class
|
181
|
+
@jobs_by_class ||= Hash.new { |hash, key| hash[key] = [] }
|
182
182
|
end
|
183
|
+
alias_method :jobs_by_worker, :jobs_by_class
|
183
184
|
|
184
185
|
def delete_for(jid, queue, klass)
|
185
186
|
jobs_by_queue[queue.to_s].delete_if { |job| job["jid"] == jid }
|
186
|
-
|
187
|
+
jobs_by_class[klass].delete_if { |job| job["jid"] == jid }
|
187
188
|
end
|
188
189
|
|
189
190
|
def clear_for(queue, klass)
|
190
|
-
jobs_by_queue[queue].clear
|
191
|
-
|
191
|
+
jobs_by_queue[queue.to_s].clear
|
192
|
+
jobs_by_class[klass].clear
|
192
193
|
end
|
193
194
|
|
194
195
|
def clear_all
|
195
196
|
jobs_by_queue.clear
|
196
|
-
|
197
|
+
jobs_by_class.clear
|
197
198
|
end
|
198
199
|
end
|
199
200
|
end
|
200
201
|
|
201
|
-
module
|
202
|
+
module Job
|
202
203
|
##
|
203
204
|
# The Sidekiq testing infrastructure overrides perform_async
|
204
205
|
# so that it does not actually touch the network. Instead it
|
@@ -212,16 +213,16 @@ module Sidekiq
|
|
212
213
|
#
|
213
214
|
# require 'sidekiq/testing'
|
214
215
|
#
|
215
|
-
# assert_equal 0,
|
216
|
-
#
|
217
|
-
# assert_equal 1,
|
218
|
-
# assert_equal :something,
|
216
|
+
# assert_equal 0, HardJob.jobs.size
|
217
|
+
# HardJob.perform_async(:something)
|
218
|
+
# assert_equal 1, HardJob.jobs.size
|
219
|
+
# assert_equal :something, HardJob.jobs[0]['args'][0]
|
219
220
|
#
|
220
221
|
# assert_equal 0, Sidekiq::Extensions::DelayedMailer.jobs.size
|
221
222
|
# MyMailer.delay.send_welcome_email('foo@example.com')
|
222
223
|
# assert_equal 1, Sidekiq::Extensions::DelayedMailer.jobs.size
|
223
224
|
#
|
224
|
-
# You can also clear and drain all
|
225
|
+
# You can also clear and drain all job types:
|
225
226
|
#
|
226
227
|
# assert_equal 0, Sidekiq::Extensions::DelayedMailer.jobs.size
|
227
228
|
# assert_equal 0, Sidekiq::Extensions::DelayedModel.jobs.size
|
@@ -241,14 +242,14 @@ module Sidekiq
|
|
241
242
|
#
|
242
243
|
# RSpec.configure do |config|
|
243
244
|
# config.before(:each) do
|
244
|
-
# Sidekiq::
|
245
|
+
# Sidekiq::Job.clear_all
|
245
246
|
# end
|
246
247
|
# end
|
247
248
|
#
|
248
249
|
# or for acceptance testing, i.e. with cucumber:
|
249
250
|
#
|
250
251
|
# AfterStep do
|
251
|
-
# Sidekiq::
|
252
|
+
# Sidekiq::Job.drain_all
|
252
253
|
# end
|
253
254
|
#
|
254
255
|
# When I sign up as "foo@example.com"
|
@@ -262,7 +263,7 @@ module Sidekiq
|
|
262
263
|
|
263
264
|
# Jobs queued for this worker
|
264
265
|
def jobs
|
265
|
-
Queues.
|
266
|
+
Queues.jobs_by_class[to_s]
|
266
267
|
end
|
267
268
|
|
268
269
|
# Clear all jobs for this worker
|
@@ -288,11 +289,11 @@ module Sidekiq
|
|
288
289
|
end
|
289
290
|
|
290
291
|
def process_job(job)
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
Sidekiq::Testing.server_middleware.invoke(
|
295
|
-
execute_job(
|
292
|
+
inst = new
|
293
|
+
inst.jid = job["jid"]
|
294
|
+
inst.bid = job["bid"] if inst.respond_to?(:bid=)
|
295
|
+
Sidekiq::Testing.server_middleware.invoke(inst, job, job["queue"]) do
|
296
|
+
execute_job(inst, job["args"])
|
296
297
|
end
|
297
298
|
end
|
298
299
|
|
@@ -306,18 +307,18 @@ module Sidekiq
|
|
306
307
|
Queues.jobs_by_queue.values.flatten
|
307
308
|
end
|
308
309
|
|
309
|
-
# Clear all queued jobs
|
310
|
+
# Clear all queued jobs
|
310
311
|
def clear_all
|
311
312
|
Queues.clear_all
|
312
313
|
end
|
313
314
|
|
314
|
-
# Drain all queued jobs
|
315
|
+
# Drain (execute) all queued jobs
|
315
316
|
def drain_all
|
316
317
|
while jobs.any?
|
317
|
-
|
318
|
+
job_classes = jobs.map { |job| job["class"] }.uniq
|
318
319
|
|
319
|
-
|
320
|
-
Sidekiq::Testing.constantize(
|
320
|
+
job_classes.each do |job_class|
|
321
|
+
Sidekiq::Testing.constantize(job_class).drain
|
321
322
|
end
|
322
323
|
end
|
323
324
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "securerandom"
|
4
|
+
require "sidekiq/client"
|
5
|
+
|
6
|
+
module Sidekiq
|
7
|
+
class TransactionAwareClient
|
8
|
+
def initialize(redis_pool)
|
9
|
+
@redis_client = Client.new(redis_pool)
|
10
|
+
end
|
11
|
+
|
12
|
+
def push(item)
|
13
|
+
# pre-allocate the JID so we can return it immediately and
|
14
|
+
# save it to the database as part of the transaction.
|
15
|
+
item["jid"] ||= SecureRandom.hex(12)
|
16
|
+
AfterCommitEverywhere.after_commit { @redis_client.push(item) }
|
17
|
+
item["jid"]
|
18
|
+
end
|
19
|
+
|
20
|
+
##
|
21
|
+
# We don't provide transactionality for push_bulk because we don't want
|
22
|
+
# to hold potentially hundreds of thousands of job records in memory due to
|
23
|
+
# a long running enqueue process.
|
24
|
+
def push_bulk(items)
|
25
|
+
@redis_client.push_bulk(items)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
##
|
31
|
+
# Use `Sidekiq.transactional_push!` in your sidekiq.rb initializer
|
32
|
+
module Sidekiq
|
33
|
+
def self.transactional_push!
|
34
|
+
begin
|
35
|
+
require "after_commit_everywhere"
|
36
|
+
rescue LoadError
|
37
|
+
Sidekiq.logger.error("You need to add after_commit_everywhere to your Gemfile to use Sidekiq's transactional client")
|
38
|
+
raise
|
39
|
+
end
|
40
|
+
|
41
|
+
default_job_options["client_class"] = Sidekiq::TransactionAwareClient
|
42
|
+
Sidekiq::JobUtil::TRANSIENT_ATTRIBUTES << "client_class"
|
43
|
+
true
|
44
|
+
end
|
45
|
+
end
|
data/lib/sidekiq/version.rb
CHANGED
data/lib/sidekiq/web/action.rb
CHANGED
@@ -15,11 +15,11 @@ module Sidekiq
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def halt(res)
|
18
|
-
throw :halt, [res, {"
|
18
|
+
throw :halt, [res, {"content-type" => "text/plain"}, [res.to_s]]
|
19
19
|
end
|
20
20
|
|
21
21
|
def redirect(location)
|
22
|
-
throw :halt, [302, {"
|
22
|
+
throw :halt, [302, {"location" => "#{request.base_url}#{location}"}, []]
|
23
23
|
end
|
24
24
|
|
25
25
|
def params
|
@@ -68,7 +68,7 @@ module Sidekiq
|
|
68
68
|
end
|
69
69
|
|
70
70
|
def json(payload)
|
71
|
-
[200, {"
|
71
|
+
[200, {"content-type" => "application/json", "cache-control" => "private, no-store"}, [Sidekiq.dump_json(payload)]]
|
72
72
|
end
|
73
73
|
|
74
74
|
def initialize(env, block)
|
@@ -50,14 +50,33 @@ module Sidekiq
|
|
50
50
|
|
51
51
|
get "/" do
|
52
52
|
@redis_info = redis_info.select { |k, v| REDIS_KEYS.include? k }
|
53
|
-
|
53
|
+
days = (params["days"] || 30).to_i
|
54
|
+
return halt(401) if days < 1 || days > 180
|
55
|
+
|
56
|
+
stats_history = Sidekiq::Stats::History.new(days)
|
54
57
|
@processed_history = stats_history.processed
|
55
58
|
@failed_history = stats_history.failed
|
56
59
|
|
57
60
|
erb(:dashboard)
|
58
61
|
end
|
59
62
|
|
63
|
+
get "/metrics" do
|
64
|
+
q = Sidekiq::Metrics::Query.new
|
65
|
+
@query_result = q.top_jobs
|
66
|
+
erb(:metrics)
|
67
|
+
end
|
68
|
+
|
69
|
+
get "/metrics/:name" do
|
70
|
+
@name = route_params[:name]
|
71
|
+
q = Sidekiq::Metrics::Query.new
|
72
|
+
@query_result = q.for_job(@name)
|
73
|
+
erb(:metrics_for_job)
|
74
|
+
end
|
75
|
+
|
60
76
|
get "/busy" do
|
77
|
+
@count = (params["count"] || 100).to_i
|
78
|
+
(@current_page, @total_size, @workset) = page_items(workset, params["page"], @count)
|
79
|
+
|
61
80
|
erb(:busy)
|
62
81
|
end
|
63
82
|
|
@@ -296,10 +315,10 @@ module Sidekiq
|
|
296
315
|
|
297
316
|
def call(env)
|
298
317
|
action = self.class.match(env)
|
299
|
-
return [404, {"
|
318
|
+
return [404, {"content-type" => "text/plain", "x-cascade" => "pass"}, ["Not Found"]] unless action
|
300
319
|
|
301
320
|
app = @klass
|
302
|
-
resp = catch(:halt) do
|
321
|
+
resp = catch(:halt) do
|
303
322
|
self.class.run_befores(app, action)
|
304
323
|
action.instance_exec env, &action.block
|
305
324
|
ensure
|
@@ -313,10 +332,10 @@ module Sidekiq
|
|
313
332
|
else
|
314
333
|
# rendered content goes here
|
315
334
|
headers = {
|
316
|
-
"
|
317
|
-
"
|
318
|
-
"
|
319
|
-
"
|
335
|
+
"content-type" => "text/html",
|
336
|
+
"cache-control" => "private, no-store",
|
337
|
+
"content-language" => action.locale,
|
338
|
+
"content-security-policy" => CSP_HEADER
|
320
339
|
}
|
321
340
|
# we'll let Rack calculate Content-Length for us.
|
322
341
|
[200, headers, [resp]]
|
@@ -143,7 +143,7 @@ module Sidekiq
|
|
143
143
|
one_time_pad = SecureRandom.random_bytes(token.length)
|
144
144
|
encrypted_token = xor_byte_strings(one_time_pad, token)
|
145
145
|
masked_token = one_time_pad + encrypted_token
|
146
|
-
Base64.
|
146
|
+
Base64.urlsafe_encode64(masked_token)
|
147
147
|
end
|
148
148
|
|
149
149
|
# Essentially the inverse of +mask_token+.
|
@@ -169,7 +169,7 @@ module Sidekiq
|
|
169
169
|
end
|
170
170
|
|
171
171
|
def decode_token(token)
|
172
|
-
Base64.
|
172
|
+
Base64.urlsafe_decode64(token)
|
173
173
|
end
|
174
174
|
|
175
175
|
def xor_byte_strings(s1, s2)
|
data/lib/sidekiq/web/helpers.rb
CHANGED
@@ -15,7 +15,7 @@ module Sidekiq
|
|
15
15
|
# so extensions can be localized
|
16
16
|
@strings[lang] ||= settings.locales.each_with_object({}) do |path, global|
|
17
17
|
find_locale_files(lang).each do |file|
|
18
|
-
strs = YAML.
|
18
|
+
strs = YAML.safe_load(File.open(file))
|
19
19
|
global.merge!(strs[lang])
|
20
20
|
end
|
21
21
|
end
|
@@ -137,17 +137,30 @@ module Sidekiq
|
|
137
137
|
end
|
138
138
|
|
139
139
|
def sort_direction_label
|
140
|
-
params[:direction] == "asc" ? "↑" : "↓"
|
140
|
+
(params[:direction] == "asc") ? "↑" : "↓"
|
141
141
|
end
|
142
142
|
|
143
|
-
def
|
144
|
-
@
|
143
|
+
def workset
|
144
|
+
@work ||= Sidekiq::WorkSet.new
|
145
145
|
end
|
146
146
|
|
147
147
|
def processes
|
148
148
|
@processes ||= Sidekiq::ProcessSet.new
|
149
149
|
end
|
150
150
|
|
151
|
+
# Sorts processes by hostname following the natural sort order
|
152
|
+
def sorted_processes
|
153
|
+
@sorted_processes ||= begin
|
154
|
+
return processes unless processes.all? { |p| p["hostname"] }
|
155
|
+
|
156
|
+
processes.to_a.sort_by do |process|
|
157
|
+
# Kudos to `shurikk` on StackOverflow
|
158
|
+
# https://stackoverflow.com/a/15170063/575547
|
159
|
+
process["hostname"].split(/(\d+)/).map { |a| /\d+/.match?(a) ? a.to_i : a }
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
151
164
|
def stats
|
152
165
|
@stats ||= Sidekiq::Stats.new
|
153
166
|
end
|
@@ -175,7 +188,7 @@ module Sidekiq
|
|
175
188
|
end
|
176
189
|
|
177
190
|
def current_status
|
178
|
-
|
191
|
+
(workset.size == 0) ? "idle" : "active"
|
179
192
|
end
|
180
193
|
|
181
194
|
def relative_time(time)
|
@@ -208,7 +221,7 @@ module Sidekiq
|
|
208
221
|
end
|
209
222
|
|
210
223
|
def truncate(text, truncate_after_chars = 2000)
|
211
|
-
truncate_after_chars && text.size > truncate_after_chars ? "#{text[0..truncate_after_chars]}..." : text
|
224
|
+
(truncate_after_chars && text.size > truncate_after_chars) ? "#{text[0..truncate_after_chars]}..." : text
|
212
225
|
end
|
213
226
|
|
214
227
|
def display_args(args, truncate_after_chars = 2000)
|
@@ -242,7 +255,7 @@ module Sidekiq
|
|
242
255
|
queue class args retry_count retried_at failed_at
|
243
256
|
jid error_message error_class backtrace
|
244
257
|
error_backtrace enqueued_at retry wrapped
|
245
|
-
created_at tags
|
258
|
+
created_at tags display_class
|
246
259
|
])
|
247
260
|
|
248
261
|
def retry_extra_items(retry_job)
|
@@ -301,7 +314,7 @@ module Sidekiq
|
|
301
314
|
end
|
302
315
|
|
303
316
|
def environment_title_prefix
|
304
|
-
environment = Sidekiq
|
317
|
+
environment = Sidekiq[:environment] || ENV["APP_ENV"] || ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
|
305
318
|
|
306
319
|
"[#{environment.upcase}] " unless environment == "production"
|
307
320
|
end
|
data/lib/sidekiq/web.rb
CHANGED
@@ -33,6 +33,10 @@ module Sidekiq
|
|
33
33
|
"Dead" => "morgue"
|
34
34
|
}
|
35
35
|
|
36
|
+
if ENV["SIDEKIQ_METRICS_BETA"] == "1"
|
37
|
+
DEFAULT_TABS["Metrics"] = "metrics"
|
38
|
+
end
|
39
|
+
|
36
40
|
class << self
|
37
41
|
def settings
|
38
42
|
self
|
@@ -144,13 +148,13 @@ module Sidekiq
|
|
144
148
|
m = middlewares
|
145
149
|
|
146
150
|
rules = []
|
147
|
-
rules = [[:all, {"
|
151
|
+
rules = [[:all, {"cache-control" => "public, max-age=86400"}]] unless ENV["SIDEKIQ_WEB_TESTING"]
|
148
152
|
|
149
153
|
::Rack::Builder.new do
|
150
154
|
use Rack::Static, urls: ["/stylesheets", "/images", "/javascripts"],
|
151
|
-
|
152
|
-
|
153
|
-
|
155
|
+
root: ASSETS,
|
156
|
+
cascade: true,
|
157
|
+
header_rules: rules
|
154
158
|
m.each { |middleware, block| use(*middleware, &block) }
|
155
159
|
use Sidekiq::Web::CsrfProtection unless $TESTING
|
156
160
|
run WebApplication.new(klass)
|
data/lib/sidekiq/worker.rb
CHANGED
@@ -82,7 +82,7 @@ module Sidekiq
|
|
82
82
|
end
|
83
83
|
|
84
84
|
def get_sidekiq_options # :nodoc:
|
85
|
-
self.sidekiq_options_hash ||= Sidekiq.
|
85
|
+
self.sidekiq_options_hash ||= Sidekiq.default_job_options
|
86
86
|
end
|
87
87
|
|
88
88
|
def sidekiq_class_attribute(*attrs)
|
@@ -171,30 +171,78 @@ module Sidekiq
|
|
171
171
|
# SomeWorker.set(queue: 'foo').perform_async(....)
|
172
172
|
#
|
173
173
|
class Setter
|
174
|
+
include Sidekiq::JobUtil
|
175
|
+
|
174
176
|
def initialize(klass, opts)
|
175
177
|
@klass = klass
|
176
|
-
|
178
|
+
# NB: the internal hash always has stringified keys
|
179
|
+
@opts = opts.transform_keys(&:to_s)
|
177
180
|
|
178
181
|
# ActiveJob compatibility
|
179
|
-
interval = @opts.delete(
|
182
|
+
interval = @opts.delete("wait_until") || @opts.delete("wait")
|
180
183
|
at(interval) if interval
|
181
184
|
end
|
182
185
|
|
183
186
|
def set(options)
|
184
|
-
|
185
|
-
@opts.
|
187
|
+
hash = options.transform_keys(&:to_s)
|
188
|
+
interval = hash.delete("wait_until") || @opts.delete("wait")
|
189
|
+
@opts.merge!(hash)
|
186
190
|
at(interval) if interval
|
187
191
|
self
|
188
192
|
end
|
189
193
|
|
190
194
|
def perform_async(*args)
|
191
|
-
@
|
195
|
+
if @opts["sync"] == true
|
196
|
+
perform_inline(*args)
|
197
|
+
else
|
198
|
+
@klass.client_push(@opts.merge("args" => args, "class" => @klass))
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
# Explicit inline execution of a job. Returns nil if the job did not
|
203
|
+
# execute, true otherwise.
|
204
|
+
def perform_inline(*args)
|
205
|
+
raw = @opts.merge("args" => args, "class" => @klass)
|
206
|
+
|
207
|
+
# validate and normalize payload
|
208
|
+
item = normalize_item(raw)
|
209
|
+
queue = item["queue"]
|
210
|
+
|
211
|
+
# run client-side middleware
|
212
|
+
result = Sidekiq.client_middleware.invoke(item["class"], item, queue, Sidekiq.redis_pool) do
|
213
|
+
item
|
214
|
+
end
|
215
|
+
return nil unless result
|
216
|
+
|
217
|
+
# round-trip the payload via JSON
|
218
|
+
msg = Sidekiq.load_json(Sidekiq.dump_json(item))
|
219
|
+
|
220
|
+
# prepare the job instance
|
221
|
+
klass = msg["class"].constantize
|
222
|
+
job = klass.new
|
223
|
+
job.jid = msg["jid"]
|
224
|
+
job.bid = msg["bid"] if job.respond_to?(:bid)
|
225
|
+
|
226
|
+
# run the job through server-side middleware
|
227
|
+
result = Sidekiq.server_middleware.invoke(job, msg, msg["queue"]) do
|
228
|
+
# perform it
|
229
|
+
job.perform(*msg["args"])
|
230
|
+
true
|
231
|
+
end
|
232
|
+
return nil unless result
|
233
|
+
# jobs do not return a result. they should store any
|
234
|
+
# modified state.
|
235
|
+
true
|
192
236
|
end
|
237
|
+
alias_method :perform_sync, :perform_inline
|
193
238
|
|
194
239
|
def perform_bulk(args, batch_size: 1_000)
|
195
|
-
|
196
|
-
|
240
|
+
client = @klass.build_client
|
241
|
+
result = args.each_slice(batch_size).flat_map do |slice|
|
242
|
+
client.push_bulk(@opts.merge("class" => @klass, "args" => slice))
|
197
243
|
end
|
244
|
+
|
245
|
+
result.is_a?(Enumerator::Lazy) ? result.force : result
|
198
246
|
end
|
199
247
|
|
200
248
|
# +interval+ must be a timestamp, numeric or something that acts
|
@@ -209,7 +257,7 @@ module Sidekiq
|
|
209
257
|
def at(interval)
|
210
258
|
int = interval.to_f
|
211
259
|
now = Time.now.to_f
|
212
|
-
ts = (int < 1_000_000_000 ? now + int : int)
|
260
|
+
ts = ((int < 1_000_000_000) ? now + int : int)
|
213
261
|
# Optimization to enqueue something now that is scheduled to go out now or in the past
|
214
262
|
@opts["at"] = ts if ts > now
|
215
263
|
self
|
@@ -238,8 +286,14 @@ module Sidekiq
|
|
238
286
|
end
|
239
287
|
|
240
288
|
def perform_async(*args)
|
241
|
-
|
289
|
+
Setter.new(self, {}).perform_async(*args)
|
290
|
+
end
|
291
|
+
|
292
|
+
# Inline execution of job's perform method after passing through Sidekiq.client_middleware and Sidekiq.server_middleware
|
293
|
+
def perform_inline(*args)
|
294
|
+
Setter.new(self, {}).perform_inline(*args)
|
242
295
|
end
|
296
|
+
alias_method :perform_sync, :perform_inline
|
243
297
|
|
244
298
|
##
|
245
299
|
# Push a large number of jobs to Redis, while limiting the batch of
|
@@ -261,10 +315,8 @@ module Sidekiq
|
|
261
315
|
#
|
262
316
|
# SomeWorker.perform_bulk([[1], [2], [3]])
|
263
317
|
#
|
264
|
-
def perform_bulk(
|
265
|
-
|
266
|
-
Sidekiq::Client.push_bulk("class" => self, "args" => slice)
|
267
|
-
end
|
318
|
+
def perform_bulk(*args, **kwargs)
|
319
|
+
Setter.new(self, {}).perform_bulk(*args, **kwargs)
|
268
320
|
end
|
269
321
|
|
270
322
|
# +interval+ must be a timestamp, numeric or something that acts
|
@@ -272,7 +324,7 @@ module Sidekiq
|
|
272
324
|
def perform_in(interval, *args)
|
273
325
|
int = interval.to_f
|
274
326
|
now = Time.now.to_f
|
275
|
-
ts = (int < 1_000_000_000 ? now + int : int)
|
327
|
+
ts = ((int < 1_000_000_000) ? now + int : int)
|
276
328
|
|
277
329
|
item = {"class" => self, "args" => args}
|
278
330
|
|
@@ -288,7 +340,7 @@ module Sidekiq
|
|
288
340
|
# Legal options:
|
289
341
|
#
|
290
342
|
# queue - use a named queue for this Worker, default 'default'
|
291
|
-
# retry - enable
|
343
|
+
# retry - enable retries via JobRetry, *true* to use the default
|
292
344
|
# or *Integer* count
|
293
345
|
# backtrace - whether to save any error backtrace in the retry payload to display in web UI,
|
294
346
|
# can be true, false or an integer number of lines to save, default *false*
|
@@ -296,15 +348,22 @@ module Sidekiq
|
|
296
348
|
#
|
297
349
|
# In practice, any option is allowed. This is the main mechanism to configure the
|
298
350
|
# options for a specific job.
|
351
|
+
#
|
352
|
+
# These options will be saved into the serialized job when enqueued by
|
353
|
+
# the client.
|
299
354
|
def sidekiq_options(opts = {})
|
300
355
|
super
|
301
356
|
end
|
302
357
|
|
303
358
|
def client_push(item) # :nodoc:
|
304
|
-
|
305
|
-
|
359
|
+
raise ArgumentError, "Job payloads should contain no Symbols: #{item}" if item.any? { |k, v| k.is_a?(::Symbol) }
|
360
|
+
build_client.push(item)
|
361
|
+
end
|
306
362
|
|
307
|
-
|
363
|
+
def build_client # :nodoc:
|
364
|
+
pool = Thread.current[:sidekiq_via_pool] || get_sidekiq_options["pool"] || Sidekiq.redis_pool
|
365
|
+
client_class = get_sidekiq_options["client_class"] || Sidekiq::Client
|
366
|
+
client_class.new(pool)
|
308
367
|
end
|
309
368
|
end
|
310
369
|
end
|