sidekiq 4.2.2 → 6.3.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 (138) hide show
  1. checksums.yaml +5 -5
  2. data/Changes.md +516 -0
  3. data/LICENSE +2 -2
  4. data/README.md +23 -36
  5. data/bin/sidekiq +26 -2
  6. data/bin/sidekiqload +28 -38
  7. data/bin/sidekiqmon +8 -0
  8. data/lib/generators/sidekiq/templates/worker_spec.rb.erb +1 -1
  9. data/lib/generators/sidekiq/templates/worker_test.rb.erb +2 -2
  10. data/lib/generators/sidekiq/worker_generator.rb +21 -13
  11. data/lib/sidekiq/api.rb +401 -243
  12. data/lib/sidekiq/cli.rb +228 -212
  13. data/lib/sidekiq/client.rb +76 -53
  14. data/lib/sidekiq/delay.rb +41 -0
  15. data/lib/sidekiq/exception_handler.rb +12 -16
  16. data/lib/sidekiq/extensions/action_mailer.rb +13 -22
  17. data/lib/sidekiq/extensions/active_record.rb +13 -10
  18. data/lib/sidekiq/extensions/class_methods.rb +14 -11
  19. data/lib/sidekiq/extensions/generic_proxy.rb +12 -4
  20. data/lib/sidekiq/fetch.rb +39 -31
  21. data/lib/sidekiq/job.rb +13 -0
  22. data/lib/sidekiq/job_logger.rb +63 -0
  23. data/lib/sidekiq/job_retry.rb +259 -0
  24. data/lib/sidekiq/launcher.rb +170 -71
  25. data/lib/sidekiq/logger.rb +166 -0
  26. data/lib/sidekiq/manager.rb +17 -20
  27. data/lib/sidekiq/middleware/chain.rb +20 -8
  28. data/lib/sidekiq/middleware/current_attributes.rb +52 -0
  29. data/lib/sidekiq/middleware/i18n.rb +5 -7
  30. data/lib/sidekiq/monitor.rb +133 -0
  31. data/lib/sidekiq/paginator.rb +18 -14
  32. data/lib/sidekiq/processor.rb +169 -78
  33. data/lib/sidekiq/rails.rb +41 -36
  34. data/lib/sidekiq/redis_connection.rb +65 -20
  35. data/lib/sidekiq/scheduled.rb +85 -34
  36. data/lib/sidekiq/sd_notify.rb +149 -0
  37. data/lib/sidekiq/systemd.rb +24 -0
  38. data/lib/sidekiq/testing/inline.rb +2 -1
  39. data/lib/sidekiq/testing.rb +52 -26
  40. data/lib/sidekiq/util.rb +48 -15
  41. data/lib/sidekiq/version.rb +2 -1
  42. data/lib/sidekiq/web/action.rb +15 -17
  43. data/lib/sidekiq/web/application.rb +114 -92
  44. data/lib/sidekiq/web/csrf_protection.rb +180 -0
  45. data/lib/sidekiq/web/helpers.rb +151 -83
  46. data/lib/sidekiq/web/router.rb +27 -19
  47. data/lib/sidekiq/web.rb +85 -76
  48. data/lib/sidekiq/worker.rb +233 -43
  49. data/lib/sidekiq.rb +88 -64
  50. data/sidekiq.gemspec +24 -22
  51. data/web/assets/images/apple-touch-icon.png +0 -0
  52. data/web/assets/javascripts/application.js +86 -59
  53. data/web/assets/javascripts/dashboard.js +81 -85
  54. data/web/assets/stylesheets/application-dark.css +147 -0
  55. data/web/assets/stylesheets/application-rtl.css +242 -0
  56. data/web/assets/stylesheets/application.css +319 -141
  57. data/web/assets/stylesheets/bootstrap-rtl.min.css +9 -0
  58. data/web/assets/stylesheets/bootstrap.css +2 -2
  59. data/web/locales/ar.yml +87 -0
  60. data/web/locales/de.yml +14 -2
  61. data/web/locales/en.yml +8 -1
  62. data/web/locales/es.yml +22 -5
  63. data/web/locales/fa.yml +80 -0
  64. data/web/locales/fr.yml +10 -3
  65. data/web/locales/he.yml +79 -0
  66. data/web/locales/ja.yml +12 -4
  67. data/web/locales/lt.yml +83 -0
  68. data/web/locales/pl.yml +4 -4
  69. data/web/locales/ru.yml +4 -0
  70. data/web/locales/ur.yml +80 -0
  71. data/web/locales/vi.yml +83 -0
  72. data/web/views/_footer.erb +5 -2
  73. data/web/views/_job_info.erb +4 -3
  74. data/web/views/_nav.erb +4 -18
  75. data/web/views/_paging.erb +1 -1
  76. data/web/views/_poll_link.erb +2 -5
  77. data/web/views/_summary.erb +7 -7
  78. data/web/views/busy.erb +60 -22
  79. data/web/views/dashboard.erb +23 -15
  80. data/web/views/dead.erb +3 -3
  81. data/web/views/layout.erb +14 -3
  82. data/web/views/morgue.erb +19 -12
  83. data/web/views/queue.erb +24 -14
  84. data/web/views/queues.erb +14 -4
  85. data/web/views/retries.erb +22 -13
  86. data/web/views/retry.erb +4 -4
  87. data/web/views/scheduled.erb +7 -4
  88. metadata +44 -194
  89. data/.github/contributing.md +0 -32
  90. data/.github/issue_template.md +0 -4
  91. data/.gitignore +0 -12
  92. data/.travis.yml +0 -12
  93. data/3.0-Upgrade.md +0 -70
  94. data/4.0-Upgrade.md +0 -53
  95. data/COMM-LICENSE +0 -95
  96. data/Ent-Changes.md +0 -146
  97. data/Gemfile +0 -29
  98. data/Pro-2.0-Upgrade.md +0 -138
  99. data/Pro-3.0-Upgrade.md +0 -44
  100. data/Pro-Changes.md +0 -570
  101. data/Rakefile +0 -9
  102. data/bin/sidekiqctl +0 -99
  103. data/code_of_conduct.md +0 -50
  104. data/lib/sidekiq/core_ext.rb +0 -106
  105. data/lib/sidekiq/logging.rb +0 -106
  106. data/lib/sidekiq/middleware/server/active_record.rb +0 -13
  107. data/lib/sidekiq/middleware/server/logging.rb +0 -40
  108. data/lib/sidekiq/middleware/server/retry_jobs.rb +0 -205
  109. data/test/config.yml +0 -9
  110. data/test/env_based_config.yml +0 -11
  111. data/test/fake_env.rb +0 -1
  112. data/test/fixtures/en.yml +0 -2
  113. data/test/helper.rb +0 -75
  114. data/test/test_actors.rb +0 -138
  115. data/test/test_api.rb +0 -528
  116. data/test/test_cli.rb +0 -418
  117. data/test/test_client.rb +0 -266
  118. data/test/test_exception_handler.rb +0 -56
  119. data/test/test_extensions.rb +0 -127
  120. data/test/test_fetch.rb +0 -50
  121. data/test/test_launcher.rb +0 -95
  122. data/test/test_logging.rb +0 -35
  123. data/test/test_manager.rb +0 -50
  124. data/test/test_middleware.rb +0 -158
  125. data/test/test_processor.rb +0 -201
  126. data/test/test_rails.rb +0 -22
  127. data/test/test_redis_connection.rb +0 -132
  128. data/test/test_retry.rb +0 -326
  129. data/test/test_retry_exhausted.rb +0 -149
  130. data/test/test_scheduled.rb +0 -115
  131. data/test/test_scheduling.rb +0 -50
  132. data/test/test_sidekiq.rb +0 -107
  133. data/test/test_testing.rb +0 -143
  134. data/test/test_testing_fake.rb +0 -357
  135. data/test/test_testing_inline.rb +0 -94
  136. data/test/test_util.rb +0 -13
  137. data/test/test_web.rb +0 -666
  138. data/test/test_web_helpers.rb +0 -54
