sidekiq 5.2.10 → 6.5.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (126) hide show
  1. checksums.yaml +4 -4
  2. data/Changes.md +422 -1
  3. data/LICENSE +3 -3
  4. data/README.md +24 -35
  5. data/bin/sidekiq +27 -3
  6. data/bin/sidekiqload +79 -67
  7. data/bin/sidekiqmon +8 -0
  8. data/lib/generators/sidekiq/job_generator.rb +57 -0
  9. data/lib/generators/sidekiq/templates/{worker.rb.erb → job.rb.erb} +2 -2
  10. data/lib/generators/sidekiq/templates/{worker_spec.rb.erb → job_spec.rb.erb} +1 -1
  11. data/lib/generators/sidekiq/templates/{worker_test.rb.erb → job_test.rb.erb} +1 -1
  12. data/lib/sidekiq/api.rb +527 -310
  13. data/lib/sidekiq/cli.rb +204 -208
  14. data/lib/sidekiq/client.rb +78 -82
  15. data/lib/sidekiq/component.rb +65 -0
  16. data/lib/sidekiq/delay.rb +8 -7
  17. data/lib/sidekiq/extensions/action_mailer.rb +13 -22
  18. data/lib/sidekiq/extensions/active_record.rb +13 -10
  19. data/lib/sidekiq/extensions/class_methods.rb +14 -11
  20. data/lib/sidekiq/extensions/generic_proxy.rb +7 -5
  21. data/lib/sidekiq/fetch.rb +50 -40
  22. data/lib/sidekiq/job.rb +13 -0
  23. data/lib/sidekiq/job_logger.rb +33 -7
  24. data/lib/sidekiq/job_retry.rb +126 -106
  25. data/lib/sidekiq/job_util.rb +71 -0
  26. data/lib/sidekiq/launcher.rb +177 -83
  27. data/lib/sidekiq/logger.rb +156 -0
  28. data/lib/sidekiq/manager.rb +40 -41
  29. data/lib/sidekiq/metrics/deploy.rb +47 -0
  30. data/lib/sidekiq/metrics/query.rb +153 -0
  31. data/lib/sidekiq/metrics/shared.rb +94 -0
  32. data/lib/sidekiq/metrics/tracking.rb +134 -0
  33. data/lib/sidekiq/middleware/chain.rb +102 -46
  34. data/lib/sidekiq/middleware/current_attributes.rb +63 -0
  35. data/lib/sidekiq/middleware/i18n.rb +7 -7
  36. data/lib/sidekiq/middleware/modules.rb +21 -0
  37. data/lib/sidekiq/monitor.rb +133 -0
  38. data/lib/sidekiq/paginator.rb +28 -16
  39. data/lib/sidekiq/processor.rb +104 -97
  40. data/lib/sidekiq/rails.rb +46 -37
  41. data/lib/sidekiq/redis_client_adapter.rb +154 -0
  42. data/lib/sidekiq/redis_connection.rb +108 -77
  43. data/lib/sidekiq/ring_buffer.rb +29 -0
  44. data/lib/sidekiq/scheduled.rb +105 -42
  45. data/lib/sidekiq/sd_notify.rb +149 -0
  46. data/lib/sidekiq/systemd.rb +24 -0
  47. data/lib/sidekiq/testing/inline.rb +6 -5
  48. data/lib/sidekiq/testing.rb +68 -58
  49. data/lib/sidekiq/transaction_aware_client.rb +45 -0
  50. data/lib/sidekiq/version.rb +2 -1
  51. data/lib/sidekiq/web/action.rb +15 -11
  52. data/lib/sidekiq/web/application.rb +103 -77
  53. data/lib/sidekiq/web/csrf_protection.rb +180 -0
  54. data/lib/sidekiq/web/helpers.rb +125 -95
  55. data/lib/sidekiq/web/router.rb +23 -19
  56. data/lib/sidekiq/web.rb +65 -105
  57. data/lib/sidekiq/worker.rb +259 -109
  58. data/lib/sidekiq.rb +170 -62
  59. data/sidekiq.gemspec +23 -16
  60. data/web/assets/images/apple-touch-icon.png +0 -0
  61. data/web/assets/javascripts/application.js +113 -61
  62. data/web/assets/javascripts/chart.min.js +13 -0
  63. data/web/assets/javascripts/chartjs-plugin-annotation.min.js +7 -0
  64. data/web/assets/javascripts/dashboard.js +53 -89
  65. data/web/assets/javascripts/graph.js +16 -0
  66. data/web/assets/javascripts/metrics.js +262 -0
  67. data/web/assets/stylesheets/application-dark.css +143 -0
  68. data/web/assets/stylesheets/application-rtl.css +0 -4
  69. data/web/assets/stylesheets/application.css +88 -233
  70. data/web/locales/ar.yml +8 -2
  71. data/web/locales/de.yml +14 -2
  72. data/web/locales/el.yml +43 -19
  73. data/web/locales/en.yml +13 -1
  74. data/web/locales/es.yml +18 -2
  75. data/web/locales/fr.yml +10 -3
  76. data/web/locales/ja.yml +14 -1
  77. data/web/locales/lt.yml +83 -0
  78. data/web/locales/pl.yml +4 -4
  79. data/web/locales/pt-br.yml +27 -9
  80. data/web/locales/ru.yml +4 -0
  81. data/web/locales/vi.yml +83 -0
  82. data/web/locales/zh-cn.yml +36 -11
  83. data/web/locales/zh-tw.yml +32 -7
  84. data/web/views/_footer.erb +1 -1
  85. data/web/views/_job_info.erb +3 -2
  86. data/web/views/_nav.erb +1 -1
  87. data/web/views/_poll_link.erb +2 -5
  88. data/web/views/_summary.erb +7 -7
  89. data/web/views/busy.erb +61 -22
  90. data/web/views/dashboard.erb +23 -14
  91. data/web/views/dead.erb +3 -3
  92. data/web/views/layout.erb +3 -1
  93. data/web/views/metrics.erb +69 -0
  94. data/web/views/metrics_for_job.erb +87 -0
  95. data/web/views/morgue.erb +9 -6
  96. data/web/views/queue.erb +23 -10
  97. data/web/views/queues.erb +10 -2
  98. data/web/views/retries.erb +11 -8
  99. data/web/views/retry.erb +3 -3
  100. data/web/views/scheduled.erb +5 -2
  101. metadata +58 -63
  102. data/.circleci/config.yml +0 -61
  103. data/.github/contributing.md +0 -32
  104. data/.github/issue_template.md +0 -11
  105. data/.gitignore +0 -15
  106. data/.travis.yml +0 -11
  107. data/3.0-Upgrade.md +0 -70
  108. data/4.0-Upgrade.md +0 -53
  109. data/5.0-Upgrade.md +0 -56
  110. data/COMM-LICENSE +0 -97
  111. data/Ent-Changes.md +0 -238
  112. data/Gemfile +0 -19
  113. data/Pro-2.0-Upgrade.md +0 -138
  114. data/Pro-3.0-Upgrade.md +0 -44
  115. data/Pro-4.0-Upgrade.md +0 -35
  116. data/Pro-Changes.md +0 -759
  117. data/Rakefile +0 -9
  118. data/bin/sidekiqctl +0 -20
  119. data/code_of_conduct.md +0 -50
  120. data/lib/generators/sidekiq/worker_generator.rb +0 -49
  121. data/lib/sidekiq/core_ext.rb +0 -1
  122. data/lib/sidekiq/ctl.rb +0 -221
  123. data/lib/sidekiq/exception_handler.rb +0 -29
  124. data/lib/sidekiq/logging.rb +0 -122
  125. data/lib/sidekiq/middleware/server/active_record.rb +0 -23
  126. data/lib/sidekiq/util.rb +0 -66
