sidekiq 5.2.9 → 6.4.1

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.

Files changed (106) hide show
  1. checksums.yaml +4 -4
  2. data/Changes.md +318 -1
  3. data/LICENSE +3 -3
  4. data/README.md +23 -34
  5. data/bin/sidekiq +27 -3
  6. data/bin/sidekiqload +67 -61
  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 +335 -267
  13. data/lib/sidekiq/cli.rb +164 -182
  14. data/lib/sidekiq/client.rb +58 -61
  15. data/lib/sidekiq/delay.rb +7 -6
  16. data/lib/sidekiq/exception_handler.rb +10 -12
  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 +6 -4
  21. data/lib/sidekiq/fetch.rb +40 -32
  22. data/lib/sidekiq/job.rb +13 -0
  23. data/lib/sidekiq/job_logger.rb +33 -7
  24. data/lib/sidekiq/job_retry.rb +70 -71
  25. data/lib/sidekiq/job_util.rb +65 -0
  26. data/lib/sidekiq/launcher.rb +161 -71
  27. data/lib/sidekiq/logger.rb +170 -0
  28. data/lib/sidekiq/manager.rb +17 -21
  29. data/lib/sidekiq/middleware/chain.rb +20 -8
  30. data/lib/sidekiq/middleware/current_attributes.rb +57 -0
  31. data/lib/sidekiq/middleware/i18n.rb +5 -7
  32. data/lib/sidekiq/monitor.rb +133 -0
  33. data/lib/sidekiq/paginator.rb +20 -16
  34. data/lib/sidekiq/processor.rb +71 -70
  35. data/lib/sidekiq/rails.rb +40 -37
  36. data/lib/sidekiq/redis_connection.rb +48 -48
  37. data/lib/sidekiq/scheduled.rb +62 -28
  38. data/lib/sidekiq/sd_notify.rb +149 -0
  39. data/lib/sidekiq/systemd.rb +24 -0
  40. data/lib/sidekiq/testing/inline.rb +2 -1
  41. data/lib/sidekiq/testing.rb +36 -27
  42. data/lib/sidekiq/util.rb +57 -15
  43. data/lib/sidekiq/version.rb +2 -1
  44. data/lib/sidekiq/web/action.rb +15 -11
  45. data/lib/sidekiq/web/application.rb +88 -75
  46. data/lib/sidekiq/web/csrf_protection.rb +180 -0
  47. data/lib/sidekiq/web/helpers.rb +109 -92
  48. data/lib/sidekiq/web/router.rb +23 -19
  49. data/lib/sidekiq/web.rb +61 -105
  50. data/lib/sidekiq/worker.rb +247 -105
  51. data/lib/sidekiq.rb +77 -44
  52. data/sidekiq.gemspec +23 -16
  53. data/web/assets/images/apple-touch-icon.png +0 -0
  54. data/web/assets/javascripts/application.js +83 -64
  55. data/web/assets/javascripts/dashboard.js +54 -73
  56. data/web/assets/stylesheets/application-dark.css +143 -0
  57. data/web/assets/stylesheets/application-rtl.css +0 -4
  58. data/web/assets/stylesheets/application.css +45 -232
  59. data/web/locales/ar.yml +8 -2
  60. data/web/locales/de.yml +14 -2
  61. data/web/locales/en.yml +6 -1
  62. data/web/locales/es.yml +18 -2
  63. data/web/locales/fr.yml +10 -3
  64. data/web/locales/ja.yml +7 -1
  65. data/web/locales/lt.yml +83 -0
  66. data/web/locales/pl.yml +4 -4
  67. data/web/locales/ru.yml +4 -0
  68. data/web/locales/vi.yml +83 -0
  69. data/web/views/_footer.erb +1 -1
  70. data/web/views/_job_info.erb +3 -2
  71. data/web/views/_poll_link.erb +2 -5
  72. data/web/views/_summary.erb +7 -7
  73. data/web/views/busy.erb +54 -20
  74. data/web/views/dashboard.erb +22 -14
  75. data/web/views/dead.erb +3 -3
  76. data/web/views/layout.erb +3 -1
  77. data/web/views/morgue.erb +9 -6
  78. data/web/views/queue.erb +19 -10
  79. data/web/views/queues.erb +10 -2
  80. data/web/views/retries.erb +11 -8
  81. data/web/views/retry.erb +3 -3
  82. data/web/views/scheduled.erb +5 -2
  83. metadata +34 -64
  84. data/.circleci/config.yml +0 -61
  85. data/.github/contributing.md +0 -32
  86. data/.github/issue_template.md +0 -11
  87. data/.gitignore +0 -15
  88. data/.travis.yml +0 -11
  89. data/3.0-Upgrade.md +0 -70
  90. data/4.0-Upgrade.md +0 -53
  91. data/5.0-Upgrade.md +0 -56
  92. data/COMM-LICENSE +0 -97
  93. data/Ent-Changes.md +0 -238
  94. data/Gemfile +0 -23
  95. data/Pro-2.0-Upgrade.md +0 -138
  96. data/Pro-3.0-Upgrade.md +0 -44
  97. data/Pro-4.0-Upgrade.md +0 -35
  98. data/Pro-Changes.md +0 -759
  99. data/Rakefile +0 -9
  100. data/bin/sidekiqctl +0 -20
  101. data/code_of_conduct.md +0 -50
  102. data/lib/generators/sidekiq/worker_generator.rb +0 -49
  103. data/lib/sidekiq/core_ext.rb +0 -1
  104. data/lib/sidekiq/ctl.rb +0 -221
  105. data/lib/sidekiq/logging.rb +0 -122
  106. data/lib/sidekiq/middleware/server/active_record.rb +0 -23
