sidekiq 5.2.10 → 7.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (149) hide show
  1. checksums.yaml +4 -4
  2. data/Changes.md +540 -8
  3. data/LICENSE.txt +9 -0
  4. data/README.md +47 -50
  5. data/bin/sidekiq +22 -3
  6. data/bin/sidekiqload +213 -115
  7. data/bin/sidekiqmon +11 -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 +557 -354
  13. data/lib/sidekiq/capsule.rb +127 -0
  14. data/lib/sidekiq/cli.rb +204 -226
  15. data/lib/sidekiq/client.rb +124 -101
  16. data/lib/sidekiq/component.rb +68 -0
  17. data/lib/sidekiq/config.rb +278 -0
  18. data/lib/sidekiq/deploy.rb +62 -0
  19. data/lib/sidekiq/embedded.rb +61 -0
  20. data/lib/sidekiq/fetch.rb +49 -42
  21. data/lib/sidekiq/job.rb +374 -0
  22. data/lib/sidekiq/job_logger.rb +33 -7
  23. data/lib/sidekiq/job_retry.rb +145 -107
  24. data/lib/sidekiq/job_util.rb +107 -0
  25. data/lib/sidekiq/launcher.rb +203 -105
  26. data/lib/sidekiq/logger.rb +131 -0
  27. data/lib/sidekiq/manager.rb +43 -46
  28. data/lib/sidekiq/metrics/query.rb +153 -0
  29. data/lib/sidekiq/metrics/shared.rb +95 -0
  30. data/lib/sidekiq/metrics/tracking.rb +136 -0
  31. data/lib/sidekiq/middleware/chain.rb +113 -56
  32. data/lib/sidekiq/middleware/current_attributes.rb +95 -0
  33. data/lib/sidekiq/middleware/i18n.rb +7 -7
  34. data/lib/sidekiq/middleware/modules.rb +21 -0
  35. data/lib/sidekiq/monitor.rb +146 -0
  36. data/lib/sidekiq/paginator.rb +28 -16
  37. data/lib/sidekiq/processor.rb +122 -120
  38. data/lib/sidekiq/rails.rb +53 -38
  39. data/lib/sidekiq/redis_client_adapter.rb +96 -0
  40. data/lib/sidekiq/redis_connection.rb +38 -107
  41. data/lib/sidekiq/ring_buffer.rb +29 -0
  42. data/lib/sidekiq/scheduled.rb +111 -49
  43. data/lib/sidekiq/sd_notify.rb +149 -0
  44. data/lib/sidekiq/systemd.rb +24 -0
  45. data/lib/sidekiq/testing/inline.rb +6 -5
  46. data/lib/sidekiq/testing.rb +66 -84
  47. data/lib/sidekiq/transaction_aware_client.rb +44 -0
  48. data/lib/sidekiq/version.rb +3 -1
  49. data/lib/sidekiq/web/action.rb +15 -11
  50. data/lib/sidekiq/web/application.rb +119 -78
  51. data/lib/sidekiq/web/csrf_protection.rb +180 -0
  52. data/lib/sidekiq/web/helpers.rb +137 -106
  53. data/lib/sidekiq/web/router.rb +23 -19
  54. data/lib/sidekiq/web.rb +68 -107
  55. data/lib/sidekiq/worker_compatibility_alias.rb +13 -0
  56. data/lib/sidekiq.rb +92 -182
  57. data/sidekiq.gemspec +25 -16
  58. data/web/assets/images/apple-touch-icon.png +0 -0
  59. data/web/assets/javascripts/application.js +131 -61
  60. data/web/assets/javascripts/base-charts.js +106 -0
  61. data/web/assets/javascripts/chart.min.js +13 -0
  62. data/web/assets/javascripts/chartjs-plugin-annotation.min.js +7 -0
  63. data/web/assets/javascripts/dashboard-charts.js +168 -0
  64. data/web/assets/javascripts/dashboard.js +36 -292
  65. data/web/assets/javascripts/metrics.js +264 -0
  66. data/web/assets/stylesheets/application-dark.css +147 -0
  67. data/web/assets/stylesheets/application-rtl.css +2 -95
  68. data/web/assets/stylesheets/application.css +102 -522
  69. data/web/locales/ar.yml +71 -65
  70. data/web/locales/cs.yml +62 -62
  71. data/web/locales/da.yml +60 -53
  72. data/web/locales/de.yml +65 -53
  73. data/web/locales/el.yml +43 -24
  74. data/web/locales/en.yml +84 -66
  75. data/web/locales/es.yml +70 -54
  76. data/web/locales/fa.yml +65 -65
  77. data/web/locales/fr.yml +83 -62
  78. data/web/locales/gd.yml +99 -0
  79. data/web/locales/he.yml +65 -64
  80. data/web/locales/hi.yml +59 -59
  81. data/web/locales/it.yml +53 -53
  82. data/web/locales/ja.yml +75 -64
  83. data/web/locales/ko.yml +52 -52
  84. data/web/locales/lt.yml +83 -0
  85. data/web/locales/nb.yml +61 -61
  86. data/web/locales/nl.yml +52 -52
  87. data/web/locales/pl.yml +45 -45
  88. data/web/locales/pt-br.yml +83 -55
  89. data/web/locales/pt.yml +51 -51
  90. data/web/locales/ru.yml +68 -63
  91. data/web/locales/sv.yml +53 -53
  92. data/web/locales/ta.yml +60 -60
  93. data/web/locales/uk.yml +62 -61
  94. data/web/locales/ur.yml +64 -64
  95. data/web/locales/vi.yml +83 -0
  96. data/web/locales/zh-cn.yml +43 -16
  97. data/web/locales/zh-tw.yml +42 -8
  98. data/web/views/_footer.erb +6 -3
  99. data/web/views/_job_info.erb +21 -4
  100. data/web/views/_metrics_period_select.erb +12 -0
  101. data/web/views/_nav.erb +1 -1
  102. data/web/views/_paging.erb +2 -0
  103. data/web/views/_poll_link.erb +3 -6
  104. data/web/views/_summary.erb +7 -7
  105. data/web/views/busy.erb +75 -25
  106. data/web/views/dashboard.erb +58 -18
  107. data/web/views/dead.erb +3 -3
  108. data/web/views/layout.erb +3 -1
  109. data/web/views/metrics.erb +82 -0
  110. data/web/views/metrics_for_job.erb +68 -0
  111. data/web/views/morgue.erb +14 -15
  112. data/web/views/queue.erb +33 -24
  113. data/web/views/queues.erb +13 -3
  114. data/web/views/retries.erb +16 -17
  115. data/web/views/retry.erb +3 -3
  116. data/web/views/scheduled.erb +17 -15
  117. metadata +70 -71
  118. data/.circleci/config.yml +0 -61
  119. data/.github/contributing.md +0 -32
  120. data/.github/issue_template.md +0 -11
  121. data/.gitignore +0 -15
  122. data/.travis.yml +0 -11
  123. data/3.0-Upgrade.md +0 -70
  124. data/4.0-Upgrade.md +0 -53
  125. data/5.0-Upgrade.md +0 -56
  126. data/COMM-LICENSE +0 -97
  127. data/Ent-Changes.md +0 -238
  128. data/Gemfile +0 -19
  129. data/LICENSE +0 -9
  130. data/Pro-2.0-Upgrade.md +0 -138
  131. data/Pro-3.0-Upgrade.md +0 -44
  132. data/Pro-4.0-Upgrade.md +0 -35
  133. data/Pro-Changes.md +0 -759
  134. data/Rakefile +0 -9
  135. data/bin/sidekiqctl +0 -20
  136. data/code_of_conduct.md +0 -50
  137. data/lib/generators/sidekiq/worker_generator.rb +0 -49
  138. data/lib/sidekiq/core_ext.rb +0 -1
  139. data/lib/sidekiq/ctl.rb +0 -221
  140. data/lib/sidekiq/delay.rb +0 -42
  141. data/lib/sidekiq/exception_handler.rb +0 -29
  142. data/lib/sidekiq/extensions/action_mailer.rb +0 -57
  143. data/lib/sidekiq/extensions/active_record.rb +0 -40
  144. data/lib/sidekiq/extensions/class_methods.rb +0 -40
  145. data/lib/sidekiq/extensions/generic_proxy.rb +0 -31
  146. data/lib/sidekiq/logging.rb +0 -122
  147. data/lib/sidekiq/middleware/server/active_record.rb +0 -23
  148. data/lib/sidekiq/util.rb +0 -66
  149. data/lib/sidekiq/worker.rb +0 -220