@@ -1,9 +1,12 @@
1
1
  # frozen_string_literal: true
2
- require 'securerandom'
3
- require 'sidekiq/middleware/chain'
2
+
3
+ require "securerandom"
4
+ require "sidekiq/middleware/chain"
5
+ require "sidekiq/job_util"
4
6
 
5
7
  module Sidekiq
6
8
  class Client
9
+ include Sidekiq::JobUtil
7
10
 
8
11
  ##
9
12
  # Define client-side middleware:
@@ -12,14 +15,14 @@ module Sidekiq
12
15
  # client.middleware do |chain|
13
16
  # chain.use MyClientMiddleware
14
17
  # end
15
- # client.push('class' => 'SomeWorker', 'args' => [1,2,3])
18
+ # client.push('class' => 'SomeJob', 'args' => [1,2,3])
16
19
  #
17
20
  # All client instances default to the globally-defined
18
21
  # Sidekiq.client_middleware but you can change as necessary.
19
22
  #
20
23
  def middleware(&block)
21
24
  @chain ||= Sidekiq.client_middleware
22
- if block_given?
25
+ if block
23
26
  @chain = @chain.dup
24
27
  yield @chain
25
28
  end
@@ -38,7 +41,7 @@ module Sidekiq
38
41
  # Generally this is only needed for very large Sidekiq installs processing