data/lib/sidekiq/web.rb CHANGED
@@ -1,20 +1,20 @@
1
1
  # frozen_string_literal: true
2
- require 'erb'
3
2
 
4
- require 'sidekiq'
5
- require 'sidekiq/api'
6
- require 'sidekiq/paginator'
7
- require 'sidekiq/web/helpers'
3
+ require "erb"
8
4
 
9
- require 'sidekiq/web/router'
10
- require 'sidekiq/web/action'
11
- require 'sidekiq/web/application'
5
+ require "sidekiq"
6
+ require "sidekiq/api"
7
+ require "sidekiq/paginator"
8
+ require "sidekiq/web/helpers"
12
9
 
13
- require 'rack/protection'
10
+ require "sidekiq/web/router"
11
+ require "sidekiq/web/action"
12
+ require "sidekiq/web/application"
13
+ require "sidekiq/web/csrf_protection"
14
14
 
15
- require 'rack/builder'
16
- require 'rack/file'
17
- require 'rack/session/cookie'
15
+ require "rack/content_length"
16
+ require "rack/builder"
17
+ require "rack/static"
18
18
 
19
19
  module Sidekiq
20
20
  class Web
@@ -25,12 +25,12 @@ module Sidekiq
25
25
  ASSETS = "#{ROOT}/assets"
26
26
 
27
27
  DEFAULT_TABS = {
28
- "Dashboard" => '',
29
- "Busy" => 'busy',
30
- "Queues" => 'queues',
31
- "Retries" => 'retries',
32
- "Scheduled" => 'scheduled',
33
- "Dead" => 'morgue',
28
+ "Dashboard" => "",
29
+ "Busy" => "busy",
30
+ "Queues" => "queues",
31
+ "Retries" => "retries",
32
+ "Scheduled" => "scheduled",
33
+ "Dead" => "morgue"
34
34
  }
35
35
 
36
36
  class << self
@@ -38,14 +38,6 @@ module Sidekiq
38
38
  self
39
39
  end
40
40
 
41
- def middlewares
42
- @middlewares ||= []
43
- end
44
-
45
- def use(*middleware_args, &block)
46
- middlewares << [middleware_args, block]
47
- end
48
-
49
41
  def default_tabs
50
42
  DEFAULT_TABS
51
43
  end
@@ -64,39 +56,52 @@ module Sidekiq
64
56
  end
65
57
 
66
58
  def enable(*opts)
67
- opts.each {|key| set(key, true) }
59
+ opts.each { |key| set(key, true) }
68
60
  end
69
61
 
70
62
  def disable(*opts)