data/lib/sidekiq/web.rb CHANGED
@@ -1,36 +1,36 @@
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
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
- "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
@@ -63,25 +55,53 @@ module Sidekiq
63
55
  @views ||= VIEWS
64
56
  end
65
57
 
66
- # Helper for the Sinatra syntax: Sidekiq::Web.set(:session_secret, Rails.application.secrets...)
58
+ def enable(*opts)
59
+ opts.each { |key| set(key, true) }
60
+ end
61
+
62
+ def disable(*opts)
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
+ end
73
+
67
74
  def set(attribute, value)
68
75
  send(:"#{attribute}=", value)
69
76
  end
70
77
 
71
- attr_accessor :app_url, :session_secret, :redis_pool
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
72
87
  attr_writer :locales, :views
73
88
  end
74
89
 
90
+ def self.inherited(child)
91
+ child.app_url = app_url
92
+ child.redis_pool = redis_pool
93
+ end
94
+
75
95
  def settings
76
96
  self.class.settings
77
97
  end
78
98
 
79
- def use(*middleware_args, &block)
80
- middlewares << [middleware_args, block]
99
+ def middlewares
100
+ @middlewares ||= self.class.middlewares
81
101
  end
82
102
 
83
- def middlewares
84
- @middlewares ||= Web.middlewares.dup
103
+ def use(*args, &block)
104
+ middlewares << [args, block]
85
105
  end