@@ -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,13 @@ 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
- @chain ||= Sidekiq.client_middleware
22
- if block_given?
24
+ if block
23
25
  @chain = @chain.dup
24
26
  yield @chain
25
27
  end
@@ -28,34 +30,49 @@ module Sidekiq
28
30
 
29
31
  attr_accessor :redis_pool
30
32
 
31
- # Sidekiq::Client normally uses the default Redis pool but you may
32
- # pass a custom ConnectionPool if you want to shard your
33
- # Sidekiq jobs across several Redis instances (for scalability
34
- # reasons, e.g.)
33
+ # Sidekiq::Client is responsible for pushing job payloads to Redis.
34
+ # Requires the :pool or :config keyword argument.
35
35
  #
36
- # Sidekiq::Client.new(ConnectionPool.new { Redis.new })
36
+ # Sidekiq::Client.new(pool: Sidekiq::RedisConnection.create)
37
37
  #
38
- # Generally this is only needed for very large Sidekiq installs processing
39
- # thousands of jobs per second. I don't recommend sharding unless you
40
- # cannot scale any other way (e.g. splitting your app into smaller apps).
41
- def initialize(redis_pool=nil)
42
- @redis_pool = redis_pool || Thread.current[:sidekiq_via_pool] || Sidekiq.redis_pool
38
+ # Inside the Sidekiq process, you can reuse the configured resources:
39
+ #
40
+ # Sidekiq::Client.new(config: config)
41
+ #
42
+ # @param pool [ConnectionPool] explicit Redis pool to use
43
+ # @param config [Sidekiq::Config] use the pool and middleware from the given Sidekiq container
44
+ # @param chain [Sidekiq::Middleware::Chain] use the given middleware chain
45
+ def initialize(*args, **kwargs)
46
+ if args.size == 1 && kwargs.size == 0
47
+ warn "Sidekiq::Client.new(pool) is deprecated, please use Sidekiq::Client.new(pool: pool), #{caller(0..3)}"
48
+ # old calling method, accept 1 pool argument
49
+ @redis_pool = args[0]
50
+ @chain = Sidekiq.default_configuration.client_middleware
51
+ @config = Sidekiq.default_configuration
52
+ else
53
+ # new calling method: keyword arguments
54
+ @config = kwargs[:config] || Sidekiq.default_configuration
55
+ @redis_pool = kwargs[:pool] || Thread.current[:sidekiq_redis_pool] || @config&.redis_pool
56
+ @chain = kwargs[:chain] || @config&.client_middleware
57
+ raise ArgumentError, "No Redis pool available for Sidekiq::Client" unless @redis_pool
58
+ end
43
59
  end