71
- opts.each {|key| set(key, false) }
63
+ opts.each { |key| set(key, false) }
64
+ end
65
+
66
+ def middlewares
67
+ @middlewares ||= []
68
+ end
69
+
70
+ def use(*args, &block)
71
+ middlewares << [args, block]
72
72
  end
73
73
 
74
- # Helper for the Sinatra syntax: Sidekiq::Web.set(:session_secret, Rails.application.secrets...)
75
74
  def set(attribute, value)
76
75
  send(:"#{attribute}=", value)
77
76
  end
78
77
 
79
- attr_accessor :app_url, :session_secret, :redis_pool, :sessions
78
+ def sessions=(val)
79
+ puts "WARNING: Sidekiq::Web.sessions= is no longer relevant and will be removed in Sidekiq 7.0. #{caller(1..1).first}"
80
+ end
81
+
82
+ def session_secret=(val)
83
+ puts "WARNING: Sidekiq::Web.session_secret= is no longer relevant and will be removed in Sidekiq 7.0. #{caller(1..1).first}"
84
+ end
85
+
86
+ attr_accessor :app_url, :redis_pool
80
87
  attr_writer :locales, :views
81
88
  end
82
89
 
83
90
  def self.inherited(child)
84
- child.app_url = self.app_url
85
- child.session_secret = self.session_secret
86
- child.redis_pool = self.redis_pool
87
- child.sessions = self.sessions
91
+ child.app_url = app_url
92
+ child.redis_pool = redis_pool
88
93
  end
89
94
 
90
95
  def settings
91
96
  self.class.settings
92
97
  end
93
98
 
94
- def use(*middleware_args, &block)
95
- middlewares << [middleware_args, block]
99
+ def middlewares
100
+ @middlewares ||= self.class.middlewares
96
101
  end
97
102
 
98
- def middlewares
99
- @middlewares ||= Web.middlewares.dup
103
+ def use(*args, &block)
104
+ middlewares << [args, block]
100
105
  end
101
106
 
102
107
  def call(env)
@@ -113,29 +118,19 @@ module Sidekiq
113
118
  end
114
119
 
115
120
  def enable(*opts)
116
- opts.each {|key| set(key, true) }
121
+ opts.each { |key| set(key, true) }
117
122
  end
118
123
 
119
124
  def disable(*opts)
120
- opts.each {|key| set(key, false) }
125
+ opts.each { |key| set(key, false) }
121
126
  end
122
127
 
123
128
  def set(attribute, value)
124
129
  send(:"#{attribute}=", value)
125
130
  end
126
131
 
127
- # Default values
128
- set :sessions, true
129
-
130
- attr_writer :sessions
131
-
132
- def sessions
133
- unless instance_variable_defined?("@sessions")
134
- @sessions = self.class.sessions
135
- @sessions = @sessions.to_hash.dup if @sessions.respond_to?(:to_hash)
136
- end
137
-
138
- @sessions
132
+ def sessions=(val)
133
+ puts "Sidekiq::Web#sessions= is no longer relevant and will be removed in Sidekiq 7.0. #{caller[2..2].first}"
139
134
  end
140
135
 
141
136
  def self.register(extension)
@@ -144,50 +139,20 @@ module Sidekiq
144
139
 
145
140
  private
146
141
 
147
- def using?(middleware)
148
- middlewares.any? do |(m,_)|
149
- m.kind_of?(Array) && (m[0] == middleware || m[0].kind_of?(middleware))
150
- end
151
- end
152
-
153
- def build_sessions
154
- middlewares = self.middlewares
155
-
156
- unless using?(::Rack::Protection) || ENV['RACK_ENV'] == 'test'
157
- middlewares.unshift [[::Rack::Protection, { use: :authenticity_token }], nil]
158
- end
159
-
160
- s = sessions
161
- return unless s
162
-
163
- unless using? ::Rack::Session::Cookie
164
- unless secret = Web.session_secret
165
- require 'securerandom'
166
- secret = SecureRandom.hex(64)
167
- end
168
-
169
- options = { secret: secret }
170
- options = options.merge(s.to_hash) if s.respond_to? :to_hash
171
-
172
- middlewares.unshift [[::Rack::Session::Cookie, options], nil]
173
- end
174
- end
175
-
176
142
  def build