39
42
  # thousands of jobs per second. I don't recommend sharding unless you
40
43
  # cannot scale any other way (e.g. splitting your app into smaller apps).
41
- def initialize(redis_pool=nil)
44
+ def initialize(redis_pool = nil)
42
45
  @redis_pool = redis_pool || Thread.current[:sidekiq_via_pool] || Sidekiq.redis_pool
43
46
  end
44
47
 
@@ -46,16 +49,16 @@ module Sidekiq
46
49
  # The main method used to push a job to Redis. Accepts a number of options:
47
50
  #
48
51
  # queue - the named queue to use, default 'default'
49
- # class - the worker class to call, required
52
+ # class - the job class to call, required
50
53
  # args - an array of simple arguments to the perform method, must be JSON-serializable
51
54
  # at - timestamp to schedule the job (optional), must be Numeric (e.g. Time.now.to_f)
52
55
  # retry - whether to retry this job if it fails, default true or an integer number of retries
53
56
  # backtrace - whether to save any error backtrace, default false
54
57
  #
55
58
  # If class is set to the class name, the jobs' options will be based on Sidekiq's default
56
- # worker options. Otherwise, they will be based on the job class's options.
59
+ # job options. Otherwise, they will be based on the job class's options.
57
60
  #
58
- # Any options valid for a worker class's sidekiq_options are also available here.
61
+ # Any options valid for a job class's sidekiq_options are also available here.
59
62
  #
60
63
  # All options must be strings, not symbols. NB: because we are serializing to JSON, all
61
64
  # symbols in 'args' will be converted to strings. Note that +backtrace: true+ can take quite a bit of
@@ -64,15 +67,17 @@ module Sidekiq
64
67
  # Returns a unique Job ID. If middleware stops the job, nil will be returned instead.
65
68
  #
66
69
  # Example:
67
- # push('queue' => 'my_queue', 'class' => MyWorker, 'args' => ['foo', 1, :bat => 'bar'])
70
+ # push('queue' => 'my_queue', 'class' => MyJob, 'args' => ['foo', 1, :bat => 'bar'])
68
71
  #
69
72
  def push(item)
70
73
  normed = normalize_item(item)
71
- payload = process_single(item['class'], normed)
72
-
74
+ payload = middleware.invoke(item["class"], normed, normed["queue"], @redis_pool) do
75
+ normed
76
+ end
73
77
  if payload
78
+ verify_json(payload)
74
79
  raw_push([payload])
75
- payload['jid']
80
+ payload["jid"]
76
81
  end
77
82
  end
78
83
 
@@ -90,19 +95,30 @@ module Sidekiq
90
95
  # Returns an array of the of pushed jobs' jids. The number of jobs pushed can be less
91
96
  # than the number given if the middleware stopped processing for one or more jobs.
92
97
  def push_bulk(items)
93
- arg = items['args'].first
94
- return [] unless arg # no jobs to push
95
- raise ArgumentError, "Bulk arguments must be an Array of Arrays: [[1], [2]]" if !arg.is_a?(Array)
98
+ args = items["args"]
99
+ raise ArgumentError, "Bulk arguments must be an Array of Arrays: [[1], [2]]" unless args.is_a?(Array) && args.all?(Array)
100
+ return [] if args.empty? # no jobs to push
101
+
102
+ at = items.delete("at")
103
+ raise ArgumentError, "Job 'at' must be a Numeric or an Array of Numeric timestamps" if at && (Array(at).empty? || !Array(at).all? { |entry| entry.is_a?(Numeric) })
104
+ raise ArgumentError, "Job 'at' Array must have same size as 'args' Array" if at.is_a?(Array) && at.size != args.size
105
+
106
+ jid = items.delete("jid")
107
+ raise ArgumentError, "Explicitly passing 'jid' when pushing more than one job is not supported" if jid && args.size > 1
96
108
 