86
106
 
87
107
  def call(env)
@@ -97,44 +117,42 @@ module Sidekiq
97
117
  @app ||= build
98
118
  end
99
119
 
120
+ def enable(*opts)
121
+ opts.each { |key| set(key, true) }
122
+ end
123
+
124
+ def disable(*opts)
125
+ opts.each { |key| set(key, false) }
126
+ end
127
+
128
+ def set(attribute, value)
129
+ send(:"#{attribute}=", value)
130
+ end
131
+
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}"
134
+ end
135
+
100
136
  def self.register(extension)
101
137
  extension.registered(WebApplication)
102
138
  end
103
139
 
104
140
  private
105
141
 
106
- def using?(middleware)
107
- middlewares.any? do |(m,_)|
108
- m.kind_of?(Array) && (m[0] == middleware || m[0].kind_of?(middleware))
109
- end
110
- end
111
-
112
142
  def build
113
- middlewares = self.middlewares
114
143
  klass = self.class
144
+ m = middlewares
115
145
 
116
- unless using?(::Rack::Protection) || ENV['RACK_ENV'] == 'test'
117
- middlewares.unshift [[::Rack::Protection, { use: :authenticity_token }], nil]
118
- end
119
-
120
- unless using? ::Rack::Session::Cookie
121
- unless secret = Web.session_secret
122
- require 'securerandom'
123
- secret = SecureRandom.hex(64)
124
- end
125
-
126
- middlewares.unshift [[::Rack::Session::Cookie, { secret: secret }], nil]
127
- end
146
+ rules = []
147
+ rules = [[:all, {"Cache-Control" => "public, max-age=86400"}]] unless ENV["SIDEKIQ_WEB_TESTING"]
128
148
 
129
149
  ::Rack::Builder.new do
130
- %w(stylesheets javascripts images).each do |asset_dir|
131
- map "/#{asset_dir}" do
132
- run ::Rack::File.new("#{ASSETS}/#{asset_dir}", { 'Cache-Control' => 'public, max-age=86400' })
133
- end
134
- end
135
-
136
- middlewares.each {|middleware, block| use(*middleware, &block) }
137
-
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
138
156
  run WebApplication.new(klass)
139
157
  end
140
158
  end
@@ -143,18 +161,9 @@ module Sidekiq
143
161
  Sidekiq::WebApplication.helpers WebHelpers