177
- build_sessions
178
-
179
- middlewares = self.middlewares
180
143
  klass = self.class
144
+ m = middlewares
181
145
 
182
- ::Rack::Builder.new do
183
- %w(stylesheets javascripts images).each do |asset_dir|
184
- map "/#{asset_dir}" do
185
- run ::Rack::File.new("#{ASSETS}/#{asset_dir}", { 'Cache-Control' => 'public, max-age=86400' })
186
- end
187
- end
188
-
189
- middlewares.each {|middleware, block| use(*middleware, &block) }
146
+ rules = []
147
+ rules = [[:all, {"Cache-Control" => "public, max-age=86400"}]] unless ENV["SIDEKIQ_WEB_TESTING"]
190
148
 
149
+ ::Rack::Builder.new do
150
+ use Rack::Static, urls: ["/stylesheets", "/images", "/javascripts"],
151
+ root: ASSETS,
152
+ cascade: true,
153
+ header_rules: rules
154
+ m.each { |middleware, block| use(*middleware, &block) }
155
+ use Sidekiq::Web::CsrfProtection unless $TESTING
191
156
  run WebApplication.new(klass)
192
157
  end
193
158
  end
@@ -196,18 +161,9 @@ module Sidekiq
196
161
  Sidekiq::WebApplication.helpers WebHelpers
197
162
  Sidekiq::WebApplication.helpers Sidekiq::Paginator
198
163
 
199
- Sidekiq::WebAction.class_eval "def _render\n#{ERB.new(File.read(Web::LAYOUT)).src}\nend"
200
- end
201
-
202
- if defined?(::ActionDispatch::Request::Session) &&
203
- !::ActionDispatch::Request::Session.method_defined?(:each)
204
- # mperham/sidekiq#2460
205
- # Rack apps can't reuse the Rails session store without
206
- # this monkeypatch, fixed in Rails 5.
207
- class ActionDispatch::Request::Session
208
- def each(&block)
209
- hash = self.to_hash
210
- hash.each(&block)
164
+ Sidekiq::WebAction.class_eval <<-RUBY, __FILE__, __LINE__ + 1
165
+ def _render
166
+ #{ERB.new(File.read(Web::LAYOUT)).src}
211
167
  end
212
- end
168
+ RUBY
213
169
  end
@@ -1,14 +1,15 @@
1
1
  # frozen_string_literal: true
2
- require 'sidekiq/client'
3
2
 
4
- module Sidekiq
3
+ require "sidekiq/client"
5
4
 
5
+ module Sidekiq
6
6
  ##
7
7
  # Include this module in your worker class and you can easily create
8
8
  # asynchronous jobs:
9
9
  #
10
10
  # class HardWorker
11
11
  # include Sidekiq::Worker
12
+ # sidekiq_options queue: 'critical', retry: 5
12
13
  #
13
14
  # def perform(*args)
14
15
  # # do some work
@@ -20,16 +21,145 @@ module Sidekiq
20
21
  # HardWorker.perform_async(1, 2, 3)
21
22
  #
22
23
  # Note that perform_async is a class method, perform is an instance method.
24
+ #
25
+ # Sidekiq::Worker also includes several APIs to provide compatibility with
26
+ # ActiveJob.
27
+ #
28
+ # class SomeWorker
29
+ # include Sidekiq::Worker
30
+ # queue_as :critical
31
+ #
32
+ # def perform(...)
33
+ # end
34
+ # end
35
+ #
36
+ # SomeWorker.set(wait_until: 1.hour).perform_async(123)
37
+ #
38
+ # Note that arguments passed to the job must still obey Sidekiq's
39
+ # best practice for simple, JSON-native data types. Sidekiq will not
40
+ # implement ActiveJob's more complex argument serialization. For
41
+ # this reason, we don't implement `perform_later` as our call semantics
42
+ # are very different.
43
+ #
23
44
  module Worker
