sidekiq 4.2.10 → 5.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of sidekiq might be problematic. Click here for more details.

Files changed (75) hide show
  1. checksums.yaml +5 -5
  2. data/.circleci/config.yml +61 -0
  3. data/.github/issue_template.md +3 -1
  4. data/.gitignore +3 -0
  5. data/.travis.yml +6 -13
  6. data/5.0-Upgrade.md +56 -0
  7. data/COMM-LICENSE +12 -10
  8. data/Changes.md +164 -1
  9. data/Ent-Changes.md +67 -2
  10. data/Gemfile +14 -20
  11. data/LICENSE +1 -1
  12. data/Pro-4.0-Upgrade.md +35 -0
  13. data/Pro-Changes.md +133 -2
  14. data/README.md +8 -6
  15. data/Rakefile +2 -5
  16. data/bin/sidekiqctl +13 -92
  17. data/bin/sidekiqload +5 -10
  18. data/lib/generators/sidekiq/templates/worker_spec.rb.erb +1 -1
  19. data/lib/sidekiq/api.rb +145 -57
  20. data/lib/sidekiq/cli.rb +120 -81
  21. data/lib/sidekiq/client.rb +25 -18
  22. data/lib/sidekiq/core_ext.rb +1 -119
  23. data/lib/sidekiq/ctl.rb +221 -0
  24. data/lib/sidekiq/delay.rb +42 -0
  25. data/lib/sidekiq/exception_handler.rb +2 -4
  26. data/lib/sidekiq/extensions/generic_proxy.rb +7 -1
  27. data/lib/sidekiq/fetch.rb +1 -1
  28. data/lib/sidekiq/job_logger.rb +25 -0
  29. data/lib/sidekiq/job_retry.rb +262 -0
  30. data/lib/sidekiq/launcher.rb +19 -19
  31. data/lib/sidekiq/logging.rb +18 -2
  32. data/lib/sidekiq/manager.rb +5 -6
  33. data/lib/sidekiq/middleware/server/active_record.rb +10 -0
  34. data/lib/sidekiq/processor.rb +126 -48
  35. data/lib/sidekiq/rails.rb +8 -73
  36. data/lib/sidekiq/redis_connection.rb +43 -5
  37. data/lib/sidekiq/scheduled.rb +35 -8
  38. data/lib/sidekiq/testing.rb +16 -7
  39. data/lib/sidekiq/util.rb +5 -2
  40. data/lib/sidekiq/version.rb +1 -1
  41. data/lib/sidekiq/web/action.rb +2 -6
  42. data/lib/sidekiq/web/application.rb +33 -16
  43. data/lib/sidekiq/web/helpers.rb +69 -22
  44. data/lib/sidekiq/web/router.rb +10 -10
  45. data/lib/sidekiq/web.rb +4 -4
  46. data/lib/sidekiq/worker.rb +118 -19
  47. data/lib/sidekiq.rb +27 -27
  48. data/sidekiq.gemspec +6 -17
  49. data/web/assets/javascripts/application.js +0 -0
  50. data/web/assets/javascripts/dashboard.js +32 -17
  51. data/web/assets/stylesheets/application-rtl.css +246 -0
  52. data/web/assets/stylesheets/application.css +371 -6
  53. data/web/assets/stylesheets/bootstrap-rtl.min.css +9 -0
  54. data/web/assets/stylesheets/bootstrap.css +2 -2
  55. data/web/locales/ar.yml +81 -0
  56. data/web/locales/en.yml +2 -0
  57. data/web/locales/es.yml +4 -3
  58. data/web/locales/fa.yml +1 -0
  59. data/web/locales/he.yml +79 -0
  60. data/web/locales/ja.yml +5 -3
  61. data/web/locales/ur.yml +80 -0
  62. data/web/views/_footer.erb +5 -2
  63. data/web/views/_nav.erb +4 -18
  64. data/web/views/_paging.erb +1 -1
  65. data/web/views/busy.erb +9 -5
  66. data/web/views/dashboard.erb +1 -1
  67. data/web/views/layout.erb +11 -2
  68. data/web/views/morgue.erb +4 -4
  69. data/web/views/queue.erb +8 -7
  70. data/web/views/queues.erb +2 -0
  71. data/web/views/retries.erb +9 -5
  72. data/web/views/scheduled.erb +2 -2
  73. metadata +31 -160
  74. data/lib/sidekiq/middleware/server/logging.rb +0 -31
  75. data/lib/sidekiq/middleware/server/retry_jobs.rb +0 -205
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
  ##
@@ -47,7 +46,10 @@ module Sidekiq
47
46
  @@identity ||= "#{hostname}:#{$$}:#{process_nonce}"
