sidekiq 4.2.7 → 5.2.8
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 +5 -5
- data/.circleci/config.yml +61 -0
- data/.github/issue_template.md +8 -1
- data/.gitignore +3 -0
- data/.travis.yml +5 -6
- data/5.0-Upgrade.md +56 -0
- data/COMM-LICENSE +12 -10
- data/Changes.md +187 -0
- data/Ent-Changes.md +84 -3
- data/Gemfile +14 -20
- data/LICENSE +1 -1
- data/Pro-4.0-Upgrade.md +35 -0
- data/Pro-Changes.md +172 -3
- data/README.md +8 -6
- data/Rakefile +2 -5
- data/bin/sidekiqctl +13 -92
- data/bin/sidekiqload +16 -21
- data/lib/generators/sidekiq/templates/worker_spec.rb.erb +1 -1
- data/lib/generators/sidekiq/templates/worker_test.rb.erb +1 -1
- data/lib/sidekiq/api.rb +163 -67
- data/lib/sidekiq/cli.rb +121 -78
- data/lib/sidekiq/client.rb +25 -18
- data/lib/sidekiq/core_ext.rb +1 -119
- data/lib/sidekiq/ctl.rb +221 -0
- data/lib/sidekiq/delay.rb +42 -0
- data/lib/sidekiq/exception_handler.rb +2 -4
- data/lib/sidekiq/extensions/generic_proxy.rb +7 -1
- data/lib/sidekiq/fetch.rb +1 -1
- data/lib/sidekiq/job_logger.rb +25 -0
- data/lib/sidekiq/job_retry.rb +262 -0
- data/lib/sidekiq/launcher.rb +19 -19
- data/lib/sidekiq/logging.rb +18 -2
- data/lib/sidekiq/manager.rb +6 -7
- data/lib/sidekiq/middleware/server/active_record.rb +10 -0
- data/lib/sidekiq/processor.rb +126 -39
- data/lib/sidekiq/rails.rb +16 -77
- data/lib/sidekiq/redis_connection.rb +50 -5
- data/lib/sidekiq/scheduled.rb +35 -8
- data/lib/sidekiq/testing.rb +21 -6
- data/lib/sidekiq/util.rb +6 -2
- data/lib/sidekiq/version.rb +1 -1
- data/lib/sidekiq/web/action.rb +2 -6
- data/lib/sidekiq/web/application.rb +34 -17
- data/lib/sidekiq/web/helpers.rb +72 -23
- data/lib/sidekiq/web/router.rb +10 -10
- data/lib/sidekiq/web.rb +4 -4
- data/lib/sidekiq/worker.rb +118 -19
- data/lib/sidekiq.rb +27 -27
- data/sidekiq.gemspec +8 -13
- data/web/assets/javascripts/application.js +0 -0
- data/web/assets/javascripts/dashboard.js +33 -18
- data/web/assets/stylesheets/application-rtl.css +246 -0
- data/web/assets/stylesheets/application.css +371 -6
- data/web/assets/stylesheets/bootstrap-rtl.min.css +9 -0
- data/web/assets/stylesheets/bootstrap.css +2 -2
- data/web/locales/ar.yml +81 -0
- data/web/locales/en.yml +2 -0
- data/web/locales/es.yml +4 -3
- data/web/locales/fa.yml +80 -0
- data/web/locales/he.yml +79 -0
- data/web/locales/ja.yml +5 -3
- data/web/locales/ur.yml +80 -0
- data/web/views/_footer.erb +5 -2
- data/web/views/_job_info.erb +1 -1
- data/web/views/_nav.erb +4 -18
- data/web/views/_paging.erb +1 -1
- data/web/views/busy.erb +9 -5
- data/web/views/dashboard.erb +3 -3
- data/web/views/layout.erb +11 -2
- data/web/views/morgue.erb +6 -4
- data/web/views/queue.erb +11 -10
- data/web/views/queues.erb +4 -2
- data/web/views/retries.erb +9 -5
- data/web/views/retry.erb +1 -1
- data/web/views/scheduled.erb +2 -2
- metadata +33 -151
- data/lib/sidekiq/middleware/server/logging.rb +0 -40
- data/lib/sidekiq/middleware/server/retry_jobs.rb +0 -205
- data/test/config.yml +0 -9
- data/test/env_based_config.yml +0 -11
- data/test/fake_env.rb +0 -1
- data/test/fixtures/en.yml +0 -2
- data/test/helper.rb +0 -75
- data/test/test_actors.rb +0 -138
- data/test/test_api.rb +0 -528
- data/test/test_cli.rb +0 -418
- data/test/test_client.rb +0 -266
- data/test/test_exception_handler.rb +0 -56
- data/test/test_extensions.rb +0 -129
- data/test/test_fetch.rb +0 -50
- data/test/test_launcher.rb +0 -92
- data/test/test_logging.rb +0 -35
- data/test/test_manager.rb +0 -50
- data/test/test_middleware.rb +0 -158
- data/test/test_processor.rb +0 -249
- data/test/test_rails.rb +0 -22
- data/test/test_redis_connection.rb +0 -132
- data/test/test_retry.rb +0 -326
- data/test/test_retry_exhausted.rb +0 -149
- data/test/test_scheduled.rb +0 -115
- data/test/test_scheduling.rb +0 -58
- data/test/test_sidekiq.rb +0 -107
- data/test/test_testing.rb +0 -143
- data/test/test_testing_fake.rb +0 -359
- data/test/test_testing_inline.rb +0 -94
- data/test/test_util.rb +0 -13
- data/test/test_web.rb +0 -679
- data/test/test_web_helpers.rb +0 -54
data/lib/sidekiq/testing.rb
CHANGED
@@ -55,6 +55,15 @@ module Sidekiq
|
|
55
55
|
yield @server_chain if block_given?
|
56
56
|
@server_chain
|
57
57
|
end
|
58
|
+
|
59
|
+
def constantize(str)
|
60
|
+
names = str.split('::')
|
61
|
+
names.shift if names.empty? || names.first.empty?
|
62
|
+
|
63
|
+
names.inject(Object) do |constant, name|
|
64
|
+
constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
|
65
|
+
end
|
66
|
+
end
|
58
67
|
end
|
59
68
|
end
|
60
69
|
|
@@ -63,9 +72,7 @@ module Sidekiq
|
|
63
72
|
|
64
73
|
class EmptyQueueError < RuntimeError; end
|
65
74
|
|
66
|
-
|
67
|
-
alias_method :raw_push_real, :raw_push
|
68
|
-
|
75
|
+
module TestingClient
|
69
76
|
def raw_push(payloads)
|
70
77
|
if Sidekiq::Testing.fake?
|
71
78
|
payloads.each do |job|
|
@@ -76,18 +83,20 @@ module Sidekiq
|
|
76
83
|
true
|
77
84
|
elsif Sidekiq::Testing.inline?
|
78
85
|
payloads.each do |job|
|
79
|
-
klass = job['class']
|
86
|
+
klass = Sidekiq::Testing.constantize(job['class'])
|
80
87
|
job['id'] ||= SecureRandom.hex(12)
|
81
88
|
job_hash = Sidekiq.load_json(Sidekiq.dump_json(job))
|
82
89
|
klass.process_job(job_hash)
|
83
90
|
end
|
84
91
|
true
|
85
92
|
else
|
86
|
-
|
93
|
+
super
|
87
94
|
end
|
88
95
|
end
|
89
96
|
end
|
90
97
|
|
98
|
+
Sidekiq::Client.prepend TestingClient
|
99
|
+
|
91
100
|
module Queues
|
92
101
|
##
|
93
102
|
# The Queues class is only for testing the fake queue implementation.
|
@@ -309,10 +318,16 @@ module Sidekiq
|
|
309
318
|
worker_classes = jobs.map { |job| job["class"] }.uniq
|
310
319
|
|
311
320
|
worker_classes.each do |worker_class|
|
312
|
-
|
321
|
+
Sidekiq::Testing.constantize(worker_class).drain
|
313
322
|
end
|
314
323
|
end
|
315
324
|
end
|
316
325
|
end
|
317
326
|
end
|
318
327
|
end
|
328
|
+
|
329
|
+
if defined?(::Rails) && Rails.respond_to?(:env) && !Rails.env.test?
|
330
|
+
puts("**************************************************")
|
331
|
+
puts("⛔️ WARNING: Sidekiq testing API enabled, but this is not the test environment. Your jobs will not go to Redis.")
|
332
|
+
puts("**************************************************")
|
333
|
+
end
|
data/lib/sidekiq/util.rb
CHANGED
@@ -2,7 +2,6 @@
|
|
2
2
|
require 'socket'
|
3
3
|
require 'securerandom'
|
4
4
|
require 'sidekiq/exception_handler'
|
5
|
-
require 'sidekiq/core_ext'
|
6
5
|
|
7
6
|
module Sidekiq
|
8
7
|
##
|
@@ -22,6 +21,7 @@ module Sidekiq
|
|
22
21
|
|
23
22
|
def safe_thread(name, &block)
|
24
23
|
Thread.new do
|
24
|
+
Thread.current['sidekiq_label'] = name
|
25
25
|
watchdog(name, &block)
|
26
26
|
end
|
27
27
|
end
|
@@ -46,7 +46,10 @@ module Sidekiq
|
|
46
46
|
@@identity ||= "#{hostname}:#{$$}:#{process_nonce}"
|
47
47
|
end
|
48
48
|
|
49
|
-
def fire_event(event,
|
49
|
+
def fire_event(event, options={})
|
50
|
+
reverse = options[:reverse]
|
51
|
+
reraise = options[:reraise]
|
52
|
+
|
50
53
|
arr = Sidekiq.options[:lifecycle_events][event]
|
51
54
|
arr.reverse! if reverse
|
52
55
|
arr.each do |block|
|
@@ -54,6 +57,7 @@ module Sidekiq
|
|
54
57
|
block.call
|
55
58
|
rescue => ex
|
56
59
|
handle_exception(ex, { context: "Exception during Sidekiq lifecycle event.", event: event })
|
60
|
+
raise ex if reraise
|
57
61
|
end
|
58
62
|
end
|
59
63
|
arr.clear
|
data/lib/sidekiq/version.rb
CHANGED
data/lib/sidekiq/web/action.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module Sidekiq
|
4
4
|
class WebAction
|
5
|
-
RACK_SESSION = 'rack.session'
|
5
|
+
RACK_SESSION = 'rack.session'
|
6
6
|
|
7
7
|
attr_accessor :env, :block, :type
|
8
8
|
|
@@ -39,10 +39,6 @@ module Sidekiq
|
|
39
39
|
env[RACK_SESSION]
|
40
40
|
end
|
41
41
|
|
42
|
-
def content_type(type)
|
43
|
-
@type = type
|
44
|
-
end
|
45
|
-
|
46
42
|
def erb(content, options = {})
|
47
43
|
if content.kind_of? Symbol
|
48
44
|
unless respond_to?(:"_erb_#{content}")
|
@@ -81,7 +77,7 @@ module Sidekiq
|
|
81
77
|
private
|
82
78
|
|
83
79
|
def _erb(file, locals)
|
84
|
-
locals.each {|k, v| define_singleton_method(k){ v } } if locals
|
80
|
+
locals.each {|k, v| define_singleton_method(k){ v } unless (singleton_methods.include? k)} if locals
|
85
81
|
|
86
82
|
if file.kind_of?(String)
|
87
83
|
ERB.new(file).result(binding)
|
@@ -4,9 +4,24 @@ module Sidekiq
|
|
4
4
|
class WebApplication
|
5
5
|
extend WebRouter
|
6
6
|
|
7
|
-
CONTENT_LENGTH = "Content-Length"
|
8
|
-
CONTENT_TYPE = "Content-Type"
|
7
|
+
CONTENT_LENGTH = "Content-Length"
|
8
|
+
CONTENT_TYPE = "Content-Type"
|
9
9
|
REDIS_KEYS = %w(redis_version uptime_in_days connected_clients used_memory_human used_memory_peak_human)
|
10
|
+
CSP_HEADER = [
|
11
|
+
"default-src 'self' https: http:",
|
12
|
+
"child-src 'self'",
|
13
|
+
"connect-src 'self' https: http: wss: ws:",
|
14
|
+
"font-src 'self' https: http:",
|
15
|
+
"frame-src 'self'",
|
16
|
+
"img-src 'self' https: http: data:",
|
17
|
+
"manifest-src 'self'",
|
18
|
+
"media-src 'self'",
|
19
|
+
"object-src 'none'",
|
20
|
+
"script-src 'self' https: http: 'unsafe-inline'",
|
21
|
+
"style-src 'self' https: http: 'unsafe-inline'",
|
22
|
+
"worker-src 'self'",
|
23
|
+
"base-uri 'self'"
|
24
|
+
].join('; ').freeze
|
10
25
|
|
11
26
|
def initialize(klass)
|
12
27
|
@klass = klass
|
@@ -85,7 +100,7 @@ module Sidekiq
|
|
85
100
|
name = route_params[:name]
|
86
101
|
Sidekiq::Job.new(params['key_val'], name).delete
|
87
102
|
|
88
|
-
redirect_with_query("#{root_path}queues/#{name}")
|
103
|
+
redirect_with_query("#{root_path}queues/#{CGI.escape(name)}")
|
89
104
|
end
|
90
105
|
|
91
106
|
get '/morgue' do
|
@@ -181,6 +196,12 @@ module Sidekiq
|
|
181
196
|
redirect "#{root_path}retries"
|
182
197
|
end
|
183
198
|
|
199
|
+
post "/retries/all/kill" do
|
200
|
+
Sidekiq::RetrySet.new.kill_all
|
201
|
+
|
202
|
+
redirect "#{root_path}retries"
|
203
|
+
end
|
204
|
+
|
184
205
|
post "/retries/:key" do
|
185
206
|
job = Sidekiq::RetrySet.new.fetch(*parse_params(route_params[:key])).first
|
186
207
|
|
@@ -234,7 +255,6 @@ module Sidekiq
|
|
234
255
|
get '/stats' do
|
235
256
|
sidekiq_stats = Sidekiq::Stats.new
|
236
257
|
redis_stats = redis_info.select { |k, v| REDIS_KEYS.include? k }
|
237
|
-
|
238
258
|
json(
|
239
259
|
sidekiq: {
|
240
260
|
processed: sidekiq_stats.processed,
|
@@ -247,7 +267,8 @@ module Sidekiq
|
|
247
267
|
dead: sidekiq_stats.dead_size,
|
248
268
|
default_latency: sidekiq_stats.default_queue_latency
|
249
269
|
},
|
250
|
-
redis: redis_stats
|
270
|
+
redis: redis_stats,
|
271
|
+
server_utc_time: server_utc_time
|
251
272
|
)
|
252
273
|
end
|
253
274
|
|
@@ -274,19 +295,15 @@ module Sidekiq
|
|
274
295
|
resp = case resp
|
275
296
|
when Array
|
276
297
|
resp
|
277
|
-
when Fixnum
|
278
|
-
[resp, {}, []]
|
279
298
|
else
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
[200, type_header, [resp]]
|
299
|
+
headers = {
|
300
|
+
"Content-Type" => "text/html",
|
301
|
+
"Cache-Control" => "no-cache",
|
302
|
+
"Content-Language" => action.locale,
|
303
|
+
"Content-Security-Policy" => CSP_HEADER
|
304
|
+
}
|
305
|
+
|
306
|
+
[200, headers, [resp]]
|
290
307
|
end
|
291
308
|
|
292
309
|
resp[1] = resp[1].dup
|
data/lib/sidekiq/web/helpers.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
require 'uri'
|
3
3
|
require 'set'
|
4
4
|
require 'yaml'
|
5
|
+
require 'cgi'
|
5
6
|
|
6
7
|
module Sidekiq
|
7
8
|
# This is not a public API
|
@@ -14,7 +15,7 @@ module Sidekiq
|
|
14
15
|
settings.locales.each_with_object({}) do |path, global|
|
15
16
|
find_locale_files(lang).each do |file|
|
16
17
|
strs = YAML.load(File.open(file))
|
17
|
-
global.
|
18
|
+
global.merge!(strs[lang])
|
18
19
|
end
|
19
20
|
end
|
20
21
|
end
|
@@ -23,6 +24,7 @@ module Sidekiq
|
|
23
24
|
def clear_caches
|
24
25
|
@@strings = nil
|
25
26
|
@@locale_files = nil
|
27
|
+
@@available_locales = nil
|
26
28
|
end
|
27
29
|
|
28
30
|
def locale_files
|
@@ -31,6 +33,10 @@ module Sidekiq
|
|
31
33
|
end
|
32
34
|
end
|
33
35
|
|
36
|
+
def available_locales
|
37
|
+
@@available_locales ||= locale_files.map { |path| File.basename(path, '.yml') }.uniq
|
38
|
+
end
|
39
|
+
|
34
40
|
def find_locale_files(lang)
|
35
41
|
locale_files.select { |file| file =~ /\/#{lang}\.yml$/ }
|
36
42
|
end
|
@@ -64,20 +70,44 @@ module Sidekiq
|
|
64
70
|
end
|
65
71
|
end
|
66
72
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
73
|
+
def text_direction
|
74
|
+
get_locale['TextDirection'] || 'ltr'
|
75
|
+
end
|
76
|
+
|
77
|
+
def rtl?
|
78
|
+
text_direction == 'rtl'
|
79
|
+
end
|
80
|
+
|
81
|
+
# See https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4
|
82
|
+
def user_preferred_languages
|
83
|
+
languages = env['HTTP_ACCEPT_LANGUAGE']
|
84
|
+
languages.to_s.downcase.gsub(/\s+/, '').split(',').map do |language|
|
85
|
+
locale, quality = language.split(';q=', 2)
|
86
|
+
locale = nil if locale == '*' # Ignore wildcards
|
87
|
+
quality = quality ? quality.to_f : 1.0
|
88
|
+
[locale, quality]
|
89
|
+
end.sort do |(_, left), (_, right)|
|
90
|
+
right <=> left
|
91
|
+
end.map(&:first).compact
|
92
|
+
end
|
93
|
+
|
94
|
+
# Given an Accept-Language header like "fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4,ru;q=0.2"
|
95
|
+
# this method will try to best match the available locales to the user's preferred languages.
|
96
|
+
#
|
97
|
+
# Inspiration taken from https://github.com/iain/http_accept_language/blob/master/lib/http_accept_language/parser.rb
|
71
98
|
def locale
|
72
99
|
@locale ||= begin
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
100
|
+
matched_locale = user_preferred_languages.map do |preferred|
|
101
|
+
preferred_language = preferred.split('-', 2).first
|
102
|
+
|
103
|
+
lang_group = available_locales.select do |available|
|
104
|
+
preferred_language == available.split('-', 2).first
|
105
|
+
end
|
106
|
+
|
107
|
+
lang_group.find { |lang| lang == preferred } || lang_group.min_by(&:length)
|
108
|
+
end.compact.first
|
109
|
+
|
110
|
+
matched_locale || 'en'
|
81
111
|
end
|
82
112
|
end
|
83
113
|
|
@@ -91,7 +121,7 @@ module Sidekiq
|
|
91
121
|
end
|
92
122
|
|
93
123
|
def t(msg, options={})
|
94
|
-
string = get_locale[msg] || msg
|
124
|
+
string = get_locale[msg] || strings('en')[msg] || msg
|
95
125
|
if options.empty?
|
96
126
|
string
|
97
127
|
else
|
@@ -118,11 +148,14 @@ module Sidekiq
|
|
118
148
|
end
|
119
149
|
|
120
150
|
def redis_connection
|
121
|
-
Sidekiq.redis
|
151
|
+
Sidekiq.redis do |conn|
|
152
|
+
c = conn.connection
|
153
|
+
"redis://#{c[:location]}/#{c[:db]}"
|
154
|
+
end
|
122
155
|
end
|
123
156
|
|
124
157
|
def namespace
|
125
|
-
|
158
|
+
@ns ||= Sidekiq.redis { |conn| conn.respond_to?(:namespace) ? conn.namespace : nil }
|
126
159
|
end
|
127
160
|
|
128
161
|
def redis_info
|
@@ -142,7 +175,8 @@ module Sidekiq
|
|
142
175
|
end
|
143
176
|
|
144
177
|
def relative_time(time)
|
145
|
-
|
178
|
+
stamp = time.getutc.iso8601
|
179
|
+
%{<time class="ltr" dir="ltr" title="#{stamp}" datetime="#{stamp}">#{time}</time>}
|
146
180
|
end
|
147
181
|
|
148
182
|
def job_params(job, score)
|
@@ -150,7 +184,7 @@ module Sidekiq
|
|
150
184
|
end
|
151
185
|
|
152
186
|
def parse_params(params)
|
153
|
-
score, jid = params.split("-")
|
187
|
+
score, jid = params.split("-", 2)
|
154
188
|
[score.to_f, jid]
|
155
189
|
end
|
156
190
|
|
@@ -158,9 +192,13 @@ module Sidekiq
|
|
158
192
|
|
159
193
|
# Merge options with current params, filter safe params, and stringify to query string
|
160
194
|
def qparams(options)
|
161
|
-
|
195
|
+
# stringify
|
196
|
+
options.keys.each do |key|
|
197
|
+
options[key.to_s] = options.delete(key)
|
198
|
+
end
|
199
|
+
|
162
200
|
params.merge(options).map do |key, value|
|
163
|
-
SAFE_QPARAMS.include?(key) ? "#{key}=#{value}" : next
|
201
|
+
SAFE_QPARAMS.include?(key) ? "#{key}=#{CGI.escape(value.to_s)}" : next
|
164
202
|
end.compact.join("&")
|
165
203
|
end
|
166
204
|
|
@@ -169,9 +207,16 @@ module Sidekiq
|
|
169
207
|
end
|
170
208
|
|
171
209
|
def display_args(args, truncate_after_chars = 2000)
|
172
|
-
args
|
173
|
-
|
174
|
-
|
210
|
+
return "Invalid job payload, args is nil" if args == nil
|
211
|
+
return "Invalid job payload, args must be an Array, not #{args.class.name}" if !args.is_a?(Array)
|
212
|
+
|
213
|
+
begin
|
214
|
+
args.map do |arg|
|
215
|
+
h(truncate(to_display(arg), truncate_after_chars))
|
216
|
+
end.join(", ")
|
217
|
+
rescue
|
218
|
+
"Illegal job arguments: #{h args.inspect}"
|
219
|
+
end
|
175
220
|
end
|
176
221
|
|
177
222
|
def csrf_tag
|
@@ -248,6 +293,10 @@ module Sidekiq
|
|
248
293
|
"Sidekiq v#{Sidekiq::VERSION}"
|
249
294
|
end
|
250
295
|
|
296
|
+
def server_utc_time
|
297
|
+
Time.now.utc.strftime('%H:%M:%S UTC')
|
298
|
+
end
|
299
|
+
|
251
300
|
def redis_connection_and_namespace
|
252
301
|
@redis_connection_and_namespace ||= begin
|
253
302
|
namespace_suffix = namespace == nil ? '' : "##{namespace}"
|
data/lib/sidekiq/web/router.rb
CHANGED
@@ -3,16 +3,16 @@ require 'rack'
|
|
3
3
|
|
4
4
|
module Sidekiq
|
5
5
|
module WebRouter
|
6
|
-
GET = 'GET'
|
7
|
-
DELETE = 'DELETE'
|
8
|
-
POST = 'POST'
|
9
|
-
PUT = 'PUT'
|
10
|
-
PATCH = 'PATCH'
|
11
|
-
HEAD = 'HEAD'
|
6
|
+
GET = 'GET'
|
7
|
+
DELETE = 'DELETE'
|
8
|
+
POST = 'POST'
|
9
|
+
PUT = 'PUT'
|
10
|
+
PATCH = 'PATCH'
|
11
|
+
HEAD = 'HEAD'
|
12
12
|
|
13
|
-
ROUTE_PARAMS = 'rack.route_params'
|
14
|
-
REQUEST_METHOD = 'REQUEST_METHOD'
|
15
|
-
PATH_INFO = 'PATH_INFO'
|
13
|
+
ROUTE_PARAMS = 'rack.route_params'
|
14
|
+
REQUEST_METHOD = 'REQUEST_METHOD'
|
15
|
+
PATH_INFO = 'PATH_INFO'
|
16
16
|
|
17
17
|
def get(path, &block)
|
18
18
|
route(GET, path, &block)
|
@@ -64,7 +64,7 @@ module Sidekiq
|
|
64
64
|
class WebRoute
|
65
65
|
attr_accessor :request_method, :pattern, :block, :name
|
66
66
|
|
67
|
-
NAMED_SEGMENTS_PATTERN = /\/([^\/]*):([^\.:$\/]+)
|
67
|
+
NAMED_SEGMENTS_PATTERN = /\/([^\/]*):([^\.:$\/]+)/
|
68
68
|
|
69
69
|
def initialize(request_method, pattern, block)
|
70
70
|
@request_method = request_method
|
data/lib/sidekiq/web.rb
CHANGED
@@ -19,10 +19,10 @@ require 'rack/session/cookie'
|
|
19
19
|
module Sidekiq
|
20
20
|
class Web
|
21
21
|
ROOT = File.expand_path("#{File.dirname(__FILE__)}/../../web")
|
22
|
-
VIEWS = "#{ROOT}/views"
|
23
|
-
LOCALES = ["#{ROOT}/locales"
|
24
|
-
LAYOUT = "#{VIEWS}/layout.erb"
|
25
|
-
ASSETS = "#{ROOT}/assets"
|
22
|
+
VIEWS = "#{ROOT}/views"
|
23
|
+
LOCALES = ["#{ROOT}/locales"]
|
24
|
+
LAYOUT = "#{VIEWS}/layout.erb"
|
25
|
+
ASSETS = "#{ROOT}/assets"
|
26
26
|
|
27
27
|
DEFAULT_TABS = {
|
28
28
|
"Dashboard" => '',
|
data/lib/sidekiq/worker.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
require 'sidekiq/client'
|
3
|
-
require 'sidekiq/core_ext'
|
4
3
|
|
5
4
|
module Sidekiq
|
6
5
|
|
@@ -8,13 +7,13 @@ module Sidekiq
|
|
8
7
|
# Include this module in your worker class and you can easily create
|
9
8
|
# asynchronous jobs:
|
10
9
|
#
|
11
|
-
#
|
12
|
-
#
|
10
|
+
# class HardWorker
|
11
|
+
# include Sidekiq::Worker
|
13
12
|
#
|
14
|
-
#
|
15
|
-
#
|
13
|
+
# def perform(*args)
|
14
|
+
# # do some work
|
15
|
+
# end
|
16
16
|
# end
|
17
|
-
# end
|
18
17
|
#
|
19
18
|
# Then in your Rails app, you can do this:
|
20
19
|
#
|
@@ -28,16 +27,51 @@ module Sidekiq
|
|
28
27
|
raise ArgumentError, "You cannot include Sidekiq::Worker in an ActiveJob: #{base.name}" if base.ancestors.any? {|c| c.name == 'ActiveJob::Base' }
|
29
28
|
|
30
29
|
base.extend(ClassMethods)
|
31
|
-
base.
|
32
|
-
base.
|
33
|
-
base.
|
30
|
+
base.sidekiq_class_attribute :sidekiq_options_hash
|
31
|
+
base.sidekiq_class_attribute :sidekiq_retry_in_block
|
32
|
+
base.sidekiq_class_attribute :sidekiq_retries_exhausted_block
|
34
33
|
end
|
35
34
|
|
36
35
|
def logger
|
37
36
|
Sidekiq.logger
|
38
37
|
end
|
39
38
|
|
39
|
+
# This helper class encapsulates the set options for `set`, e.g.
|
40
|
+
#
|
41
|
+
# SomeWorker.set(queue: 'foo').perform_async(....)
|
42
|
+
#
|
43
|
+
class Setter
|
44
|
+
def initialize(klass, opts)
|
45
|
+
@klass = klass
|
46
|
+
@opts = opts
|
47
|
+
end
|
48
|
+
|
49
|
+
def set(options)
|
50
|
+
@opts.merge!(options)
|
51
|
+
self
|
52
|
+
end
|
53
|
+
|
54
|
+
def perform_async(*args)
|
55
|
+
@klass.client_push(@opts.merge('args' => args, 'class' => @klass))
|
56
|
+
end
|
57
|
+
|
58
|
+
# +interval+ must be a timestamp, numeric or something that acts
|
59
|
+
# numeric (like an activesupport time interval).
|
60
|
+
def perform_in(interval, *args)
|
61
|
+
int = interval.to_f
|
62
|
+
now = Time.now.to_f
|
63
|
+
ts = (int < 1_000_000_000 ? now + int : int)
|
64
|
+
|
65
|
+
payload = @opts.merge('class' => @klass, 'args' => args, 'at' => ts)
|
66
|
+
# Optimization to enqueue something now that is scheduled to go out now or in the past
|
67
|
+
payload.delete('at') if ts <= now
|
68
|
+
@klass.client_push(payload)
|
69
|
+
end
|
70
|
+
alias_method :perform_at, :perform_in
|
71
|
+
end
|
72
|
+
|
40
73
|
module ClassMethods
|
74
|
+
ACCESSOR_MUTEX = Mutex.new
|
41
75
|
|
42
76
|
def delay(*args)
|
43
77
|
raise ArgumentError, "Do not call .delay on a Sidekiq::Worker class, call .perform_async"
|
@@ -52,8 +86,7 @@ module Sidekiq
|
|
52
86
|
end
|
53
87
|
|
54
88
|
def set(options)
|
55
|
-
|
56
|
-
self
|
89
|
+
Setter.new(self, options)
|
57
90
|
end
|
58
91
|
|
59
92
|
def perform_async(*args)
|
@@ -70,7 +103,7 @@ module Sidekiq
|
|
70
103
|
item = { 'class' => self, 'args' => args, 'at' => ts }
|
71
104
|
|
72
105
|
# Optimization to enqueue something now that is scheduled to go out now or in the past
|
73
|
-
item.delete('at'
|
106
|
+
item.delete('at') if ts <= now
|
74
107
|
|
75
108
|
client_push(item)
|
76
109
|
end
|
@@ -90,7 +123,8 @@ module Sidekiq
|
|
90
123
|
# In practice, any option is allowed. This is the main mechanism to configure the
|
91
124
|
# options for a specific job.
|
92
125
|
def sidekiq_options(opts={})
|
93
|
-
|
126
|
+
# stringify
|
127
|
+
self.sidekiq_options_hash = get_sidekiq_options.merge(Hash[opts.map{|k, v| [k.to_s, v]}])
|
94
128
|
end
|
95
129
|
|
96
130
|
def sidekiq_retry_in(&block)
|
@@ -107,13 +141,78 @@ module Sidekiq
|
|
107
141
|
|
108
142
|
def client_push(item) # :nodoc:
|
109
143
|
pool = Thread.current[:sidekiq_via_pool] || get_sidekiq_options['pool'] || Sidekiq.redis_pool
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
144
|
+
# stringify
|
145
|
+
item.keys.each do |key|
|
146
|
+
item[key.to_s] = item.delete(key)
|
147
|
+
end
|
148
|
+
|
149
|
+
Sidekiq::Client.new(pool).push(item)
|
150
|
+
end
|
151
|
+
|
152
|
+
def sidekiq_class_attribute(*attrs)
|
153
|
+
instance_reader = true
|
154
|
+
instance_writer = true
|
155
|
+
|
156
|
+
attrs.each do |name|
|
157
|
+
synchronized_getter = "__synchronized_#{name}"
|
158
|
+
|
159
|
+
singleton_class.instance_eval do
|
160
|
+
undef_method(name) if method_defined?(name) || private_method_defined?(name)
|
161
|
+
end
|
162
|
+
|
163
|
+
define_singleton_method(synchronized_getter) { nil }
|
164
|
+
singleton_class.class_eval do
|
165
|
+
private(synchronized_getter)
|
166
|
+
end
|
167
|
+
|
168
|
+
define_singleton_method(name) { ACCESSOR_MUTEX.synchronize { send synchronized_getter } }
|
169
|
+
|
170
|
+
ivar = "@#{name}"
|
171
|
+
|
172
|
+
singleton_class.instance_eval do
|
173
|
+
m = "#{name}="
|
174
|
+
undef_method(m) if method_defined?(m) || private_method_defined?(m)
|
175
|
+
end
|
176
|
+
define_singleton_method("#{name}=") do |val|
|
177
|
+
singleton_class.class_eval do
|
178
|
+
ACCESSOR_MUTEX.synchronize do
|
179
|
+
undef_method(synchronized_getter) if method_defined?(synchronized_getter) || private_method_defined?(synchronized_getter)
|
180
|
+
define_method(synchronized_getter) { val }
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
if singleton_class?
|
185
|
+
class_eval do
|
186
|
+
undef_method(name) if method_defined?(name) || private_method_defined?(name)
|
187
|
+
define_method(name) do
|
188
|
+
if instance_variable_defined? ivar
|
189
|
+
instance_variable_get ivar
|
190
|
+
else
|
191
|
+
singleton_class.send name
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
val
|
197
|
+
end
|
198
|
+
|
199
|
+
if instance_reader
|
200
|
+
undef_method(name) if method_defined?(name) || private_method_defined?(name)
|
201
|
+
define_method(name) do
|
202
|
+
if instance_variable_defined?(ivar)
|
203
|
+
instance_variable_get ivar
|
204
|
+
else
|
205
|
+
self.class.public_send name
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
if instance_writer
|
211
|
+
m = "#{name}="
|
212
|
+
undef_method(m) if method_defined?(m) || private_method_defined?(m)
|
213
|
+
attr_writer name
|
214
|
+
end
|
115
215
|
end
|
116
|
-
Sidekiq::Client.new(pool).push(hash)
|
117
216
|
end
|
118
217
|
|
119
218
|
end
|