144
162
  Sidekiq::WebApplication.helpers Sidekiq::Paginator
145
163
 
146
- Sidekiq::WebAction.class_eval "def _render\n#{ERB.new(File.read(Web::LAYOUT)).src}\nend"
147
- end
148
-
149
- if defined?(::ActionDispatch::Request::Session) &&
150
- !::ActionDispatch::Request::Session.respond_to?(:each)
151
- # mperham/sidekiq#2460
152
- # Rack apps can't reuse the Rails session store without
153
- # this monkeypatch, fixed in Rails 5.
154
- class ActionDispatch::Request::Session
155
- def each(&block)
156
- hash = self.to_hash
157
- hash.each(&block)
164
+ Sidekiq::WebAction.class_eval <<-RUBY, __FILE__, __LINE__ + 1
165
+ def _render
166
+ #{ERB.new(File.read(Web::LAYOUT)).src}
158
167
  end
159
- end
168
+ RUBY
160
169
  end
@@ -1,44 +1,222 @@
1
1
  # frozen_string_literal: true
2
- require 'sidekiq/client'
3
- require 'sidekiq/core_ext'
4
2
 
5
- module Sidekiq
3
+ require "sidekiq/client"
6
4
 
5
+ module Sidekiq
7
6
  ##
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
12
+ # sidekiq_options queue: 'critical', retry: 5
13
13
  #
14
- # def perform(*args)
15
- # # do some work
14
+ # def perform(*args)
15
+ # # do some work
16
+ # end
16
17
  # end
17
- # end
18
18
  #
19
19
  # Then in your Rails app, you can do this:
20
20
  #
21
21
  # HardWorker.perform_async(1, 2, 3)
22
22
  #
23
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
+ #
24
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
+
25
156
  attr_accessor :jid
26
157
 
27
158
  def self.included(base)
28
- 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" }
29
160
 
161
+ base.include(Options)
30
162
  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
34
163
  end
35
164
 
36
165
  def logger
37
166
  Sidekiq.logger
38
167
  end
39
168
 
40
- module ClassMethods
169
+ # This helper class encapsulates the set options for `set`, e.g.
170
+ #
171
+ # SomeWorker.set(queue: 'foo').perform_async(....)
172
+ #
173
+ class Setter
174
+ def initialize(klass, opts)
175
+ @klass = klass
176
+ @opts = opts
177
+
178
+ # ActiveJob compatibility
179
+ interval = @opts.delete(:wait_until) || @opts.delete(:wait)
180
+ at(interval) if interval
181
+ end
182
+
183
+ def set(options)
184
+ interval = options.delete(:wait_until) || options.delete(:wait)
185
+ @opts.merge!(options)
186
+ at(interval) if interval
187
+ self
188
+ end
41
189
 
190
+ def perform_async(*args)
191
+ @klass.client_push(@opts.merge("args" => args, "class" => @klass))
192
+ end
193
+
194
+ def perform_bulk(args, batch_size: 1_000)
195
+ args.each_slice(batch_size).flat_map do |slice|
196
+ Sidekiq::Client.push_bulk(@opts.merge("class" => @klass, "args" => slice))
197
+ end
198
+ end
199
+
200
+ # +interval+ must be a timestamp, numeric or something that acts
201
+ # numeric (like an activesupport time interval).
202
+ def perform_in(interval, *args)
203
+ at(interval).perform_async(*args)
204
+ end
205
+ alias_method :perform_at, :perform_in
206
+
207
+ private
208
+
209
+ def at(interval)
210
+ int = interval.to_f
211
+ now = Time.now.to_f
212
+ ts = (int < 1_000_000_000 ? now + int : int)
213
+ # Optimization to enqueue something now that is scheduled to go out now or in the past
214
+ @opts["at"] = ts if ts > now
215
+ self
216
+ end
217
+ end
218
+
219
+ module ClassMethods
42
220
  def delay(*args)
43
221
  raise ArgumentError, "Do not call .delay on a Sidekiq::Worker class, call .perform_async"