97
109
  normed = normalize_item(items)
98
- payloads = items['args'].map do |args|
99
- copy = normed.merge('args' => args, 'jid' => SecureRandom.hex(12), 'enqueued_at' => Time.now.to_f)
100
- result = process_single(items['class'], copy)
101
- result ? result : nil
102
- end.compact
103
-
104
- raw_push(payloads) if !payloads.empty?
105
- payloads.collect { |payload| payload['jid'] }
110
+ payloads = args.map.with_index { |job_args, index|
111
+ copy = normed.merge("args" => job_args, "jid" => SecureRandom.hex(12))
112
+ copy["at"] = (at.is_a?(Array) ? at[index] : at) if at
113
+ result = middleware.invoke(items["class"], copy, copy["queue"], @redis_pool) do
114
+ verify_json(copy)
115
+ copy
116
+ end
117
+ result || nil
118
+ }.compact
119
+
120
+ raw_push(payloads) unless payloads.empty?
121
+ payloads.collect { |payload| payload["jid"] }
106
122
  end
107
123
 
108
124
  # Allows sharding of jobs across any number of Redis instances. All jobs
@@ -110,8 +126,8 @@ module Sidekiq
110
126
  #
111
127
  # pool = ConnectionPool.new { Redis.new }
112
128
  # Sidekiq::Client.via(pool) do
113
- # SomeWorker.perform_async(1,2,3)
114
- # SomeOtherWorker.perform_async(1,2,3)
129
+ # SomeJob.perform_async(1,2,3)
130
+ # SomeOtherJob.perform_async(1,2,3)
115
131
  # end
116
132
  #
117
133
  # Generally this is only needed for very large Sidekiq installs processing
@@ -127,7 +143,6 @@ module Sidekiq
127
143
  end
128
144
 
129
145
  class << self
130
-
131
146
  def push(item)
132
147
  new.push(item)
133
148
  end
@@ -137,40 +152,40 @@ module Sidekiq
137
152
  end
138
153
 
139
154
  # Resque compatibility helpers. Note all helpers
140
- # should go through Worker#client_push.
155
+ # should go through Sidekiq::Job#client_push.
141
156
  #
142
157
  # Example usage:
143
- # Sidekiq::Client.enqueue(MyWorker, 'foo', 1, :bat => 'bar')
158
+ # Sidekiq::Client.enqueue(MyJob, 'foo', 1, :bat => 'bar')
144
159
  #
145
160
  # Messages are enqueued to the 'default' queue.
146
161
  #
147
162
  def enqueue(klass, *args)
148
- klass.client_push('class' => klass, 'args' => args)
163
+ klass.client_push("class" => klass, "args" => args)
149
164
  end
150
165
 
151
166
  # Example usage:
152
- # Sidekiq::Client.enqueue_to(:queue_name, MyWorker, 'foo', 1, :bat => 'bar')
167
+ # Sidekiq::Client.enqueue_to(:queue_name, MyJob, 'foo', 1, :bat => 'bar')
153
168
  #
154
169
  def enqueue_to(queue, klass, *args)
155
- klass.client_push('queue' => queue, 'class' => klass, 'args' => args)
170
+ klass.client_push("queue" => queue, "class" => klass, "args" => args)
156
171
  end
157
172
 
158
173
  # Example usage:
159
- # Sidekiq::Client.enqueue_to_in(:queue_name, 3.minutes, MyWorker, 'foo', 1, :bat => 'bar')
174
+ # Sidekiq::Client.enqueue_to_in(:queue_name, 3.minutes, MyJob, 'foo', 1, :bat => 'bar')
160
175
  #
161
176
  def enqueue_to_in(queue, interval, klass, *args)
162
177
  int = interval.to_f
163
178
  now = Time.now.to_f
164
- ts = (int < 1_000_000_000 ? now + int : int)
179
+ ts = ((int < 1_000_000_000) ? now + int : int)
165
180
 
166
- item = { 'class' => klass, 'args' => args, 'at' => ts, 'queue' => queue }
167
- item.delete('at') if ts <= now
181
+ item = {"class" => klass, "args" => args, "at" => ts, "queue" => queue}
182
+ item.delete("at") if ts <= now
168
183
 