48
47
  end
49
48
 
50
- def fire_event(event, reverse=false)
49
+ def fire_event(event, options={})
50
+ reverse = options[:reverse]
51
+ reraise = options[:reraise]
52
+
51
53
  arr = Sidekiq.options[:lifecycle_events][event]
52
54
  arr.reverse! if reverse
53
55
  arr.each do |block|
@@ -55,6 +57,7 @@ module Sidekiq
55
57
  block.call
56
58
  rescue => ex
57
59
  handle_exception(ex, { context: "Exception during Sidekiq lifecycle event.", event: event })
60
+ raise ex if reraise
58
61
  end
59
62
  end
60
63
  arr.clear
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Sidekiq
3
- VERSION = "4.2.10"
3
+ VERSION = "5.2.8"
4
4
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Sidekiq
4
4
  class WebAction
5
- RACK_SESSION = 'rack.session'.freeze
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".freeze
8
- CONTENT_TYPE = "Content-Type".freeze
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
@@ -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 Integer
278
- [resp, {}, []]
279
298
  else
280
- type_header = case action.type
281
- when :json
282
- { "Content-Type" => "application/json", "Cache-Control" => "no-cache" }
283
- when String
284
- { "Content-Type" => (action.type || "text/html"), "Cache-Control" => "no-cache" }
285
- else
286
- { "Content-Type" => "text/html", "Cache-Control" => "no-cache" }
287
- end
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
@@ -15,7 +15,7 @@ module Sidekiq
15
15
  settings.locales.each_with_object({}) do |path, global|
16
16
  find_locale_files(lang).each do |file|
17
17
  strs = YAML.load(File.open(file))
18
- global.deep_merge!(strs[lang])
18
+ global.merge!(strs[lang])
19
19
  end
20
20
  end
21
21
  end
@@ -24,6 +24,7 @@ module Sidekiq
24
24
  def clear_caches
25
25
  @@strings = nil
26
26
  @@locale_files = nil
27
+ @@available_locales = nil
27
28
  end
28
29
 
29
30
  def locale_files
@@ -32,6 +33,10 @@ module Sidekiq
32
33
  end
33
34
  end
34
35
 
36
+ def available_locales
37
+ @@available_locales ||= locale_files.map { |path| File.basename(path, '.yml') }.uniq
38
+ end
39
+
35
40
  def find_locale_files(lang)