44
60
 
45
61
  ##
46
62
  # The main method used to push a job to Redis. Accepts a number of options:
47
63
  #
48
64
  # queue - the named queue to use, default 'default'
49
- # class - the worker class to call, required
65
+ # class - the job class to call, required
50
66
  # args - an array of simple arguments to the perform method, must be JSON-serializable
51
67
  # at - timestamp to schedule the job (optional), must be Numeric (e.g. Time.now.to_f)
52
68
  # retry - whether to retry this job if it fails, default true or an integer number of retries
69
+ # retry_for - relative amount of time to retry this job if it fails, default nil
53
70
  # backtrace - whether to save any error backtrace, default false
54
71
  #
55
72
  # 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.
73
+ # job options. Otherwise, they will be based on the job class's options.
57
74
  #
58
- # Any options valid for a worker class's sidekiq_options are also available here.
75
+ # Any options valid for a job class's sidekiq_options are also available here.
59
76
  #
60
77
  # All options must be strings, not symbols. NB: because we are serializing to JSON, all
61
78
  # symbols in 'args' will be converted to strings. Note that +backtrace: true+ can take quite a bit of
@@ -64,22 +81,25 @@ module Sidekiq
64
81
  # Returns a unique Job ID. If middleware stops the job, nil will be returned instead.