44
222
  end
@@ -51,26 +229,55 @@ module Sidekiq
51
229
  raise ArgumentError, "Do not call .delay_until on a Sidekiq::Worker class, call .perform_at"
52
230
  end
53
231
 
232
+ def queue_as(q)
233
+ sidekiq_options("queue" => q.to_s)
234
+ end
235
+
54
236
  def set(options)
55
- Thread.current[:sidekiq_worker_set] = options
56
- self
237
+ Setter.new(self, options)
57
238
  end
58
239
 
59
240
  def perform_async(*args)
60
- client_push('class' => self, 'args' => args)
241
+ client_push("class" => self, "args" => args)
242
+ end
243
+
244
+ ##
245
+ # Push a large number of jobs to Redis, while limiting the batch of
246
+ # each job payload to 1,000. This method helps cut down on the number
247
+ # of round trips to Redis, which can increase the performance of enqueueing
248
+ # large numbers of jobs.
249
+ #
250
+ # +items+ must be an Array of Arrays.
251
+ #
252
+ # For finer-grained control, use `Sidekiq::Client.push_bulk` directly.
253
+ #
254
+ # Example (3 Redis round trips):
255
+ #
256
+ # SomeWorker.perform_async(1)
257
+ # SomeWorker.perform_async(2)
258
+ # SomeWorker.perform_async(3)
259
+ #
260
+ # Would instead become (1 Redis round trip):
261
+ #
262
+ # SomeWorker.perform_bulk([[1], [2], [3]])
263
+ #
264
+ def perform_bulk(items, batch_size: 1_000)
265
+ items.each_slice(batch_size).flat_map do |slice|
266
+ Sidekiq::Client.push_bulk("class" => self, "args" => slice)
267
+ end
61
268
  end
62
269
 
63
270
  # +interval+ must be a timestamp, numeric or something that acts
64
271
  # numeric (like an activesupport time interval).
65
272
  def perform_in(interval, *args)
66
273
  int = interval.to_f
67
- now = Time.now
68
- ts = (int < 1_000_000_000 ? (now + interval).to_f : int)
274
+ now = Time.now.to_f
275
+ ts = (int < 1_000_000_000 ? now + int : int)
69
276
 
70
- item = { 'class' => self, 'args' => args, 'at' => ts }
277
+ item = {"class" => self, "args" => args}
71
278
 
72
279
  # Optimization to enqueue something now that is scheduled to go out now or in the past
73
- item.delete('at'.freeze) if ts <= now.to_f
280
+ item["at"] = ts if ts > now
74
281
 
75
282
  client_push(item)
76
283
  end
@@ -89,33 +296,16 @@ module Sidekiq
89
296
  #
90
297
  # In practice, any option is allowed. This is the main mechanism to configure the
91
298
  # options for a specific job.
92
- def sidekiq_options(opts={})
93
- self.sidekiq_options_hash = get_sidekiq_options.merge(opts.stringify_keys)
94
- end
95
-
96
- def sidekiq_retry_in(&block)
97
- self.sidekiq_retry_in_block = block
98
- end
99
-
100
- def sidekiq_retries_exhausted(&block)
101
- self.sidekiq_retries_exhausted_block = block
102
- end
103
-
104
- def get_sidekiq_options # :nodoc:
105
- self.sidekiq_options_hash ||= Sidekiq.default_worker_options
299
+ def sidekiq_options(opts = {})
300
+ super
106
301
  end
107
302
 
108
303
  def client_push(item) # :nodoc:
109
- 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
115
- end
116
- Sidekiq::Client.new(pool).push(hash)
117
- end
304
+ pool = Thread.current[:sidekiq_via_pool] || get_sidekiq_options["pool"] || Sidekiq.redis_pool
305
+ stringified_item = item.transform_keys(&:to_s)
118
306
 
307
+ Sidekiq::Client.new(pool).push(stringified_item)
308
+ end
119
309
  end
120
310
  end
121
311
  end