sidekiq 6.0.0 → 6.4.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 +290 -2
- data/LICENSE +3 -3
- data/README.md +7 -9
- data/bin/sidekiq +26 -2
- data/bin/sidekiqload +8 -4
- data/bin/sidekiqmon +4 -5
- 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 +222 -145
- data/lib/sidekiq/cli.rb +67 -28
- data/lib/sidekiq/client.rb +17 -34
- data/lib/sidekiq/delay.rb +2 -0
- data/lib/sidekiq/extensions/action_mailer.rb +5 -4
- data/lib/sidekiq/extensions/active_record.rb +6 -5
- data/lib/sidekiq/extensions/class_methods.rb +7 -6
- data/lib/sidekiq/extensions/generic_proxy.rb +5 -3
- data/lib/sidekiq/fetch.rb +36 -27
- data/lib/sidekiq/job.rb +13 -0
- data/lib/sidekiq/job_logger.rb +13 -5
- data/lib/sidekiq/job_retry.rb +33 -21
- data/lib/sidekiq/job_util.rb +65 -0
- data/lib/sidekiq/launcher.rb +110 -28
- data/lib/sidekiq/logger.rb +109 -12
- data/lib/sidekiq/manager.rb +10 -12
- data/lib/sidekiq/middleware/chain.rb +17 -6
- data/lib/sidekiq/middleware/current_attributes.rb +57 -0
- data/lib/sidekiq/monitor.rb +3 -18
- data/lib/sidekiq/paginator.rb +7 -2
- data/lib/sidekiq/processor.rb +22 -24
- data/lib/sidekiq/rails.rb +27 -18
- data/lib/sidekiq/redis_connection.rb +19 -13
- data/lib/sidekiq/scheduled.rb +48 -12
- data/lib/sidekiq/sd_notify.rb +149 -0
- data/lib/sidekiq/systemd.rb +24 -0
- data/lib/sidekiq/testing.rb +14 -4
- data/lib/sidekiq/util.rb +40 -1
- data/lib/sidekiq/version.rb +1 -1
- data/lib/sidekiq/web/action.rb +2 -2
- data/lib/sidekiq/web/application.rb +41 -31
- data/lib/sidekiq/web/csrf_protection.rb +180 -0
- data/lib/sidekiq/web/helpers.rb +51 -33
- data/lib/sidekiq/web/router.rb +6 -5
- data/lib/sidekiq/web.rb +37 -73
- data/lib/sidekiq/worker.rb +133 -16
- data/lib/sidekiq.rb +29 -8
- data/sidekiq.gemspec +13 -6
- data/web/assets/images/apple-touch-icon.png +0 -0
- data/web/assets/javascripts/application.js +83 -64
- data/web/assets/javascripts/dashboard.js +53 -53
- data/web/assets/stylesheets/application-dark.css +143 -0
- data/web/assets/stylesheets/application-rtl.css +0 -4
- data/web/assets/stylesheets/application.css +43 -232
- data/web/locales/ar.yml +8 -2
- data/web/locales/de.yml +14 -2
- data/web/locales/en.yml +6 -1
- data/web/locales/es.yml +18 -2
- data/web/locales/fr.yml +10 -3
- data/web/locales/ja.yml +5 -0
- data/web/locales/lt.yml +83 -0
- data/web/locales/pl.yml +4 -4
- data/web/locales/ru.yml +4 -0
- data/web/locales/vi.yml +83 -0
- data/web/views/_footer.erb +1 -1
- data/web/views/_job_info.erb +3 -2
- data/web/views/_poll_link.erb +2 -5
- data/web/views/_summary.erb +7 -7
- data/web/views/busy.erb +54 -20
- data/web/views/dashboard.erb +22 -14
- data/web/views/dead.erb +3 -3
- data/web/views/layout.erb +3 -1
- data/web/views/morgue.erb +9 -6
- data/web/views/queue.erb +19 -10
- data/web/views/queues.erb +10 -2
- data/web/views/retries.erb +11 -8
- data/web/views/retry.erb +3 -3
- data/web/views/scheduled.erb +5 -2
- metadata +34 -54
- data/.circleci/config.yml +0 -61
- data/.github/contributing.md +0 -32
- data/.github/issue_template.md +0 -11
- data/.gitignore +0 -13
- data/.standard.yml +0 -20
- data/3.0-Upgrade.md +0 -70
- data/4.0-Upgrade.md +0 -53
- data/5.0-Upgrade.md +0 -56
- data/6.0-Upgrade.md +0 -70
- data/COMM-LICENSE +0 -97
- data/Ent-2.0-Upgrade.md +0 -37
- data/Ent-Changes.md +0 -250
- data/Gemfile +0 -24
- data/Gemfile.lock +0 -196
- data/Pro-2.0-Upgrade.md +0 -138
- data/Pro-3.0-Upgrade.md +0 -44
- data/Pro-4.0-Upgrade.md +0 -35
- data/Pro-5.0-Upgrade.md +0 -25
- data/Pro-Changes.md +0 -768
- data/Rakefile +0 -10
- data/code_of_conduct.md +0 -50
- data/lib/generators/sidekiq/worker_generator.rb +0 -47
data/lib/sidekiq/web/helpers.rb
CHANGED
@@ -10,18 +10,25 @@ module Sidekiq
|
|
10
10
|
module WebHelpers
|
11
11
|
def strings(lang)
|
12
12
|
@strings ||= {}
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
end
|
13
|
+
|
14
|
+
# Allow sidekiq-web extensions to add locale paths
|
15
|
+
# so extensions can be localized
|
16
|
+
@strings[lang] ||= settings.locales.each_with_object({}) do |path, global|
|
17
|
+
find_locale_files(lang).each do |file|
|
18
|
+
strs = YAML.load(File.open(file))
|
19
|
+
global.merge!(strs[lang])
|
21
20
|
end
|
22
21
|
end
|
23
22
|
end
|
24
23
|
|
24
|
+
def singularize(str, count)
|
25
|
+
if count == 1 && str.respond_to?(:singularize) # rails
|
26
|
+
str.singularize
|
27
|
+
else
|
28
|
+
str
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
25
32
|
def clear_caches
|
26
33
|
@strings = nil
|
27
34
|
@locale_files = nil
|
@@ -63,14 +70,6 @@ module Sidekiq
|
|
63
70
|
@head_html.join if defined?(@head_html)
|
64
71
|
end
|
65
72
|
|
66
|
-
def poll_path
|
67
|
-
if current_path != "" && params["poll"]
|
68
|
-
root_path + current_path
|
69
|
-
else
|
70
|
-
""
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
73
|
def text_direction
|
75
74
|
get_locale["TextDirection"] || "ltr"
|
76
75
|
end
|
@@ -112,6 +111,13 @@ module Sidekiq
|
|
112
111
|
end
|
113
112
|
end
|
114
113
|
|
114
|
+
# within is used by Sidekiq Pro
|
115
|
+
def display_tags(job, within = nil)
|
116
|
+
job.tags.map { |tag|
|
117
|
+
"<span class='label label-info jobtag'>#{::Rack::Utils.escape_html(tag)}</span>"
|
118
|
+
}.join(" ")
|
119
|
+
end
|
120
|
+
|
115
121
|
# mperham/sidekiq#3243
|
116
122
|
def unfiltered?
|
117
123
|
yield unless env["PATH_INFO"].start_with?("/filter/")
|
@@ -130,6 +136,10 @@ module Sidekiq
|
|
130
136
|
end
|
131
137
|
end
|
132
138
|
|
139
|
+
def sort_direction_label
|
140
|
+
params[:direction] == "asc" ? "↑" : "↓"
|
141
|
+
end
|
142
|
+
|
133
143
|
def workers
|
134
144
|
@workers ||= Sidekiq::Workers.new
|
135
145
|
end
|
@@ -142,16 +152,9 @@ module Sidekiq
|
|
142
152
|
@stats ||= Sidekiq::Stats.new
|
143
153
|
end
|
144
154
|
|
145
|
-
def retries_with_score(score)
|
146
|
-
Sidekiq.redis { |conn|
|
147
|
-
conn.zrangebyscore("retry", score, score)
|
148
|
-
}.map { |msg| Sidekiq.load_json(msg) }
|
149
|
-
end
|
150
|
-
|
151
155
|
def redis_connection
|
152
156
|
Sidekiq.redis do |conn|
|
153
|
-
|
154
|
-
"redis://#{c[:location]}/#{c[:db]}"
|
157
|
+
conn.connection[:id]
|
155
158
|
end
|
156
159
|
end
|
157
160
|
|
@@ -189,16 +192,17 @@ module Sidekiq
|
|
189
192
|
[score.to_f, jid]
|
190
193
|
end
|
191
194
|
|
192
|
-
SAFE_QPARAMS = %w[page
|
195
|
+
SAFE_QPARAMS = %w[page direction]
|
193
196
|
|
194
197
|
# Merge options with current params, filter safe params, and stringify to query string
|
195
198
|
def qparams(options)
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
199
|
+
stringified_options = options.transform_keys(&:to_s)
|
200
|
+
|
201
|
+
to_query_string(params.merge(stringified_options))
|
202
|
+
end
|
200
203
|
|
201
|
-
|
204
|
+
def to_query_string(params)
|
205
|
+
params.map { |key, value|
|
202
206
|
SAFE_QPARAMS.include?(key) ? "#{key}=#{CGI.escape(value.to_s)}" : next
|
203
207
|
}.compact.join("&")
|
204
208
|
end
|
@@ -221,7 +225,7 @@ module Sidekiq
|
|
221
225
|
end
|
222
226
|
|
223
227
|
def csrf_tag
|
224
|
-
"<input type='hidden' name='authenticity_token' value='#{
|
228
|
+
"<input type='hidden' name='authenticity_token' value='#{env[:csrf_token]}'/>"
|
225
229
|
end
|
226
230
|
|
227
231
|
def to_display(arg)
|
@@ -238,7 +242,7 @@ module Sidekiq
|
|
238
242
|
queue class args retry_count retried_at failed_at
|
239
243
|
jid error_message error_class backtrace
|
240
244
|
error_backtrace enqueued_at retry wrapped
|
241
|
-
created_at
|
245
|
+
created_at tags
|
242
246
|
])
|
243
247
|
|
244
248
|
def retry_extra_items(retry_job)
|
@@ -249,7 +253,21 @@ module Sidekiq
|
|
249
253
|
end
|
250
254
|
end
|
251
255
|
|
256
|
+
def format_memory(rss_kb)
|
257
|
+
return "0" if rss_kb.nil? || rss_kb == 0
|
258
|
+
|
259
|
+
if rss_kb < 100_000
|
260
|
+
"#{number_with_delimiter(rss_kb)} KB"
|
261
|
+
elsif rss_kb < 10_000_000
|
262
|
+
"#{number_with_delimiter((rss_kb / 1024.0).to_i)} MB"
|
263
|
+
else
|
264
|
+
"#{number_with_delimiter((rss_kb / (1024.0 * 1024.0)).round(1))} GB"
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
252
268
|
def number_with_delimiter(number)
|
269
|
+
return "" if number.nil?
|
270
|
+
|
253
271
|
begin
|
254
272
|
Float(number)
|
255
273
|
rescue ArgumentError, TypeError
|
@@ -283,7 +301,7 @@ module Sidekiq
|
|
283
301
|
end
|
284
302
|
|
285
303
|
def environment_title_prefix
|
286
|
-
environment = Sidekiq.options[:environment] || ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
|
304
|
+
environment = Sidekiq.options[:environment] || ENV["APP_ENV"] || ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
|
287
305
|
|
288
306
|
"[#{environment.upcase}] " unless environment == "production"
|
289
307
|
end
|
data/lib/sidekiq/web/router.rb
CHANGED
@@ -15,6 +15,10 @@ module Sidekiq
|
|
15
15
|
REQUEST_METHOD = "REQUEST_METHOD"
|
16
16
|
PATH_INFO = "PATH_INFO"
|
17
17
|
|
18
|
+
def head(path, &block)
|
19
|
+
route(HEAD, path, &block)
|
20
|
+
end
|
21
|
+
|
18
22
|
def get(path, &block)
|
19
23
|
route(GET, path, &block)
|
20
24
|
end
|
@@ -39,7 +43,6 @@ module Sidekiq
|
|
39
43
|
@routes ||= {GET => [], POST => [], PUT => [], PATCH => [], DELETE => [], HEAD => []}
|
40
44
|
|
41
45
|
@routes[method] << WebRoute.new(method, path, block)
|
42
|
-
@routes[HEAD] << WebRoute.new(method, path, block) if method == GET
|
43
46
|
end
|
44
47
|
|
45
48
|
def match(env)
|
@@ -66,7 +69,7 @@ module Sidekiq
|
|
66
69
|
class WebRoute
|
67
70
|
attr_accessor :request_method, :pattern, :block, :name
|
68
71
|
|
69
|
-
NAMED_SEGMENTS_PATTERN = /\/([^\/]*):([
|
72
|
+
NAMED_SEGMENTS_PATTERN = /\/([^\/]*):([^.:$\/]+)/
|
70
73
|
|
71
74
|
def initialize(request_method, pattern, block)
|
72
75
|
@request_method = request_method
|
@@ -94,9 +97,7 @@ module Sidekiq
|
|
94
97
|
{} if path == matcher
|
95
98
|
else
|
96
99
|
path_match = path.match(matcher)
|
97
|
-
|
98
|
-
Hash[path_match.names.map(&:to_sym).zip(path_match.captures)]
|
99
|
-
end
|
100
|
+
path_match&.named_captures&.transform_keys(&:to_sym)
|
100
101
|
end
|
101
102
|
end
|
102
103
|
end
|
data/lib/sidekiq/web.rb
CHANGED
@@ -10,12 +10,11 @@ require "sidekiq/web/helpers"
|
|
10
10
|
require "sidekiq/web/router"
|
11
11
|
require "sidekiq/web/action"
|
12
12
|
require "sidekiq/web/application"
|
13
|
+
require "sidekiq/web/csrf_protection"
|
13
14
|
|
14
|
-
require "rack/
|
15
|
-
|
15
|
+
require "rack/content_length"
|
16
16
|
require "rack/builder"
|
17
|
-
require "rack/
|
18
|
-
require "rack/session/cookie"
|
17
|
+
require "rack/static"
|
19
18
|
|
20
19
|
module Sidekiq
|
21
20
|
class Web
|
@@ -31,7 +30,7 @@ module Sidekiq
|
|
31
30
|
"Queues" => "queues",
|
32
31
|
"Retries" => "retries",
|
33
32
|
"Scheduled" => "scheduled",
|
34
|
-
"Dead" => "morgue"
|
33
|
+
"Dead" => "morgue"
|
35
34
|
}
|
36
35
|
|
37
36
|
class << self
|
@@ -39,14 +38,6 @@ module Sidekiq
|
|
39
38
|
self
|
40
39
|
end
|
41
40
|
|
42
|
-
def middlewares
|
43
|
-
@middlewares ||= []
|
44
|
-
end
|
45
|
-
|
46
|
-
def use(*middleware_args, &block)
|
47
|
-
middlewares << [middleware_args, block]
|
48
|
-
end
|
49
|
-
|
50
41
|
def default_tabs
|
51
42
|
DEFAULT_TABS
|
52
43
|
end
|
@@ -72,32 +63,45 @@ module Sidekiq
|
|
72
63
|
opts.each { |key| set(key, false) }
|
73
64
|
end
|
74
65
|
|
75
|
-
|
66
|
+
def middlewares
|
67
|
+
@middlewares ||= []
|
68
|
+
end
|
69
|
+
|
70
|
+
def use(*args, &block)
|
71
|
+
middlewares << [args, block]
|
72
|
+
end
|
73
|
+
|
76
74
|
def set(attribute, value)
|
77
75
|
send(:"#{attribute}=", value)
|
78
76
|
end
|
79
77
|
|
80
|
-
|
78
|
+
def sessions=(val)
|
79
|
+
puts "WARNING: Sidekiq::Web.sessions= is no longer relevant and will be removed in Sidekiq 7.0. #{caller(1..1).first}"
|
80
|
+
end
|
81
|
+
|
82
|
+
def session_secret=(val)
|
83
|
+
puts "WARNING: Sidekiq::Web.session_secret= is no longer relevant and will be removed in Sidekiq 7.0. #{caller(1..1).first}"
|
84
|
+
end
|
85
|
+
|
86
|
+
attr_accessor :app_url, :redis_pool
|
81
87
|
attr_writer :locales, :views
|
82
88
|
end
|
83
89
|
|
84
90
|
def self.inherited(child)
|
85
91
|
child.app_url = app_url
|
86
|
-
child.session_secret = session_secret
|
87
92
|
child.redis_pool = redis_pool
|
88
|
-
child.sessions = sessions
|
89
93
|
end
|
90
94
|
|
91
95
|
def settings
|
92
96
|
self.class.settings
|
93
97
|
end
|
94
98
|
|
95
|
-
def
|
96
|
-
middlewares
|
99
|
+
def middlewares
|
100
|
+
@middlewares ||= self.class.middlewares
|
97
101
|
end
|
98
102
|
|
99
|
-
def
|
100
|
-
|
103
|
+
def use(*args, &block)
|
104
|
+
middlewares << [args, block]
|
101
105
|
end
|
102
106
|
|
103
107
|
def call(env)
|
@@ -125,18 +129,8 @@ module Sidekiq
|
|
125
129
|
send(:"#{attribute}=", value)
|
126
130
|
end
|
127
131
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
attr_writer :sessions
|
132
|
-
|
133
|
-
def sessions
|
134
|
-
unless instance_variable_defined?("@sessions")
|
135
|
-
@sessions = self.class.sessions
|
136
|
-
@sessions = @sessions.to_hash.dup if @sessions.respond_to?(:to_hash)
|
137
|
-
end
|
138
|
-
|
139
|
-
@sessions
|
132
|
+
def sessions=(val)
|
133
|
+
puts "Sidekiq::Web#sessions= is no longer relevant and will be removed in Sidekiq 7.0. #{caller[2..2].first}"
|
140
134
|
end
|
141
135
|
|
142
136
|
def self.register(extension)
|
@@ -145,50 +139,20 @@ module Sidekiq
|
|
145
139
|
|
146
140
|
private
|
147
141
|
|
148
|
-
def using?(middleware)
|
149
|
-
middlewares.any? do |(m, _)|
|
150
|
-
m.is_a?(Array) && (m[0] == middleware || m[0].is_a?(middleware))
|
151
|
-
end
|
152
|
-
end
|
153
|
-
|
154
|
-
def build_sessions
|
155
|
-
middlewares = self.middlewares
|
156
|
-
|
157
|
-
unless using?(::Rack::Protection) || ENV["RACK_ENV"] == "test"
|
158
|
-
middlewares.unshift [[::Rack::Protection, {use: :authenticity_token}], nil]
|
159
|
-
end
|
160
|
-
|
161
|
-
s = sessions
|
162
|
-
return unless s
|
163
|
-
|
164
|
-
unless using? ::Rack::Session::Cookie
|
165
|
-
unless (secret = Web.session_secret)
|
166
|
-
require "securerandom"
|
167
|
-
secret = SecureRandom.hex(64)
|
168
|
-
end
|
169
|
-
|
170
|
-
options = {secret: secret}
|
171
|
-
options = options.merge(s.to_hash) if s.respond_to? :to_hash
|
172
|
-
|
173
|
-
middlewares.unshift [[::Rack::Session::Cookie, options], nil]
|
174
|
-
end
|
175
|
-
end
|
176
|
-
|
177
142
|
def build
|
178
|
-
build_sessions
|
179
|
-
|
180
|
-
middlewares = self.middlewares
|
181
143
|
klass = self.class
|
144
|
+
m = middlewares
|
182
145
|
|
183
|
-
|
184
|
-
|
185
|
-
map "/#{asset_dir}" do
|
186
|
-
run ::Rack::File.new("#{ASSETS}/#{asset_dir}", {"Cache-Control" => "public, max-age=86400"})
|
187
|
-
end
|
188
|
-
end
|
189
|
-
|
190
|
-
middlewares.each { |middleware, block| use(*middleware, &block) }
|
146
|
+
rules = []
|
147
|
+
rules = [[:all, {"Cache-Control" => "public, max-age=86400"}]] unless ENV["SIDEKIQ_WEB_TESTING"]
|
191
148
|
|
149
|
+
::Rack::Builder.new do
|
150
|
+
use Rack::Static, urls: ["/stylesheets", "/images", "/javascripts"],
|
151
|
+
root: ASSETS,
|
152
|
+
cascade: true,
|
153
|
+
header_rules: rules
|
154
|
+
m.each { |middleware, block| use(*middleware, &block) }
|
155
|
+
use Sidekiq::Web::CsrfProtection unless $TESTING
|
192
156
|
run WebApplication.new(klass)
|
193
157
|
end
|
194
158
|
end
|
data/lib/sidekiq/worker.rb
CHANGED
@@ -9,6 +9,7 @@ module Sidekiq
|
|
9
9
|
#
|
10
10
|
# class HardWorker
|
11
11
|
# include Sidekiq::Worker
|
12
|
+
# sidekiq_options queue: 'critical', retry: 5
|
12
13
|
#
|
13
14
|
# def perform(*args)
|
14
15
|
# # do some work
|
@@ -20,6 +21,26 @@ module Sidekiq
|
|
20
21
|
# HardWorker.perform_async(1, 2, 3)
|
21
22
|
#
|
22
23
|
# Note that perform_async is a class method, perform is an instance method.
|
24
|
+
#
|
25
|
+
# Sidekiq::Worker also includes several APIs to provide compatibility with
|
26
|
+
# ActiveJob.
|
27
|
+
#
|
28
|
+
# class SomeWorker
|
29
|
+
# include Sidekiq::Worker
|
30
|
+
# queue_as :critical
|
31
|
+
#
|
32
|
+
# def perform(...)
|
33
|
+
# end
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# SomeWorker.set(wait_until: 1.hour).perform_async(123)
|
37
|
+
#
|
38
|
+
# Note that arguments passed to the job must still obey Sidekiq's
|
39
|
+
# best practice for simple, JSON-native data types. Sidekiq will not
|
40
|
+
# implement ActiveJob's more complex argument serialization. For
|
41
|
+
# this reason, we don't implement `perform_later` as our call semantics
|
42
|
+
# are very different.
|
43
|
+
#
|
23
44
|
module Worker
|
24
45
|
##
|
25
46
|
# The Options module is extracted so we can include it in ActiveJob::Base
|
@@ -48,8 +69,8 @@ module Sidekiq
|
|
48
69
|
# In practice, any option is allowed. This is the main mechanism to configure the
|
49
70
|
# options for a specific job.
|
50
71
|
def sidekiq_options(opts = {})
|
51
|
-
opts =
|
52
|
-
self.sidekiq_options_hash = get_sidekiq_options.merge(
|
72
|
+
opts = opts.transform_keys(&:to_s) # stringify
|
73
|
+
self.sidekiq_options_hash = get_sidekiq_options.merge(opts)
|
53
74
|
end
|
54
75
|
|
55
76
|
def sidekiq_retry_in(&block)
|
@@ -150,33 +171,95 @@ module Sidekiq
|
|
150
171
|
# SomeWorker.set(queue: 'foo').perform_async(....)
|
151
172
|
#
|
152
173
|
class Setter
|
174
|
+
include Sidekiq::JobUtil
|
175
|
+
|
153
176
|
def initialize(klass, opts)
|
154
177
|
@klass = klass
|
155
178
|
@opts = opts
|
179
|
+
|
180
|
+
# ActiveJob compatibility
|
181
|
+
interval = @opts.delete(:wait_until) || @opts.delete(:wait)
|
182
|
+
at(interval) if interval
|
156
183
|
end
|
157
184
|
|
158
185
|
def set(options)
|
186
|
+
interval = options.delete(:wait_until) || options.delete(:wait)
|
159
187
|
@opts.merge!(options)
|
188
|
+
at(interval) if interval
|
160
189
|
self
|
161
190
|
end
|
162
191
|
|
163
192
|
def perform_async(*args)
|
164
|
-
@
|
193
|
+
if @opts["sync"] == true
|
194
|
+
perform_inline(*args)
|
195
|
+
else
|
196
|
+
@klass.client_push(@opts.merge("args" => args, "class" => @klass))
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
# Explicit inline execution of a job. Returns nil if the job did not
|
201
|
+
# execute, true otherwise.
|
202
|
+
def perform_inline(*args)
|
203
|
+
raw = @opts.merge("args" => args, "class" => @klass).transform_keys(&:to_s)
|
204
|
+
|
205
|
+
# validate and normalize payload
|
206
|
+
item = normalize_item(raw)
|
207
|
+
queue = item["queue"]
|
208
|
+
|
209
|
+
# run client-side middleware
|
210
|
+
result = Sidekiq.client_middleware.invoke(item["class"], item, queue, Sidekiq.redis_pool) do
|
211
|
+
item
|
212
|
+
end
|
213
|
+
return nil unless result
|
214
|
+
|
215
|
+
# round-trip the payload via JSON
|
216
|
+
msg = Sidekiq.load_json(Sidekiq.dump_json(item))
|
217
|
+
|
218
|
+
# prepare the job instance
|
219
|
+
klass = msg["class"].constantize
|
220
|
+
job = klass.new
|
221
|
+
job.jid = msg["jid"]
|
222
|
+
job.bid = msg["bid"] if job.respond_to?(:bid)
|
223
|
+
|
224
|
+
# run the job through server-side middleware
|
225
|
+
result = Sidekiq.server_middleware.invoke(job, msg, msg["queue"]) do
|
226
|
+
# perform it
|
227
|
+
job.perform(*msg["args"])
|
228
|
+
true
|
229
|
+
end
|
230
|
+
return nil unless result
|
231
|
+
# jobs do not return a result. they should store any
|
232
|
+
# modified state.
|
233
|
+
true
|
234
|
+
end
|
235
|
+
alias_method :perform_sync, :perform_inline
|
236
|
+
|
237
|
+
def perform_bulk(args, batch_size: 1_000)
|
238
|
+
hash = @opts.transform_keys(&:to_s)
|
239
|
+
result = args.each_slice(batch_size).flat_map do |slice|
|
240
|
+
Sidekiq::Client.push_bulk(hash.merge("class" => @klass, "args" => slice))
|
241
|
+
end
|
242
|
+
|
243
|
+
result.is_a?(Enumerator::Lazy) ? result.force : result
|
165
244
|
end
|
166
245
|
|
167
246
|
# +interval+ must be a timestamp, numeric or something that acts
|
168
247
|
# numeric (like an activesupport time interval).
|
169
248
|
def perform_in(interval, *args)
|
249
|
+
at(interval).perform_async(*args)
|
250
|
+
end
|
251
|
+
alias_method :perform_at, :perform_in
|
252
|
+
|
253
|
+
private
|
254
|
+
|
255
|
+
def at(interval)
|
170
256
|
int = interval.to_f
|
171
257
|
now = Time.now.to_f
|
172
258
|
ts = (int < 1_000_000_000 ? now + int : int)
|
173
|
-
|
174
|
-
payload = @opts.merge("class" => @klass, "args" => args, "at" => ts)
|
175
259
|
# Optimization to enqueue something now that is scheduled to go out now or in the past
|
176
|
-
|
177
|
-
|
260
|
+
@opts["at"] = ts if ts > now
|
261
|
+
self
|
178
262
|
end
|
179
|
-
alias_method :perform_at, :perform_in
|
180
263
|
end
|
181
264
|
|
182
265
|
module ClassMethods
|
@@ -192,12 +275,49 @@ module Sidekiq
|
|
192
275
|
raise ArgumentError, "Do not call .delay_until on a Sidekiq::Worker class, call .perform_at"
|
193
276
|
end
|
194
277
|
|
278
|
+
def queue_as(q)
|
279
|
+
sidekiq_options("queue" => q.to_s)
|
280
|
+
end
|
281
|
+
|
195
282
|
def set(options)
|
196
283
|
Setter.new(self, options)
|
197
284
|
end
|
198
285
|
|
199
286
|
def perform_async(*args)
|
200
|
-
|
287
|
+
Setter.new(self, {}).perform_async(*args)
|
288
|
+
end
|
289
|
+
|
290
|
+
# Inline execution of job's perform method after passing through Sidekiq.client_middleware and Sidekiq.server_middleware
|
291
|
+
def perform_inline(*args)
|
292
|
+
Setter.new(self, {}).perform_inline(*args)
|
293
|
+
end
|
294
|
+
|
295
|
+
##
|
296
|
+
# Push a large number of jobs to Redis, while limiting the batch of
|
297
|
+
# each job payload to 1,000. This method helps cut down on the number
|
298
|
+
# of round trips to Redis, which can increase the performance of enqueueing
|
299
|
+
# large numbers of jobs.
|
300
|
+
#
|
301
|
+
# +items+ must be an Array of Arrays.
|
302
|
+
#
|
303
|
+
# For finer-grained control, use `Sidekiq::Client.push_bulk` directly.
|
304
|
+
#
|
305
|
+
# Example (3 Redis round trips):
|
306
|
+
#
|
307
|
+
# SomeWorker.perform_async(1)
|
308
|
+
# SomeWorker.perform_async(2)
|
309
|
+
# SomeWorker.perform_async(3)
|
310
|
+
#
|
311
|
+
# Would instead become (1 Redis round trip):
|
312
|
+
#
|
313
|
+
# SomeWorker.perform_bulk([[1], [2], [3]])
|
314
|
+
#
|
315
|
+
def perform_bulk(items, batch_size: 1_000)
|
316
|
+
result = items.each_slice(batch_size).flat_map do |slice|
|
317
|
+
Sidekiq::Client.push_bulk("class" => self, "args" => slice)
|
318
|
+
end
|
319
|
+
|
320
|
+
result.is_a?(Enumerator::Lazy) ? result.force : result
|
201
321
|
end
|
202
322
|
|
203
323
|
# +interval+ must be a timestamp, numeric or something that acts
|
@@ -207,10 +327,10 @@ module Sidekiq
|
|
207
327
|
now = Time.now.to_f
|
208
328
|
ts = (int < 1_000_000_000 ? now + int : int)
|
209
329
|
|
210
|
-
item = {"class" => self, "args" => args
|
330
|
+
item = {"class" => self, "args" => args}
|
211
331
|
|
212
332
|
# Optimization to enqueue something now that is scheduled to go out now or in the past
|
213
|
-
item
|
333
|
+
item["at"] = ts if ts > now
|
214
334
|
|
215
335
|
client_push(item)
|
216
336
|
end
|
@@ -235,12 +355,9 @@ module Sidekiq
|
|
235
355
|
|
236
356
|
def client_push(item) # :nodoc:
|
237
357
|
pool = Thread.current[:sidekiq_via_pool] || get_sidekiq_options["pool"] || Sidekiq.redis_pool
|
238
|
-
|
239
|
-
item.keys.each do |key|
|
240
|
-
item[key.to_s] = item.delete(key)
|
241
|
-
end
|
358
|
+
stringified_item = item.transform_keys(&:to_s)
|
242
359
|
|
243
|
-
Sidekiq::Client.new(pool).push(
|
360
|
+
Sidekiq::Client.new(pool).push(stringified_item)
|
244
361
|
end
|
245
362
|
end
|
246
363
|
end
|