45
+ ##
46
+ # The Options module is extracted so we can include it in ActiveJob::Base
47
+ # and allow native AJs to configure Sidekiq features/internals.
48
+ module Options
49
+ def self.included(base)
50
+ base.extend(ClassMethods)
51
+ base.sidekiq_class_attribute :sidekiq_options_hash
52
+ base.sidekiq_class_attribute :sidekiq_retry_in_block
53
+ base.sidekiq_class_attribute :sidekiq_retries_exhausted_block
54
+ end
55
+
56
+ module ClassMethods
57
+ ACCESSOR_MUTEX = Mutex.new
58
+
59
+ ##
60
+ # Allows customization for this type of Worker.
61
+ # Legal options:
62
+ #
63
+ # queue - name of queue to use for this job type, default *default*
64
+ # retry - enable retries for this Worker in case of error during execution,
65
+ # *true* to use the default or *Integer* count
66
+ # backtrace - whether to save any error backtrace in the retry payload to display in web UI,
67
+ # can be true, false or an integer number of lines to save, default *false*
68
+ #
69
+ # In practice, any option is allowed. This is the main mechanism to configure the
70
+ # options for a specific job.
71
+ def sidekiq_options(opts = {})
72
+ opts = opts.transform_keys(&:to_s) # stringify
73
+ self.sidekiq_options_hash = get_sidekiq_options.merge(opts)
74
+ end
75
+
76
+ def sidekiq_retry_in(&block)
77
+ self.sidekiq_retry_in_block = block
78
+ end
79
+
80
+ def sidekiq_retries_exhausted(&block)
81
+ self.sidekiq_retries_exhausted_block = block
82
+ end
83
+
84
+ def get_sidekiq_options # :nodoc:
85
+ self.sidekiq_options_hash ||= Sidekiq.default_worker_options
86
+ end
87
+
88
+ def sidekiq_class_attribute(*attrs)
89
+ instance_reader = true
90
+ instance_writer = true
91
+
92
+ attrs.each do |name|
93
+ synchronized_getter = "__synchronized_#{name}"
94
+
95
+ singleton_class.instance_eval do
96
+ undef_method(name) if method_defined?(name) || private_method_defined?(name)
97
+ end
98
+
99
+ define_singleton_method(synchronized_getter) { nil }
100
+ singleton_class.class_eval do
101
+ private(synchronized_getter)
102
+ end
103
+
104
+ define_singleton_method(name) { ACCESSOR_MUTEX.synchronize { send synchronized_getter } }
105
+
106
+ ivar = "@#{name}"
107
+
108
+ singleton_class.instance_eval do
109
+ m = "#{name}="
110
+ undef_method(m) if method_defined?(m) || private_method_defined?(m)
111
+ end
112
+ define_singleton_method("#{name}=") do |val|
113
+ singleton_class.class_eval do
114
+ ACCESSOR_MUTEX.synchronize do
115
+ undef_method(synchronized_getter) if method_defined?(synchronized_getter) || private_method_defined?(synchronized_getter)
116
+ define_method(synchronized_getter) { val }
117
+ end
118
+ end
119
+
120
+ if singleton_class?
121
+ class_eval do
122
+ undef_method(name) if method_defined?(name) || private_method_defined?(name)
123
+ define_method(name) do
124
+ if instance_variable_defined? ivar
125
+ instance_variable_get ivar
126
+ else
127
+ singleton_class.send name
128
+ end
129
+ end
130
+ end
131
+ end
132
+ val
133
+ end
134
+
135
+ if instance_reader
136
+ undef_method(name) if method_defined?(name) || private_method_defined?(name)
137
+ define_method(name) do
138
+ if instance_variable_defined?(ivar)
139
+ instance_variable_get ivar
140
+ else
141
+ self.class.public_send name
142
+ end
143
+ end
144
+ end
145
+
146
+ if instance_writer
147
+ m = "#{name}="
148
+ undef_method(m) if method_defined?(m) || private_method_defined?(m)
149
+ attr_writer name
150
+ end
151
+ end
152
+ end
153
+ end
154
+ end
155
+
24
156
  attr_accessor :jid
25
157
 
26
158
  def self.included(base)
27
- raise ArgumentError, "You cannot include Sidekiq::Worker in an ActiveJob: #{base.name}" if base.ancestors.any? {|c| c.name == 'ActiveJob::Base' }
159
+ raise ArgumentError, "Sidekiq::Worker cannot be included in an ActiveJob: #{base.name}" if base.ancestors.any? { |c| c.name == "ActiveJob::Base" }
28
160
 