169
184
  klass.client_push(item)
170
185
  end
171
186
 
172
187
  # Example usage:
173
- # Sidekiq::Client.enqueue_in(3.minutes, MyWorker, 'foo', 1, :bat => 'bar')
188
+ # Sidekiq::Client.enqueue_in(3.minutes, MyJob, 'foo', 1, :bat => 'bar')
174
189
  #
175
190
  def enqueue_in(interval, klass, *args)
176
191
  klass.perform_in(interval, *args)
@@ -181,62 +196,43 @@ module Sidekiq
181
196
 
182
197
  def raw_push(payloads)
183
198
  @redis_pool.with do |conn|
184
- conn.multi do
185
- atomic_push(conn, payloads)
199
+ retryable = true
200
+ begin
201
+ conn.pipelined do |pipeline|
202
+ atomic_push(pipeline, payloads)
203
+ end
204
+ rescue RedisConnection.adapter::BaseError => ex
205
+ # 2550 Failover can cause the server to become a replica, need
206
+ # to disconnect and reopen the socket to get back to the primary.
207
+ # 4495 Use the same logic if we have a "Not enough replicas" error from the primary
208
+ # 4985 Use the same logic when a blocking command is force-unblocked
209
+ # The retry logic is copied from sidekiq.rb
210
+ if retryable && ex.message =~ /READONLY|NOREPLICAS|UNBLOCKED/
211
+ conn.disconnect!
212
+ retryable = false
213
+ retry
214
+ end
215
+ raise
186
216
  end
187
217
  end
188
218
  true
189
219
  end
190
220
 
191
221
  def atomic_push(conn, payloads)
192
- if payloads.first['at']
193
- conn.zadd('schedule', payloads.map do |hash|
194
- at = hash.delete('at').to_s
222
+ if payloads.first.key?("at")
223
+ conn.zadd("schedule", payloads.flat_map { |hash|
224
+ at = hash.delete("at").to_s
195
225
  [at, Sidekiq.dump_json(hash)]
196
- end)
226
+ })
197
227
  else
198
- q = payloads.first['queue']
228
+ queue = payloads.first["queue"]
199
229
  now = Time.now.to_f
200
- to_push = payloads.map do |entry|
201
- entry['enqueued_at'] = now
230
+ to_push = payloads.map { |entry|
231
+ entry["enqueued_at"] = now
202
232
  Sidekiq.dump_json(entry)
203
- end
204
- conn.sadd('queues', q)
205
- conn.lpush("queue:#{q}", to_push)
206
- end
207
- end
208
-
209
- def process_single(worker_class, item)
210
- queue = item['queue']
211
-
212
- middleware.invoke(worker_class, item, queue, @redis_pool) do
213
- item
214
- end
215
- end
216
-
217
- def normalize_item(item)
218
- raise(ArgumentError, "Job must be a Hash with 'class' and 'args' keys: { 'class' => SomeWorker, 'args' => ['bob', 1, :foo => 'bar'] }") unless item.is_a?(Hash) && item.has_key?('class') && item.has_key?('args')
219
- raise(ArgumentError, "Job args must be an Array") unless item['args'].is_a?(Array)
220
- raise(ArgumentError, "Job class must be either a Class or String representation of the class name") unless item['class'].is_a?(Class) || item['class'].is_a?(String)
221
- raise(ArgumentError, "Job 'at' must be a Numeric timestamp") if item.has_key?('at') && !item['at'].is_a?(Numeric)
222
- #raise(ArgumentError, "Arguments must be native JSON types, see https://github.com/mperham/sidekiq/wiki/Best-Practices") unless JSON.load(JSON.dump(item['args'])) == item['args']
223
-
224
- normalized_hash(item['class'])
225
- .each{ |key, value| item[key] = value if item[key].nil? }
226
-
227
- item['class'] = item['class'].to_s
228
- item['queue'] = item['queue'].to_s
229
- item['jid'] ||= SecureRandom.hex(12)
230
- item['created_at'] ||= Time.now.to_f
231
- item
232
- end
233
-
234
- def normalized_hash(item_class)
235
- if item_class.is_a?(Class)
236
- raise(ArgumentError, "Message must include a Sidekiq::Worker class, not class name: #{item_class.ancestors.inspect}") if !item_class.respond_to?('get_sidekiq_options')
237
- item_class.get_sidekiq_options
238
- else
239
- Sidekiq.default_worker_options
233
+ }
234
+ conn.sadd("queues", [queue])
235
+ conn.lpush("queue:#{queue}", to_push)
240
236
  end