65
82
  #
66
83
  # Example:
67
- # push('queue' => 'my_queue', 'class' => MyWorker, 'args' => ['foo', 1, :bat => 'bar'])
84
+ # push('queue' => 'my_queue', 'class' => MyJob, 'args' => ['foo', 1, :bat => 'bar'])
68
85
  #
69
86
  def push(item)
70
87
  normed = normalize_item(item)
71
- payload = process_single(item['class'], normed)
72
-
88
+ payload = middleware.invoke(item["class"], normed, normed["queue"], @redis_pool) do
89
+ normed
90
+ end
73
91
  if payload
92
+ verify_json(payload)
74
93
  raw_push([payload])
75
- payload['jid']
94
+ payload["jid"]
76
95
  end
77
96
  end
78
97
 
79
98
  ##
80
99
  # Push a large number of jobs to Redis. This method cuts out the redis
81
- # network round trip latency. I wouldn't recommend pushing more than
82
- # 1000 per call but YMMV based on network quality, size of job args, etc.
100
+ # network round trip latency. It pushes jobs in batches if more than
101
+ # `:batch_size` (1000 by default) of jobs are passed. I wouldn't recommend making `:batch_size`
102
+ # larger than 1000 but YMMV based on network quality, size of job args, etc.
83
103
  # A large number of jobs can cause a bit of Redis command processing latency.
84
104
  #
85
105
  # Takes the same arguments as #push except that args is expected to be
@@ -87,22 +107,43 @@ module Sidekiq
87
107
  # is run through the client middleware pipeline and each job gets its own Job ID
88
108
  # as normal.
89
109
  #
90
- # Returns an array of the of pushed jobs' jids. The number of jobs pushed can be less
91
- # than the number given if the middleware stopped processing for one or more jobs.
110
+ # Returns an array of the of pushed jobs' jids, may contain nils if any client middleware
111
+ # prevented a job push.
112
+ #
113
+ # Example (pushing jobs in batches):
114
+ # push_bulk('class' => 'MyJob', 'args' => (1..100_000).to_a, batch_size: 1_000)
115
+ #
92
116
  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)
117
+ batch_size = items.delete(:batch_size) || items.delete("batch_size") || 1_000
118
+ args = items["args"]
119
+ at = items.delete("at")
120
+ 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) })
121
+ raise ArgumentError, "Job 'at' Array must have same size as 'args' Array" if at.is_a?(Array) && at.size != args.size
122
+
123
+ jid = items.delete("jid")
124
+ raise ArgumentError, "Explicitly passing 'jid' when pushing more than one job is not supported" if jid && args.size > 1
96
125
 
97
126
  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'] }
127
+ result = args.each_slice(batch_size).flat_map do |slice|
128
+ raise ArgumentError, "Bulk arguments must be an Array of Arrays: [[1], [2]]" unless slice.is_a?(Array) && slice.all?(Array)
129
+ break [] if slice.empty? # no jobs to push
130
+
131
+ payloads = slice.map.with_index { |job_args, index|
132
+ copy = normed.merge("args" => job_args, "jid" => SecureRandom.hex(12))
133
+ copy["at"] = (at.is_a?(Array) ? at[index] : at) if at
134
+ result = middleware.invoke(items["class"], copy, copy["queue"], @redis_pool) do
135
+ verify_json(copy)
136
+ copy
137
+ end
138
+ result || nil
139
+ }
140
+
141
+ to_push = payloads.compact
142
+ raw_push(to_push) unless to_push.empty?
143
+ payloads.map { |payload| payload&.[]("jid") }
144
+ end
145
+
146
+ result.is_a?(Enumerator::Lazy) ? result.force : result
106
147
  end
107
148
 
108
149
  # Allows sharding of jobs across any number of Redis instances. All jobs
@@ -110,8 +151,8 @@ module Sidekiq
110
151
  #
111
152
  # pool = ConnectionPool.new { Redis.new }