161
+ base.include(Options)
29
162
  base.extend(ClassMethods)
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
33
163
  end
34
164
 
35
165
  def logger
@@ -41,38 +171,100 @@ module Sidekiq
41
171
  # SomeWorker.set(queue: 'foo').perform_async(....)
42
172
  #
43
173
  class Setter
174
+ include Sidekiq::JobUtil
175
+
44
176
  def initialize(klass, opts)
45
177
  @klass = klass
46
178
  @opts = opts
179
+
180
+ # ActiveJob compatibility
181
+ interval = @opts.delete(:wait_until) || @opts.delete(:wait)
182
+ at(interval) if interval
47
183
  end
48
184
 
49
185
  def set(options)
186
+ interval = options.delete(:wait_until) || options.delete(:wait)
50
187
  @opts.merge!(options)
188
+ at(interval) if interval
51
189
  self
52
190
  end
53
191
 
54
192
  def perform_async(*args)
55
- @klass.client_push(@opts.merge('args' => args, 'class' => @klass))
193
+ if @opts["sync"] == true
194
+ perform_inline(*args)
195
+ else
196
+ @klass.client_push(@opts.merge("args" => args, "class" => @klass))
197
+ end
198
+ end
199
+
200
+ # Explicit inline execution of a job. Returns nil if the job did not
201
+ # execute, true otherwise.
202
+ def perform_inline(*args)
203
+ raw = @opts.merge("args" => args, "class" => @klass).transform_keys(&:to_s)
204
+
205
+ # validate and normalize payload
206
+ item = normalize_item(raw)
207
+ queue = item["queue"]
208
+
209
+ # run client-side middleware
210
+ result = Sidekiq.client_middleware.invoke(item["class"], item, queue, Sidekiq.redis_pool) do
211
+ item
212
+ end
213
+ return nil unless result
214
+
215
+ # round-trip the payload via JSON
216
+ msg = Sidekiq.load_json(Sidekiq.dump_json(item))
217
+
218
+ # prepare the job instance
219
+ klass = msg["class"].constantize
220
+ job = klass.new
221
+ job.jid = msg["jid"]
222
+ job.bid = msg["bid"] if job.respond_to?(:bid)
223
+
224
+ # run the job through server-side middleware
225
+ result = Sidekiq.server_middleware.invoke(job, msg, msg["queue"]) do
226
+ # perform it
227
+ job.perform(*msg["args"])
228
+ true
229
+ end
230
+ return nil unless result
231
+ # jobs do not return a result. they should store any
232
+ # modified state.
233
+ true
234
+ end
235
+ alias_method :perform_sync, :perform_inline
236
+
237
+ def perform_bulk(args, batch_size: 1_000)
238
+ hash = @opts.transform_keys(&:to_s)
239
+ pool = Thread.current[:sidekiq_via_pool] || @klass.get_sidekiq_options["pool"] || Sidekiq.redis_pool
240
+ client = Sidekiq::Client.new(pool)
241
+ result = args.each_slice(batch_size).flat_map do |slice|
242
+ client.push_bulk(hash.merge("class" => @klass, "args" => slice))
243
+ end
244
+
245
+ result.is_a?(Enumerator::Lazy) ? result.force : result
56
246
  end
57
247
 
58
248
  # +interval+ must be a timestamp, numeric or something that acts
59
249
  # numeric (like an activesupport time interval).
60
250
  def perform_in(interval, *args)
251
+ at(interval).perform_async(*args)
252
+ end
253
+ alias_method :perform_at, :perform_in
254
+
255
+ private
256
+
257
+ def at(interval)
61
258
  int = interval.to_f
62
259
  now = Time.now.to_f
63
260
  ts = (int < 1_000_000_000 ? now + int : int)
64
-
65
- payload = @opts.merge('class' => @klass, 'args' => args, 'at' => ts)
66
261
  # 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)
262
+ @opts["at"] = ts if ts > now
263
+ self
69
264
  end
70
- alias_method :perform_at, :perform_in
71
265
  end
72
266
 
73
267
  module ClassMethods
74
- ACCESSOR_MUTEX = Mutex.new
75
-
76
268
  def delay(*args)