241
237
  end
242
238
  end
@@ -0,0 +1,65 @@
1
+ module Sidekiq
2
+ ##
3
+ # Sidekiq::Component assumes a config instance is available at @config
4
+ module Component # :nodoc:
5
+ attr_reader :config
6
+
7
+ def watchdog(last_words)
8
+ yield
9
+ rescue Exception => ex
10
+ handle_exception(ex, {context: last_words})
11
+ raise ex
12
+ end
13
+
14
+ def safe_thread(name, &block)
15
+ Thread.new do
16
+ Thread.current.name = name
17
+ watchdog(name, &block)
18
+ end
19
+ end
20
+
21
+ def logger
22
+ config.logger
23
+ end
24
+
25
+ def redis(&block)
26
+ config.redis(&block)
27
+ end
28
+
29
+ def tid
30
+ Thread.current["sidekiq_tid"] ||= (Thread.current.object_id ^ ::Process.pid).to_s(36)
31
+ end
32
+
33
+ def hostname
34
+ ENV["DYNO"] || Socket.gethostname
35
+ end
36
+
37
+ def process_nonce
38
+ @@process_nonce ||= SecureRandom.hex(6)
39
+ end
40
+
41
+ def identity
42
+ @@identity ||= "#{hostname}:#{::Process.pid}:#{process_nonce}"
43
+ end
44
+
45
+ def handle_exception(ex, ctx = {})
46
+ config.handle_exception(ex, ctx)
47
+ end
48
+
49
+ def fire_event(event, options = {})
50
+ oneshot = options.fetch(:oneshot, true)
51
+ reverse = options[:reverse]
52
+ reraise = options[:reraise]
53
+
54
+ arr = config[:lifecycle_events][event]
55
+ arr.reverse! if reverse
56
+ arr.each do |block|
57
+ block.call
58
+ rescue => ex
59
+ handle_exception(ex, {context: "Exception during Sidekiq lifecycle event.", event: event})
60
+ raise ex if reraise
61
+ end
62
+ arr.clear if oneshot # once we've fired an event, we never fire it again
63
+ end
64
+ end
65
+ end
data/lib/sidekiq/delay.rb CHANGED
@@ -1,11 +1,13 @@
1
1
  # frozen_string_literal: true
2
- module Sidekiq
3
- module Extensions
4
2
 
3
+ module Sidekiq # :nodoc:
4
+ module Extensions
5
5
  def self.enable_delay!
6
+ warn "Sidekiq's Delayed Extensions will be removed in Sidekiq 7.0", uplevel: 1
7
+
6
8
  if defined?(::ActiveSupport)
7
- require 'sidekiq/extensions/active_record'
8
- require 'sidekiq/extensions/action_mailer'
9
+ require "sidekiq/extensions/active_record"
10
+ require "sidekiq/extensions/action_mailer"
9
11
 
10
12
  # Need to patch Psych so it can autoload classes whose names are serialized
11
13
  # in the delayed YAML.
@@ -19,7 +21,7 @@ module Sidekiq
19
21
  end
20
22
  end
21
23
 
22
- require 'sidekiq/extensions/class_methods'
24
+ require "sidekiq/extensions/class_methods"
23
25
  Module.__send__(:include, Sidekiq::Extensions::Klass)
24
26
  end
25
27
 
@@ -27,7 +29,7 @@ module Sidekiq
27
29
  def resolve_class(klass_name)
28
30
  return nil if !klass_name || klass_name.empty?
29
31
  # constantize
30
- names = klass_name.split('::')
32
+ names = klass_name.split("::")
31
33
  names.shift if names.empty? || names.first.empty?
32
34
 
33
35
  names.inject(Object) do |constant, name|
@@ -39,4 +41,3 @@ module Sidekiq
39
41
  end
40
42
  end
41
43
  end
42
-
@@ -1,12 +1,14 @@
1
1
  # frozen_string_literal: true