36
41
  locale_files.select { |file| file =~ /\/#{lang}\.yml$/ }
37
42
  end
@@ -65,20 +70,44 @@ module Sidekiq
65
70
  end
66
71
  end
67
72
 
68
- # Given a browser request Accept-Language header like
69
- # "fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4,ru;q=0.2", this function
70
- # will return "fr" since that's the first code with a matching
71
- # locale in web/locales
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
72
98
  def locale
73
99
  @locale ||= begin
74
- locale = 'en'.freeze
75
- languages = env['HTTP_ACCEPT_LANGUAGE'.freeze] || 'en'.freeze
76
- languages.downcase.split(','.freeze).each do |lang|
77
- next if lang == '*'.freeze
78
- lang = lang.split(';'.freeze)[0]
79
- break locale = lang if find_locale_files(lang).any?
80
- end
81
- locale
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'
82
111
  end
83
112
  end
84
113
 
@@ -92,7 +121,7 @@ module Sidekiq
92
121
  end
93
122
 
94
123
  def t(msg, options={})
95
- string = get_locale[msg] || msg
124
+ string = get_locale[msg] || strings('en')[msg] || msg
96
125
  if options.empty?
97
126
  string
98
127
  else
@@ -119,11 +148,14 @@ module Sidekiq
119
148
  end
120
149
 
121
150
  def redis_connection
122
- Sidekiq.redis { |conn| conn.client.id }
151
+ Sidekiq.redis do |conn|
152
+ c = conn.connection
153
+ "redis://#{c[:location]}/#{c[:db]}"
154
+ end
123
155
  end
124
156
 
125
157
  def namespace
126
- @@ns ||= Sidekiq.redis { |conn| conn.respond_to?(:namespace) ? conn.namespace : nil }
158
+ @ns ||= Sidekiq.redis { |conn| conn.respond_to?(:namespace) ? conn.namespace : nil }
127
159
  end
128
160
 
129
161
  def redis_info
@@ -144,7 +176,7 @@ module Sidekiq
144
176
 
145
177
  def relative_time(time)
146
178
  stamp = time.getutc.iso8601
147
- %{<time title="#{stamp}" datetime="#{stamp}">#{time}</time>}
179
+ %{<time class="ltr" dir="ltr" title="#{stamp}" datetime="#{stamp}">#{time}</time>}
148
180
  end
149
181
 
150
182
  def job_params(job, score)
@@ -152,7 +184,7 @@ module Sidekiq
152
184
  end
153
185
 
154
186
  def parse_params(params)
155
- score, jid = params.split("-")
187
+ score, jid = params.split("-", 2)
156
188
  [score.to_f, jid]
157
189
  end
158
190
 
@@ -160,7 +192,11 @@ module Sidekiq
160
192
 
161
193
  # Merge options with current params, filter safe params, and stringify to query string
162
194
  def qparams(options)
163
- options = options.stringify_keys
195
+ # stringify
196
+ options.keys.each do |key|
197
+ options[key.to_s] = options.delete(key)
198
+ end
199
+
164
200
  params.merge(options).map do |key, value|
165
201
  SAFE_QPARAMS.include?(key) ? "#{key}=#{CGI.escape(value.to_s)}" : next
166
202
  end.compact.join("&")
@@ -171,9 +207,16 @@ module Sidekiq
171
207
  end
172
208
 
173
209
  def display_args(args, truncate_after_chars = 2000)
174
- args.map do |arg|
175
- h(truncate(to_display(arg), truncate_after_chars))
176
- end.join(", ")
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
177
220
  end
178
221
 
179
222
  def csrf_tag
@@ -250,6 +293,10 @@ module Sidekiq
250
293
  "Sidekiq v#{Sidekiq::VERSION}"
251
294
  end
252
295
 
296
+ def server_utc_time
297
+ Time.now.utc.strftime('%H:%M:%S UTC')
298
+ end
299
+
253
300
  def redis_connection_and_namespace
254
301
  @redis_connection_and_namespace ||= begin
255
302
  namespace_suffix = namespace == nil ? '' : "##{namespace}"
@@ -3,16 +3,16 @@ require 'rack'
3
3
 
4
4
  module Sidekiq
5
5
  module WebRouter
6
- GET = 'GET'.freeze
7
- DELETE = 'DELETE'.freeze
8
- POST = 'POST'.freeze
9
- PUT = 'PUT'.freeze
10
- PATCH = 'PATCH'.freeze
11
- HEAD = 'HEAD'.freeze
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'.freeze
14
- REQUEST_METHOD = 'REQUEST_METHOD'.freeze
15
- PATH_INFO = 'PATH_INFO'.freeze
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 = /\/([^\/]*):([^\.:$\/]+)/.freeze
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".freeze
23
- LOCALES = ["#{ROOT}/locales".freeze]
24
- LAYOUT = "#{VIEWS}/layout.erb".freeze
25
- ASSETS = "#{ROOT}/assets".freeze
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" => '',
@@ -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
- # class HardWorker
12
- # include Sidekiq::Worker
10
+ # class HardWorker
11
+ # include Sidekiq::Worker
13
12
  #
14
- # def perform(*args)
15
- # # do some work
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.class_attribute :sidekiq_options_hash
32
- base.class_attribute :sidekiq_retry_in_block
33
- base.class_attribute :sidekiq_retries_exhausted_block
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
- Thread.current[:sidekiq_worker_set] = options
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'.freeze) if ts <= now
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
- self.sidekiq_options_hash = get_sidekiq_options.merge(opts.stringify_keys)
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
- hash = if Thread.current[:sidekiq_worker_set]
111
- x, Thread.current[:sidekiq_worker_set] = Thread.current[:sidekiq_worker_set], nil
112
- x.stringify_keys.merge(item.stringify_keys)
113
- else
114
- item.stringify_keys
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
data/lib/sidekiq.rb CHANGED
@@ -1,12 +1,13 @@
1
- # encoding: utf-8
2
1
  # frozen_string_literal: true
2
+
3
3
  require 'sidekiq/version'
4
- fail "Sidekiq #{Sidekiq::VERSION} does not support Ruby versions below 2.0.0." if RUBY_PLATFORM != 'java' && RUBY_VERSION < '2.0.0'
4
+ fail "Sidekiq #{Sidekiq::VERSION} does not support Ruby versions below 2.2.2." if RUBY_PLATFORM != 'java' && Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.2.2')
5
5
 
6
6
  require 'sidekiq/logging'
7
7
  require 'sidekiq/client'
8
8
  require 'sidekiq/worker'
9
9
  require 'sidekiq/redis_connection'
10
+ require 'sidekiq/delay'
10
11
 
11
12
  require 'json'
12
13
 
@@ -17,13 +18,14 @@ module Sidekiq
17
18
  DEFAULTS = {
18
19
  queues: [],
19
20
  labels: [],
20
- concurrency: 25,
21
+ concurrency: 10,
21
22
  require: '.',
22
23
  environment: nil,
23
24
  timeout: 8,
24
25
  poll_interval_average: nil,
25
- average_scheduled_poll_interval: 15,
26
+ average_scheduled_poll_interval: 5,
26
27
  error_handlers: [],
28
+ death_handlers: [],
27
29
  lifecycle_events: {
28
30
  startup: [],
29
31
  quiet: [],
@@ -33,7 +35,6 @@ module Sidekiq
33
35
  dead_max_jobs: 10_000,
34
36
  dead_timeout_in_seconds: 180 * 24 * 60 * 60, # 6 months
35
37
  reloader: proc { |&block| block.call },
36
- executor: proc { |&block| block.call },
37
38
  }
38
39
 
39
40
  DEFAULT_WORKER_OPTIONS = {
@@ -47,7 +48,7 @@ module Sidekiq
47
48
  "connected_clients" => "9999",
48
49
  "used_memory_human" => "9P",
49
50
  "used_memory_peak_human" => "9P"
50
- }.freeze
51
+ }
51
52
 
52
53
  def self.❨╯°□°❩╯︵┻━┻
53
54
  puts "Calm down, yo."
@@ -56,6 +57,7 @@ module Sidekiq
56
57
  def self.options
57
58
  @options ||= DEFAULTS.dup
58
59
  end
60
+
59
61
  def self.options=(opts)
60
62
  @options = opts
61
63
  end
@@ -94,8 +96,8 @@ module Sidekiq
94
96
  begin
95
97
  yield conn
96
98
  rescue Redis::CommandError => ex
97
- #2550 Failover can cause the server to become a slave, need
98
- # to disconnect and reopen the socket to get back to the master.
99
+ #2550 Failover can cause the server to become a replica, need
100
+ # to disconnect and reopen the socket to get back to the primary.
99
101
  (conn.disconnect!; retryable = false; retry) if retryable && ex.message =~ /READONLY/
100
102
  raise
101
103
  end
@@ -145,32 +147,34 @@ module Sidekiq
145
147
  end
146
148
 
147
149
  def self.default_server_middleware
148
- require 'sidekiq/middleware/server/retry_jobs'
149
- require 'sidekiq/middleware/server/logging'
150
-
151
- Middleware::Chain.new do |m|
152
- m.add Middleware::Server::RetryJobs
153
- m.add Middleware::Server::Logging
154
- end
150
+ Middleware::Chain.new
155
151
  end
156
152
 
157
153
  def self.default_worker_options=(hash)
158
- @default_worker_options = default_worker_options.merge(hash.stringify_keys)
154
+ # stringify
155
+ @default_worker_options = default_worker_options.merge(Hash[hash.map{|k, v| [k.to_s, v]}])
159
156
  end
160
157
  def self.default_worker_options
161
158
  defined?(@default_worker_options) ? @default_worker_options : DEFAULT_WORKER_OPTIONS
162
159
  end
163
160
 
161
+ def self.default_retries_exhausted=(prok)
162
+ logger.info { "default_retries_exhausted is deprecated, please use `config.death_handlers << -> {|job, ex| }`" }
163
+ return nil unless prok
164
+ death_handlers << prok
165
+ end
166
+
167
+ ##
168
+ # Death handlers are called when all retries for a job have been exhausted and
169
+ # the job dies. It's the notification to your application
170
+ # that this job will not succeed without manual intervention.
171
+ #
164
172
  # Sidekiq.configure_server do |config|
165
- # config.default_retries_exhausted = -> (job, ex) do
173
+ # config.death_handlers << ->(job, ex) do
166
174
  # end
167
175
  # end
168
- def self.default_retries_exhausted=(prok)
169
- @default_retries_exhausted = prok
170
- end
171
- @default_retries_exhausted = ->(job, ex) { }
172
- def self.default_retries_exhausted
173
- @default_retries_exhausted
176
+ def self.death_handlers
177
+ options[:death_handlers]
174
178
  end
175
179
 
176
180
  def self.load_json(string)
@@ -228,10 +232,6 @@ module Sidekiq
228
232
  # otherwise Ruby's Thread#kill will commit. See #377.
229
233
  # DO NOT RESCUE THIS ERROR IN YOUR WORKERS
230
234
  class Shutdown < Interrupt; end
231
-
232
235
  end
233
236
 
234
- require 'sidekiq/extensions/class_methods'
235
- require 'sidekiq/extensions/action_mailer'
236
- require 'sidekiq/extensions/active_record'
237
237
  require 'sidekiq/rails' if defined?(::Rails::Engine)