77
269
  raise ArgumentError, "Do not call .delay on a Sidekiq::Worker class, call .perform_async"
78
270
  end
@@ -85,12 +277,45 @@ module Sidekiq
85
277
  raise ArgumentError, "Do not call .delay_until on a Sidekiq::Worker class, call .perform_at"
86
278
  end
87
279
 
280
+ def queue_as(q)
281
+ sidekiq_options("queue" => q.to_s)
282
+ end
283
+
88
284
  def set(options)
89
285
  Setter.new(self, options)
90
286
  end
91
287
 
92
288
  def perform_async(*args)
93
- client_push('class' => self, 'args' => args)
289
+ Setter.new(self, {}).perform_async(*args)
290
+ end
291
+
292
+ # Inline execution of job's perform method after passing through Sidekiq.client_middleware and Sidekiq.server_middleware
293
+ def perform_inline(*args)
294
+ Setter.new(self, {}).perform_inline(*args)
295
+ end
296
+
297
+ ##
298
+ # Push a large number of jobs to Redis, while limiting the batch of
299
+ # each job payload to 1,000. This method helps cut down on the number
300
+ # of round trips to Redis, which can increase the performance of enqueueing
301
+ # large numbers of jobs.
302
+ #
303
+ # +items+ must be an Array of Arrays.
304
+ #
305
+ # For finer-grained control, use `Sidekiq::Client.push_bulk` directly.
306
+ #
307
+ # Example (3 Redis round trips):
308
+ #
309
+ # SomeWorker.perform_async(1)
310
+ # SomeWorker.perform_async(2)
311
+ # SomeWorker.perform_async(3)
312
+ #
313
+ # Would instead become (1 Redis round trip):
314
+ #
315
+ # SomeWorker.perform_bulk([[1], [2], [3]])
316
+ #
317
+ def perform_bulk(*args, **kwargs)
318
+ Setter.new(self, {}).perform_bulk(*args, **kwargs)
94
319
  end
95
320
 
96
321
  # +interval+ must be a timestamp, numeric or something that acts
@@ -100,10 +325,10 @@ module Sidekiq
100
325
  now = Time.now.to_f
101
326
  ts = (int < 1_000_000_000 ? now + int : int)
102
327
 
103
- item = { 'class' => self, 'args' => args, 'at' => ts }
328
+ item = {"class" => self, "args" => args}
104
329
 
105
330
  # Optimization to enqueue something now that is scheduled to go out now or in the past
106
- item.delete('at') if ts <= now
331
+ item["at"] = ts if ts > now
107
332
 
108
333
  client_push(item)
109
334
  end
@@ -122,99 +347,16 @@ module Sidekiq
122
347
  #
123
348
  # In practice, any option is allowed. This is the main mechanism to configure the
124
349
  # options for a specific job.
125
- def sidekiq_options(opts={})
126
- # stringify
127
- self.sidekiq_options_hash = get_sidekiq_options.merge(Hash[opts.map{|k, v| [k.to_s, v]}])
128
- end
129
-
130
- def sidekiq_retry_in(&block)
131
- self.sidekiq_retry_in_block = block
132
- end
133
-
134
- def sidekiq_retries_exhausted(&block)
135
- self.sidekiq_retries_exhausted_block = block
136
- end
137
-
138
- def get_sidekiq_options # :nodoc:
139
- self.sidekiq_options_hash ||= Sidekiq.default_worker_options
350
+ def sidekiq_options(opts = {})
351
+ super
140
352
  end
141
353
 
142
354
  def client_push(item) # :nodoc:
143
- pool = Thread.current[:sidekiq_via_pool] || get_sidekiq_options['pool'] || Sidekiq.redis_pool
144
- # stringify
145
- item.keys.each do |key|
146
- item[key.to_s] = item.delete(key)
147
- end
355
+ pool = Thread.current[:sidekiq_via_pool] || get_sidekiq_options["pool"] || Sidekiq.redis_pool
356
+ stringified_item = item.transform_keys(&:to_s)
148
357
 
149
- Sidekiq::Client.new(pool).push(item)
358
+ Sidekiq::Client.new(pool).push(stringified_item)
150
359
  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
215
- end
216
- end
217
-
218
360
  end
219
361
  end
220
362
  end