2
- require 'sidekiq/extensions/generic_proxy'
2
+
3
+ require "sidekiq/extensions/generic_proxy"
3
4
 
4
5
  module Sidekiq
5
6
  module Extensions
6
7
  ##
7
- # Adds 'delay', 'delay_for' and `delay_until` methods to ActionMailer to offload arbitrary email
8
- # delivery to Sidekiq. Example:
8
+ # Adds +delay+, +delay_for+ and +delay_until+ methods to ActionMailer to offload arbitrary email
9
+ # delivery to Sidekiq.
9
10
  #
11
+ # @example
10
12
  # UserMailer.delay.send_welcome_email(new_user)
11
13
  # UserMailer.delay_for(5.days).send_welcome_email(new_user)
12
14
  # UserMailer.delay_until(5.days.from_now).send_welcome_email(new_user)
@@ -19,39 +21,28 @@ module Sidekiq
19
21
  # The email method can return nil, which causes ActionMailer to return
20
22
  # an undeliverable empty message.
21
23
  if msg
22
- deliver(msg)
23
- else
24
- raise "#{target.name}##{method_name} returned an undeliverable mail object"
25
- end
26
- end
27
-
28
- private
29
-
30
- def deliver(msg)
31
- if msg.respond_to?(:deliver_now)
32
- # Rails 4.2/5.0
33
24
  msg.deliver_now
34
25
  else
35
- # Rails 3.2/4.0/4.1
36
- msg.deliver
26
+ raise "#{target.name}##{method_name} returned an undeliverable mail object"
37
27
  end
38
28
  end
39
29
  end
40
30
 
41
31
  module ActionMailer
42
- def sidekiq_delay(options={})
32
+ def sidekiq_delay(options = {})
43
33
  Proxy.new(DelayedMailer, self, options)
44
34
  end
45
- def sidekiq_delay_for(interval, options={})
46
- Proxy.new(DelayedMailer, self, options.merge('at' => Time.now.to_f + interval.to_f))
35
+
36
+ def sidekiq_delay_for(interval, options = {})
37
+ Proxy.new(DelayedMailer, self, options.merge("at" => Time.now.to_f + interval.to_f))
47
38
  end
48
- def sidekiq_delay_until(timestamp, options={})
49
- Proxy.new(DelayedMailer, self, options.merge('at' => timestamp.to_f))
39
+
40
+ def sidekiq_delay_until(timestamp, options = {})
41
+ Proxy.new(DelayedMailer, self, options.merge("at" => timestamp.to_f))
50
42
  end
51
43
  alias_method :delay, :sidekiq_delay
52
44
  alias_method :delay_for, :sidekiq_delay_for
53
45
  alias_method :delay_until, :sidekiq_delay_until
54
46
  end
55
-
56
47
  end
57
48
  end
@@ -1,13 +1,15 @@
1
1
  # frozen_string_literal: true
2
- require 'sidekiq/extensions/generic_proxy'
2
+
3
+ require "sidekiq/extensions/generic_proxy"
3
4
 
4
5
  module Sidekiq
5
6
  module Extensions
6
7
  ##
7
- # Adds 'delay', 'delay_for' and `delay_until` methods to ActiveRecord to offload instance method
8
- # execution to Sidekiq. Examples:
8
+ # Adds +delay+, +delay_for+ and +delay_until+ methods to ActiveRecord to offload instance method
9
+ # execution to Sidekiq.
9
10
  #
10
- # User.recent_signups.each { |user| user.delay.mark_as_awesome }
11
+ # @example
12
+ # User.recent_signups.each { |user| user.delay.mark_as_awesome }
11
13
  #
12
14
  # Please note, this is not recommended as this will serialize the entire
13
15
  # object to Redis. Your Sidekiq jobs should pass IDs, not entire instances.
@@ -22,19 +24,20 @@ module Sidekiq
22
24
  end
23
25
 
24
26
  module ActiveRecord
25
- def sidekiq_delay(options={})
27
+ def sidekiq_delay(options = {})
26
28
  Proxy.new(DelayedModel, self, options)
27
29
  end
28
- def sidekiq_delay_for(interval, options={})
29
- Proxy.new(DelayedModel, self, options.merge('at' => Time.now.to_f + interval.to_f))
30
+
31
+ def sidekiq_delay_for(interval, options = {})
32
+ Proxy.new(DelayedModel, self, options.merge("at" => Time.now.to_f + interval.to_f))
30
33
  end