112
153
  # Sidekiq::Client.via(pool) do
113
- # SomeWorker.perform_async(1,2,3)
114
- # SomeOtherWorker.perform_async(1,2,3)
154
+ # SomeJob.perform_async(1,2,3)
155
+ # SomeOtherJob.perform_async(1,2,3)
115
156
  # end
116
157
  #
117
158
  # Generally this is only needed for very large Sidekiq installs processing
@@ -119,58 +160,57 @@ module Sidekiq
119
160
  # you cannot scale any other way (e.g. splitting your app into smaller apps).
120
161
  def self.via(pool)
121
162
  raise ArgumentError, "No pool given" if pool.nil?
122
- current_sidekiq_pool = Thread.current[:sidekiq_via_pool]
123
- Thread.current[:sidekiq_via_pool] = pool
163
+ current_sidekiq_pool = Thread.current[:sidekiq_redis_pool]
164
+ Thread.current[:sidekiq_redis_pool] = pool
124
165
  yield
125
166
  ensure
126
- Thread.current[:sidekiq_via_pool] = current_sidekiq_pool
167
+ Thread.current[:sidekiq_redis_pool] = current_sidekiq_pool
127
168
  end
128
169
 
129
170
  class << self
130
-
131
171
  def push(item)
132
172
  new.push(item)
133
173
  end
134
174
 
135
- def push_bulk(items)
136
- new.push_bulk(items)
175
+ def push_bulk(...)
176
+ new.push_bulk(...)
137
177
  end
138
178
 
139
179
  # Resque compatibility helpers. Note all helpers
140
- # should go through Worker#client_push.
180
+ # should go through Sidekiq::Job#client_push.
141
181
  #
142
182
  # Example usage:
143
- # Sidekiq::Client.enqueue(MyWorker, 'foo', 1, :bat => 'bar')
183
+ # Sidekiq::Client.enqueue(MyJob, 'foo', 1, :bat => 'bar')
144
184
  #
145
185
  # Messages are enqueued to the 'default' queue.
146
186
  #
147
187
  def enqueue(klass, *args)
148
- klass.client_push('class' => klass, 'args' => args)
188
+ klass.client_push("class" => klass, "args" => args)
149
189
  end
150
190
 
151
191
  # Example usage:
152
- # Sidekiq::Client.enqueue_to(:queue_name, MyWorker, 'foo', 1, :bat => 'bar')
192
+ # Sidekiq::Client.enqueue_to(:queue_name, MyJob, 'foo', 1, :bat => 'bar')
153
193
  #
154
194
  def enqueue_to(queue, klass, *args)
155
- klass.client_push('queue' => queue, 'class' => klass, 'args' => args)
195
+ klass.client_push("queue" => queue, "class" => klass, "args" => args)
156
196
  end
157
197
 
158
198
  # Example usage:
159
- # Sidekiq::Client.enqueue_to_in(:queue_name, 3.minutes, MyWorker, 'foo', 1, :bat => 'bar')
199
+ # Sidekiq::Client.enqueue_to_in(:queue_name, 3.minutes, MyJob, 'foo', 1, :bat => 'bar')
160
200
  #
161
201
  def enqueue_to_in(queue, interval, klass, *args)
162
202
  int = interval.to_f
163
203
  now = Time.now.to_f
164
- ts = (int < 1_000_000_000 ? now + int : int)
204
+ ts = ((int < 1_000_000_000) ? now + int : int)
165
205
 
166
- item = { 'class' => klass, 'args' => args, 'at' => ts, 'queue' => queue }
167
- item.delete('at') if ts <= now
206
+ item = {"class" => klass, "args" => args, "at" => ts, "queue" => queue}
207
+ item.delete("at") if ts <= now
168
208
 
169
209
  klass.client_push(item)
170
210
  end
171
211
 
172
212
  # Example usage:
173
- # Sidekiq::Client.enqueue_in(3.minutes, MyWorker, 'foo', 1, :bat => 'bar')
213
+ # Sidekiq::Client.enqueue_in(3.minutes, MyJob, 'foo', 1, :bat => 'bar')
174
214
  #
175
215
  def enqueue_in(interval, klass, *args)