31
- def sidekiq_delay_until(timestamp, options={})
32
- Proxy.new(DelayedModel, self, options.merge('at' => timestamp.to_f))
34
+
35
+ def sidekiq_delay_until(timestamp, options = {})
36
+ Proxy.new(DelayedModel, self, options.merge("at" => timestamp.to_f))
33
37
  end
34
38
  alias_method :delay, :sidekiq_delay
35
39
  alias_method :delay_for, :sidekiq_delay_for
36
40
  alias_method :delay_until, :sidekiq_delay_until
37
41
  end
38
-
39
42
  end
40
43
  end
@@ -1,14 +1,16 @@
1
1
  # frozen_string_literal: true
2
- require 'sidekiq/extensions/generic_proxy'
2
+
3
+ require "sidekiq/extensions/generic_proxy"
3
4
 
4
5
  module Sidekiq
5
6
  module Extensions
6
7
  ##
7
- # Adds 'delay', 'delay_for' and `delay_until` methods to all Classes to offload class method
8
- # execution to Sidekiq. Examples:
8
+ # Adds `delay`, `delay_for` and `delay_until` methods to all Classes to offload class method
9
+ # execution to Sidekiq.
9
10
  #
10
- # User.delay.delete_inactive
11
- # Wikipedia.delay.download_changes_for(Date.today)
11
+ # @example
12
+ # User.delay.delete_inactive
13
+ # Wikipedia.delay.download_changes_for(Date.today)
12
14
  #
13
15
  class DelayedClass
14
16
  include Sidekiq::Worker
@@ -20,20 +22,21 @@ module Sidekiq
20
22
  end
21
23
 
22
24
  module Klass
23
- def sidekiq_delay(options={})
25
+ def sidekiq_delay(options = {})
24
26
  Proxy.new(DelayedClass, self, options)
25
27
  end
26
- def sidekiq_delay_for(interval, options={})
27
- Proxy.new(DelayedClass, self, options.merge('at' => Time.now.to_f + interval.to_f))
28
+
29
+ def sidekiq_delay_for(interval, options = {})
30
+ Proxy.new(DelayedClass, self, options.merge("at" => Time.now.to_f + interval.to_f))
28
31
  end
29
- def sidekiq_delay_until(timestamp, options={})
30
- Proxy.new(DelayedClass, self, options.merge('at' => timestamp.to_f))
32
+
33
+ def sidekiq_delay_until(timestamp, options = {})
34
+ Proxy.new(DelayedClass, self, options.merge("at" => timestamp.to_f))
31
35
  end
32
36
  alias_method :delay, :sidekiq_delay
33
37
  alias_method :delay_for, :sidekiq_delay_for
34
38
  alias_method :delay_until, :sidekiq_delay_until
35
39
  end
36
-
37
40
  end
38
41
  end
39
42
 
@@ -1,15 +1,16 @@
1
1
  # frozen_string_literal: true
2
- require 'yaml'
2
+
3
+ require "yaml"
3
4
 
4
5
  module Sidekiq
5
6
  module Extensions
6
7
  SIZE_LIMIT = 8_192
7
8
 
8
9
  class Proxy < BasicObject
9
- def initialize(performable, target, options={})
10
+ def initialize(performable, target, options = {})
10
11
  @performable = performable
11
12
  @target = target
12
- @opts = options
13
+ @opts = options.transform_keys(&:to_s)
13
14
  end
14
15
 
15
16
  def method_missing(name, *args)
@@ -23,9 +24,10 @@ module Sidekiq
23
24
  if marshalled.size > SIZE_LIMIT
24
25
  ::Sidekiq.logger.warn { "#{@target}.#{name} job argument is #{marshalled.bytesize} bytes, you should refactor it to reduce the size" }
25
26
  end
26
- @performable.client_push({ 'class' => @performable, 'args' => [marshalled] }.merge(@opts))
27
+ @performable.client_push({"class" => @performable,
28
+ "args" => [marshalled],
29
+ "display_class" => "#{@target}.#{name}"}.merge(@opts))
27
30
  end
28
31
  end
29
-
30
32
  end
31
33
  end