176
216
  klass.perform_in(interval, *args)
@@ -181,62 +221,45 @@ module Sidekiq
181
221
 
182
222
  def raw_push(payloads)
183
223
  @redis_pool.with do |conn|
184
- conn.multi do
185
- atomic_push(conn, payloads)
224
+ retryable = true
225
+ begin
226
+ conn.pipelined do |pipeline|
227
+ atomic_push(pipeline, payloads)
228
+ end
229
+ rescue RedisClient::Error => ex
230
+ # 2550 Failover can cause the server to become a replica, need
231
+ # to disconnect and reopen the socket to get back to the primary.
232
+ # 4495 Use the same logic if we have a "Not enough replicas" error from the primary
233
+ # 4985 Use the same logic when a blocking command is force-unblocked
234
+ # The retry logic is copied from sidekiq.rb
235
+ if retryable && ex.message =~ /READONLY|NOREPLICAS|UNBLOCKED/
236
+ conn.close
237
+ retryable = false
238
+ retry
239
+ end
240
+ raise
186
241
  end
187
242
  end
188
243
  true
189
244
  end
190
245
 
191
246
  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
247
+ if payloads.first.key?("at")
248
+ conn.zadd("schedule", payloads.flat_map { |hash|
249
+ at = hash.delete("at").to_s
250
+ # ActiveJob sets this but the job has not been enqueued yet
251
+ hash.delete("enqueued_at")
195
252
  [at, Sidekiq.dump_json(hash)]
196
- end)
253
+ })
197
254
  else
198
- q = payloads.first['queue']
255
+ queue = payloads.first["queue"]
199
256
  now = Time.now.to_f
200
- to_push = payloads.map do |entry|
201
- entry['enqueued_at'] = now
257
+ to_push = payloads.map { |entry|
258
+ entry["enqueued_at"] = now
202
259
  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
260
+ }
261
+ conn.sadd("queues", [queue])
262
+ conn.lpush("queue:#{queue}", to_push)
240
263
  end
241
264
  end
242
265
  end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sidekiq
4
+ ##
5
+ # Sidekiq::Component assumes a config instance is available at @config
6
+ module Component # :nodoc:
7
+ attr_reader :config
8
+
9
+ def watchdog(last_words)
10
+ yield
11
+ rescue Exception => ex
12
+ handle_exception(ex, {context: last_words})
13
+ raise ex
14
+ end
15
+
16
+ def safe_thread(name, &block)
17
+ Thread.new do
18
+ Thread.current.name = "sidekiq.#{name}"
19
+ watchdog(name, &block)
20
+ end
21
+ end
22
+
23
+ def logger
24
+ config.logger
25
+ end
26
+
27
+ def redis(&block)
28
+ config.redis(&block)
29
+ end
30
+
31
+ def tid
32
+ Thread.current["sidekiq_tid"] ||= (Thread.current.object_id ^ ::Process.pid).to_s(36)
33
+ end
34
+
35
+ def hostname
36
+ ENV["DYNO"] || Socket.gethostname
37
+ end
38
+
39
+ def process_nonce
40
+ @@process_nonce ||= SecureRandom.hex(6)
41
+ end
42
+
43
+ def identity
44
+ @@identity ||= "#{hostname}:#{::Process.pid}:#{process_nonce}"
45
+ end
46
+
47
+ def handle_exception(ex, ctx = {})
48
+ config.handle_exception(ex, ctx)
49
+ end
50
+
51
+ def fire_event(event, options = {})
52
+ oneshot = options.fetch(:oneshot, true)
53
+ reverse = options[:reverse]
54
+ reraise = options[:reraise]
55
+ logger.debug("Firing #{event} event") if oneshot
56
+
57
+ arr = config[:lifecycle_events][event]
58
+ arr.reverse! if reverse
59
+ arr.each do |block|
60
+ block.call
61
+ rescue => ex
62
+ handle_exception(ex, {context: "Exception during Sidekiq lifecycle event.", event: event})
63
+ raise ex if reraise
64
+ end
65
+ arr.clear if oneshot # once we've fired an event, we never fire it again
66
+ end
67
+ end
68
+ end