sidekiq 5.1.3 → 7.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (157) hide show
  1. checksums.yaml +5 -5
  2. data/Changes.md +756 -8
  3. data/LICENSE.txt +9 -0
  4. data/README.md +48 -51
  5. data/bin/multi_queue_bench +271 -0
  6. data/bin/sidekiq +22 -3
  7. data/bin/sidekiqload +213 -115
  8. data/bin/sidekiqmon +11 -0
  9. data/lib/generators/sidekiq/job_generator.rb +59 -0
  10. data/lib/generators/sidekiq/templates/{worker.rb.erb → job.rb.erb} +2 -2
  11. data/lib/generators/sidekiq/templates/{worker_spec.rb.erb → job_spec.rb.erb} +1 -1
  12. data/lib/generators/sidekiq/templates/{worker_test.rb.erb → job_test.rb.erb} +1 -1
  13. data/lib/sidekiq/api.rb +640 -330
  14. data/lib/sidekiq/capsule.rb +132 -0
  15. data/lib/sidekiq/cli.rb +244 -257
  16. data/lib/sidekiq/client.rb +132 -103
  17. data/lib/sidekiq/component.rb +68 -0
  18. data/lib/sidekiq/config.rb +293 -0
  19. data/lib/sidekiq/deploy.rb +64 -0
  20. data/lib/sidekiq/embedded.rb +63 -0
  21. data/lib/sidekiq/fetch.rb +49 -42
  22. data/lib/sidekiq/iterable_job.rb +55 -0
  23. data/lib/sidekiq/job/interrupt_handler.rb +24 -0
  24. data/lib/sidekiq/job/iterable/active_record_enumerator.rb +53 -0
  25. data/lib/sidekiq/job/iterable/csv_enumerator.rb +47 -0
  26. data/lib/sidekiq/job/iterable/enumerators.rb +135 -0
  27. data/lib/sidekiq/job/iterable.rb +231 -0
  28. data/lib/sidekiq/job.rb +385 -0
  29. data/lib/sidekiq/job_logger.rb +49 -12
  30. data/lib/sidekiq/job_retry.rb +167 -103
  31. data/lib/sidekiq/job_util.rb +109 -0
  32. data/lib/sidekiq/launcher.rb +209 -102
  33. data/lib/sidekiq/logger.rb +131 -0
  34. data/lib/sidekiq/manager.rb +43 -46
  35. data/lib/sidekiq/metrics/query.rb +158 -0
  36. data/lib/sidekiq/metrics/shared.rb +97 -0
  37. data/lib/sidekiq/metrics/tracking.rb +148 -0
  38. data/lib/sidekiq/middleware/chain.rb +113 -56
  39. data/lib/sidekiq/middleware/current_attributes.rb +113 -0
  40. data/lib/sidekiq/middleware/i18n.rb +7 -7
  41. data/lib/sidekiq/middleware/modules.rb +23 -0
  42. data/lib/sidekiq/monitor.rb +147 -0
  43. data/lib/sidekiq/paginator.rb +28 -16
  44. data/lib/sidekiq/processor.rb +175 -112
  45. data/lib/sidekiq/rails.rb +54 -39
  46. data/lib/sidekiq/redis_client_adapter.rb +114 -0
  47. data/lib/sidekiq/redis_connection.rb +65 -86
  48. data/lib/sidekiq/ring_buffer.rb +31 -0
  49. data/lib/sidekiq/scheduled.rb +139 -48
  50. data/lib/sidekiq/sd_notify.rb +149 -0
  51. data/lib/sidekiq/systemd.rb +26 -0
  52. data/lib/sidekiq/testing/inline.rb +6 -5
  53. data/lib/sidekiq/testing.rb +95 -94
  54. data/lib/sidekiq/transaction_aware_client.rb +51 -0
  55. data/lib/sidekiq/version.rb +3 -1
  56. data/lib/sidekiq/web/action.rb +22 -12
  57. data/lib/sidekiq/web/application.rb +225 -76
  58. data/lib/sidekiq/web/csrf_protection.rb +183 -0
  59. data/lib/sidekiq/web/helpers.rb +215 -118
  60. data/lib/sidekiq/web/router.rb +23 -19
  61. data/lib/sidekiq/web.rb +114 -106
  62. data/lib/sidekiq/worker_compatibility_alias.rb +13 -0
  63. data/lib/sidekiq.rb +95 -182
  64. data/sidekiq.gemspec +26 -23
  65. data/web/assets/images/apple-touch-icon.png +0 -0
  66. data/web/assets/javascripts/application.js +157 -61
  67. data/web/assets/javascripts/base-charts.js +106 -0
  68. data/web/assets/javascripts/chart.min.js +13 -0
  69. data/web/assets/javascripts/chartjs-plugin-annotation.min.js +7 -0
  70. data/web/assets/javascripts/dashboard-charts.js +192 -0
  71. data/web/assets/javascripts/dashboard.js +35 -283
  72. data/web/assets/javascripts/metrics.js +298 -0
  73. data/web/assets/stylesheets/application-dark.css +147 -0
  74. data/web/assets/stylesheets/application-rtl.css +10 -93
  75. data/web/assets/stylesheets/application.css +169 -522
  76. data/web/assets/stylesheets/bootstrap.css +2 -2
  77. data/web/locales/ar.yml +71 -64
  78. data/web/locales/cs.yml +62 -62
  79. data/web/locales/da.yml +60 -53
  80. data/web/locales/de.yml +65 -53
  81. data/web/locales/el.yml +43 -24
  82. data/web/locales/en.yml +86 -65
  83. data/web/locales/es.yml +70 -54
  84. data/web/locales/fa.yml +65 -65
  85. data/web/locales/fr.yml +83 -62
  86. data/web/locales/gd.yml +99 -0
  87. data/web/locales/he.yml +65 -64
  88. data/web/locales/hi.yml +59 -59
  89. data/web/locales/it.yml +53 -53
  90. data/web/locales/ja.yml +75 -64
  91. data/web/locales/ko.yml +52 -52
  92. data/web/locales/lt.yml +83 -0
  93. data/web/locales/nb.yml +61 -61
  94. data/web/locales/nl.yml +52 -52
  95. data/web/locales/pl.yml +45 -45
  96. data/web/locales/pt-br.yml +83 -55
  97. data/web/locales/pt.yml +51 -51
  98. data/web/locales/ru.yml +68 -63
  99. data/web/locales/sv.yml +53 -53
  100. data/web/locales/ta.yml +60 -60
  101. data/web/locales/tr.yml +101 -0
  102. data/web/locales/uk.yml +62 -61
  103. data/web/locales/ur.yml +64 -64
  104. data/web/locales/vi.yml +83 -0
  105. data/web/locales/zh-cn.yml +43 -16
  106. data/web/locales/zh-tw.yml +42 -8
  107. data/web/views/_footer.erb +18 -3
  108. data/web/views/_job_info.erb +21 -4
  109. data/web/views/_metrics_period_select.erb +12 -0
  110. data/web/views/_nav.erb +4 -18
  111. data/web/views/_paging.erb +2 -0
  112. data/web/views/_poll_link.erb +3 -6
  113. data/web/views/_summary.erb +7 -7
  114. data/web/views/busy.erb +79 -29
  115. data/web/views/dashboard.erb +49 -19
  116. data/web/views/dead.erb +3 -3
  117. data/web/views/filtering.erb +7 -0
  118. data/web/views/layout.erb +9 -7
  119. data/web/views/metrics.erb +91 -0
  120. data/web/views/metrics_for_job.erb +59 -0
  121. data/web/views/morgue.erb +14 -15
  122. data/web/views/queue.erb +33 -23
  123. data/web/views/queues.erb +19 -5
  124. data/web/views/retries.erb +19 -16
  125. data/web/views/retry.erb +3 -3
  126. data/web/views/scheduled.erb +17 -15
  127. metadata +84 -129
  128. data/.github/contributing.md +0 -32
  129. data/.github/issue_template.md +0 -11
  130. data/.gitignore +0 -13
  131. data/.travis.yml +0 -14
  132. data/3.0-Upgrade.md +0 -70
  133. data/4.0-Upgrade.md +0 -53
  134. data/5.0-Upgrade.md +0 -56
  135. data/COMM-LICENSE +0 -95
  136. data/Ent-Changes.md +0 -216
  137. data/Gemfile +0 -8
  138. data/LICENSE +0 -9
  139. data/Pro-2.0-Upgrade.md +0 -138
  140. data/Pro-3.0-Upgrade.md +0 -44
  141. data/Pro-4.0-Upgrade.md +0 -35
  142. data/Pro-Changes.md +0 -729
  143. data/Rakefile +0 -8
  144. data/bin/sidekiqctl +0 -99
  145. data/code_of_conduct.md +0 -50
  146. data/lib/generators/sidekiq/worker_generator.rb +0 -49
  147. data/lib/sidekiq/core_ext.rb +0 -1
  148. data/lib/sidekiq/delay.rb +0 -42
  149. data/lib/sidekiq/exception_handler.rb +0 -29
  150. data/lib/sidekiq/extensions/action_mailer.rb +0 -57
  151. data/lib/sidekiq/extensions/active_record.rb +0 -40
  152. data/lib/sidekiq/extensions/class_methods.rb +0 -40
  153. data/lib/sidekiq/extensions/generic_proxy.rb +0 -31
  154. data/lib/sidekiq/logging.rb +0 -122
  155. data/lib/sidekiq/middleware/server/active_record.rb +0 -23
  156. data/lib/sidekiq/util.rb +0 -66
  157. data/lib/sidekiq/worker.rb +0 -204
data/lib/sidekiq/web.rb CHANGED
@@ -1,20 +1,21 @@
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"
4
+ require "securerandom"
8
5
 
9
- require 'sidekiq/web/router'
10
- require 'sidekiq/web/action'
11
- require 'sidekiq/web/application'
6
+ require "sidekiq"
7
+ require "sidekiq/api"
8
+ require "sidekiq/paginator"
9
+ require "sidekiq/web/helpers"
12
10
 
13
- require 'rack/protection'
11
+ require "sidekiq/web/router"
12
+ require "sidekiq/web/action"
13
+ require "sidekiq/web/application"
14
+ require "sidekiq/web/csrf_protection"
14
15
 
15
- require 'rack/builder'
16
- require 'rack/file'
17
- require 'rack/session/cookie'
16
+ require "rack/content_length"
17
+ require "rack/builder"
18
+ require "rack/static"
18
19
 
19
20
  module Sidekiq
20
21
  class Web
@@ -25,27 +26,34 @@ module Sidekiq
25
26
  ASSETS = "#{ROOT}/assets"
26
27
 
27
28
  DEFAULT_TABS = {
28
- "Dashboard" => '',
29
- "Busy" => 'busy',
30
- "Queues" => 'queues',
31
- "Retries" => 'retries',
32
- "Scheduled" => 'scheduled',
33
- "Dead" => 'morgue',
29
+ "Dashboard" => "",
30
+ "Busy" => "busy",
31
+ "Queues" => "queues",
32
+ "Retries" => "retries",
33
+ "Scheduled" => "scheduled",
34
+ "Dead" => "morgue",
35
+ "Metrics" => "metrics"
34
36
  }
35
37
 
38
+ if Gem::Version.new(Rack::RELEASE) < Gem::Version.new("3")
39
+ CONTENT_LANGUAGE = "Content-Language"
40
+ CONTENT_SECURITY_POLICY = "Content-Security-Policy"
41
+ LOCATION = "Location"
42
+ X_CASCADE = "X-Cascade"
43
+ X_CONTENT_TYPE_OPTIONS = "X-Content-Type-Options"
44
+ else
45
+ CONTENT_LANGUAGE = "content-language"
46
+ CONTENT_SECURITY_POLICY = "content-security-policy"
47
+ LOCATION = "location"
48
+ X_CASCADE = "x-cascade"
49
+ X_CONTENT_TYPE_OPTIONS = "x-content-type-options"
50
+ end
51
+
36
52
  class << self
37
53
  def settings
38
54
  self
39
55
  end
40
56
 
41
- def middlewares
42
- @middlewares ||= []
43
- end
44
-
45
- def use(*middleware_args, &block)
46
- middlewares << [middleware_args, block]
47
- end
48
-
49
57
  def default_tabs
50
58
  DEFAULT_TABS
51
59
  end
@@ -55,6 +63,10 @@ module Sidekiq
55
63
  end
56
64
  alias_method :tabs, :custom_tabs
57
65
 
66
+ def custom_job_info_rows
67
+ @custom_job_info_rows ||= []
68
+ end
69
+
58
70
  def locales
59
71
  @locales ||= LOCALES
60
72
  end
@@ -64,42 +76,48 @@ module Sidekiq
64
76
  end
65
77
 
66
78
  def enable(*opts)
67
- opts.each {|key| set(key, true) }
79
+ opts.each { |key| set(key, true) }
68
80
  end
69
81
 
70
82
  def disable(*opts)
71
- opts.each {|key| set(key, false) }
83
+ opts.each { |key| set(key, false) }
84
+ end
85
+
86
+ def middlewares
87
+ @middlewares ||= []
88
+ end
89
+
90
+ def use(*args, &block)
91
+ middlewares << [args, block]
72
92
  end
73
93
 
74
- # Helper for the Sinatra syntax: Sidekiq::Web.set(:session_secret, Rails.application.secrets...)
75
94
  def set(attribute, value)
76
95
  send(:"#{attribute}=", value)
77
96
  end
78
97
 
79
- attr_accessor :app_url, :session_secret, :redis_pool, :sessions
98
+ attr_accessor :app_url, :redis_pool
80
99
  attr_writer :locales, :views
81
100
  end
82
101
 
83
102
  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
103
+ child.app_url = app_url
104
+ child.redis_pool = redis_pool
88
105
  end
89
106
 
90
107
  def settings
91
108
  self.class.settings
92
109
  end
93
110
 
94
- def use(*middleware_args, &block)
95
- middlewares << [middleware_args, block]
111
+ def middlewares
112
+ @middlewares ||= self.class.middlewares
96
113
  end
97
114
 
98
- def middlewares
99
- @middlewares ||= Web.middlewares.dup
115
+ def use(*args, &block)
116
+ middlewares << [args, block]
100
117
  end
101
118
 
102
119
  def call(env)
120
+ env[:csp_nonce] = SecureRandom.base64(16)
103
121
  app.call(env)
104
122
  end
105
123
 
@@ -113,81 +131,80 @@ module Sidekiq
113
131
  end
114
132
 
115
133
  def enable(*opts)
116
- opts.each {|key| set(key, true) }
134
+ opts.each { |key| set(key, true) }
117
135
  end
118
136
 
119
137
  def disable(*opts)
120
- opts.each {|key| set(key, false) }
138
+ opts.each { |key| set(key, false) }
121
139
  end
122
140
 
123
141
  def set(attribute, value)
124
142
  send(:"#{attribute}=", value)
125
143
  end
126
144
 
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)
145
+ # Register a class as a Sidekiq Web UI extension. The class should
146
+ # provide one or more tabs which map to an index route. Options:
147
+ #
148
+ # @param extension [Class] Class which contains the HTTP actions, required
149
+ # @param name [String] the name of the extension, used to namespace assets
150
+ # @param tab [String | Array] labels(s) of the UI tabs
151
+ # @param index [String | Array] index route(s) for each tab
152
+ # @param root_dir [String] directory location to find assets, locales and views, typically `web/` within the gemfile
153
+ # @param asset_paths [Array] one or more directories under {root}/assets/{name} to be publicly served, e.g. ["js", "css", "img"]
154
+ # @param cache_for [Integer] amount of time to cache assets, default one day
155
+ #
156
+ # TODO name, tab and index will be mandatory in 8.0
157
+ #
158
+ # Web extensions will have a root `web/` directory with `locales/`, `assets/`
159
+ # and `views/` subdirectories.
160
+ def self.register(extension, name: nil, tab: nil, index: nil, root_dir: nil, cache_for: 86400, asset_paths: nil)
161
+ tab = Array(tab)
162
+ index = Array(index)
163
+ tab.zip(index).each do |tab, index|
164
+ tabs[tab] = index
165
+ end
166
+ if root_dir
167
+ locdir = File.join(root_dir, "locales")
168
+ locales << locdir if File.directory?(locdir)
169
+
170
+ if asset_paths && name
171
+ # if you have {root}/assets/{name}/js/scripts.js
172
+ # and {root}/assets/{name}/css/styles.css
173
+ # you would pass in:
174
+ # asset_paths: ["js", "css"]
175
+ # See script_tag and style_tag in web/helpers.rb
176
+ assdir = File.join(root_dir, "assets")
177
+ assurls = Array(asset_paths).map { |x| "/#{name}/#{x}" }
178
+ assetprops = {
179
+ urls: assurls,
180
+ root: assdir,
181
+ cascade: true
182
+ }
183
+ assetprops[:header_rules] = [[:all, {Rack::CACHE_CONTROL => "private, max-age=#{cache_for.to_i}"}]] if cache_for
184
+ middlewares << [[Rack::Static, assetprops], nil]
185
+ end
136
186
  end
137
187
 
138
- @sessions
139
- end
140
-
141
- def self.register(extension)
188
+ yield self if block_given?
142
189
  extension.registered(WebApplication)
143
190
  end
144
191
 
145
192
  private
146
193
 
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
194
  def build
177
- build_sessions
178
-
179
- middlewares = self.middlewares
180
195
  klass = self.class
196
+ m = middlewares
181
197
 
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) }
198
+ rules = []
199
+ rules = [[:all, {Rack::CACHE_CONTROL => "private, max-age=86400"}]] unless ENV["SIDEKIQ_WEB_TESTING"]
190
200
 
201
+ ::Rack::Builder.new do
202
+ use Rack::Static, urls: ["/stylesheets", "/images", "/javascripts"],
203
+ root: ASSETS,
204
+ cascade: true,
205
+ header_rules: rules
206
+ m.each { |middleware, block| use(*middleware, &block) }
207
+ use Sidekiq::Web::CsrfProtection unless $TESTING
191
208
  run WebApplication.new(klass)
192
209
  end
193
210
  end
@@ -196,18 +213,9 @@ module Sidekiq
196
213
  Sidekiq::WebApplication.helpers WebHelpers
197
214
  Sidekiq::WebApplication.helpers Sidekiq::Paginator
198
215
 
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)
216
+ Sidekiq::WebAction.class_eval <<-RUBY, __FILE__, __LINE__ + 1
217
+ def _render
218
+ #{ERB.new(File.read(Web::LAYOUT)).src}
211
219
  end
212
- end
220
+ RUBY
213
221
  end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sidekiq
4
+ # Sidekiq::Job is a new alias for Sidekiq::Worker as of Sidekiq 6.3.0.
5
+ # Use `include Sidekiq::Job` rather than `include Sidekiq::Worker`.
6
+ #
7
+ # The term "worker" is too generic and overly confusing, used in several
8
+ # different contexts meaning different things. Many people call a Sidekiq
9
+ # process a "worker". Some people call the thread that executes jobs a
10
+ # "worker". This change brings Sidekiq closer to ActiveJob where your job
11
+ # classes extend ApplicationJob.
12
+ Worker = Job
13
+ end
data/lib/sidekiq.rb CHANGED
@@ -1,235 +1,148 @@
1
1
  # frozen_string_literal: true
2
- require 'sidekiq/version'
3
- fail "Sidekiq #{Sidekiq::VERSION} does not support Ruby versions below 2.2.2." if RUBY_PLATFORM != 'java' && Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.2.2')
4
2
 
5
- require 'sidekiq/logging'
6
- require 'sidekiq/client'
7
- require 'sidekiq/worker'
8
- require 'sidekiq/redis_connection'
9
- require 'sidekiq/delay'
3
+ require "sidekiq/version"
4
+ fail "Sidekiq #{Sidekiq::VERSION} does not support Ruby versions below 2.7.0." if RUBY_PLATFORM != "java" && Gem::Version.new(RUBY_VERSION) < Gem::Version.new("2.7.0")
10
5
 
11
- require 'json'
6
+ begin
7
+ require "sidekiq-ent/version"
8
+ fail <<~EOM if Gem::Version.new(Sidekiq::Enterprise::VERSION).segments[0] != Sidekiq::MAJOR
12
9
 
13
- module Sidekiq
14
- NAME = 'Sidekiq'
15
- LICENSE = 'See LICENSE and the LGPL-3.0 for licensing details.'
16
-
17
- DEFAULTS = {
18
- queues: [],
19
- labels: [],
20
- concurrency: 25,
21
- require: '.',
22
- environment: nil,
23
- timeout: 8,
24
- poll_interval_average: nil,
25
- average_scheduled_poll_interval: 5,
26
- error_handlers: [],
27
- death_handlers: [],
28
- lifecycle_events: {
29
- startup: [],
30
- quiet: [],
31
- shutdown: [],
32
- heartbeat: [],
33
- },
34
- dead_max_jobs: 10_000,
35
- dead_timeout_in_seconds: 180 * 24 * 60 * 60, # 6 months
36
- reloader: proc { |&block| block.call },
37
- }
38
-
39
- DEFAULT_WORKER_OPTIONS = {
40
- 'retry' => true,
41
- 'queue' => 'default'
42
- }
43
-
44
- FAKE_INFO = {
45
- "redis_version" => "9.9.9",
46
- "uptime_in_days" => "9999",
47
- "connected_clients" => "9999",
48
- "used_memory_human" => "9P",
49
- "used_memory_peak_human" => "9P"
50
- }
10
+ Sidekiq Enterprise #{Sidekiq::Enterprise::VERSION} does not work with Sidekiq #{Sidekiq::VERSION}.
11
+ Starting with Sidekiq 7, major versions are synchronized so Sidekiq Enterprise 7 works with Sidekiq 7.
12
+ Use `bundle up sidekiq-ent` to upgrade.
51
13
 
52
- def self.❨╯°□°❩╯︵┻━┻
53
- puts "Calm down, yo."
54
- end
14
+ EOM
15
+ rescue LoadError
16
+ end
55
17
 
56
- def self.options
57
- @options ||= DEFAULTS.dup
58
- end
59
- def self.options=(opts)
60
- @options = opts
61
- end
18
+ begin
19
+ require "sidekiq/pro/version"
20
+ fail <<~EOM if Gem::Version.new(Sidekiq::Pro::VERSION).segments[0] != Sidekiq::MAJOR
62
21
 
63
- ##
64
- # Configuration for Sidekiq server, use like:
65
- #
66
- # Sidekiq.configure_server do |config|
67
- # config.redis = { :namespace => 'myapp', :size => 25, :url => 'redis://myhost:8877/0' }
68
- # config.server_middleware do |chain|
69
- # chain.add MyServerHook
70
- # end
71
- # end
72
- def self.configure_server
73
- yield self if server?
74
- end
22
+ Sidekiq Pro #{Sidekiq::Pro::VERSION} does not work with Sidekiq #{Sidekiq::VERSION}.
23
+ Starting with Sidekiq 7, major versions are synchronized so Sidekiq Pro 7 works with Sidekiq 7.
24
+ Use `bundle up sidekiq-pro` to upgrade.
75
25
 
76
- ##
77
- # Configuration for Sidekiq client, use like:
78
- #
79
- # Sidekiq.configure_client do |config|
80
- # config.redis = { :namespace => 'myapp', :size => 1, :url => 'redis://myhost:8877/0' }
81
- # end
82
- def self.configure_client
83
- yield self unless server?
26
+ EOM
27
+ rescue LoadError
28
+ end
29
+
30
+ require "sidekiq/config"
31
+ require "sidekiq/logger"
32
+ require "sidekiq/client"
33
+ require "sidekiq/transaction_aware_client"
34
+ require "sidekiq/job"
35
+ require "sidekiq/iterable_job"
36
+ require "sidekiq/worker_compatibility_alias"
37
+ require "sidekiq/redis_client_adapter"
38
+
39
+ require "json"
40
+
41
+ module Sidekiq
42
+ NAME = "Sidekiq"
43
+ LICENSE = "See LICENSE and the LGPL-3.0 for licensing details."
44
+
45
+ def self.❨╯°□°❩╯︵┻━┻
46
+ puts "Take a deep breath and count to ten..."
84
47
  end
85
48
 
86
49
  def self.server?
87
50
  defined?(Sidekiq::CLI)
88
51
  end
89
52
 
90
- def self.redis
91
- raise ArgumentError, "requires a block" unless block_given?
92
- redis_pool.with do |conn|
93
- retryable = true
94
- begin
95
- yield conn
96
- rescue Redis::CommandError => ex
97
- #2550 Failover can cause the server to become a slave, need
98
- # to disconnect and reopen the socket to get back to the master.
99
- (conn.disconnect!; retryable = false; retry) if retryable && ex.message =~ /READONLY/
100
- raise
101
- end
102
- end
103
- end
104
-
105
- def self.redis_info
106
- redis do |conn|
107
- begin
108
- # admin commands can't go through redis-namespace starting
109
- # in redis-namespace 2.0
110
- if conn.respond_to?(:namespace)
111
- conn.redis.info
112
- else
113
- conn.info
114
- end
115
- rescue Redis::CommandError => ex
116
- #2850 return fake version when INFO command has (probably) been renamed
117
- raise unless ex.message =~ /unknown command/
118
- FAKE_INFO
119
- end
120
- end
53
+ def self.load_json(string)
54
+ JSON.parse(string)
121
55
  end
122
56
 
123
- def self.redis_pool
124
- @redis ||= Sidekiq::RedisConnection.create
57
+ def self.dump_json(object)
58
+ JSON.generate(object)
125
59
  end
126
60
 
127
- def self.redis=(hash)
128
- @redis = if hash.is_a?(ConnectionPool)
129
- hash
130
- else
131
- Sidekiq::RedisConnection.create(hash)
132
- end
61
+ def self.pro?
62
+ defined?(Sidekiq::Pro)
133
63
  end
134
64
 
135
- def self.client_middleware
136
- @client_chain ||= Middleware::Chain.new
137
- yield @client_chain if block_given?
138
- @client_chain
65
+ def self.ent?
66
+ defined?(Sidekiq::Enterprise)
139
67
  end
140
68
 
141
- def self.server_middleware
142
- @server_chain ||= default_server_middleware
143
- yield @server_chain if block_given?
144
- @server_chain
69
+ def self.redis_pool
70
+ (Thread.current[:sidekiq_capsule] || default_configuration).redis_pool
145
71
  end
146
72
 
147
- def self.default_server_middleware
148
- Middleware::Chain.new
73
+ def self.redis(&block)
74
+ (Thread.current[:sidekiq_capsule] || default_configuration).redis(&block)
149
75
  end
150
76
 
151
- def self.default_worker_options=(hash)
152
- # stringify
153
- @default_worker_options = default_worker_options.merge(Hash[hash.map{|k, v| [k.to_s, v]}])
154
- end
155
- def self.default_worker_options
156
- defined?(@default_worker_options) ? @default_worker_options : DEFAULT_WORKER_OPTIONS
77
+ def self.strict_args!(mode = :raise)
78
+ Sidekiq::Config::DEFAULTS[:on_complex_arguments] = mode
157
79
  end
158
80
 
159
- def self.default_retries_exhausted=(prok)
160
- logger.info { "default_retries_exhausted is deprecated, please use `config.death_handlers << -> {|job, ex| }`" }
161
- return nil unless prok
162
- death_handlers << prok
81
+ def self.default_job_options=(hash)
82
+ @default_job_options = default_job_options.merge(hash.transform_keys(&:to_s))
163
83
  end
164
84
 
165
- ##
166
- # Death handlers are called when all retries for a job have been exhausted and
167
- # the job dies. It's the notification to your application
168
- # that this job will not succeed without manual intervention.
169
- #
170
- # Sidekiq.configure_server do |config|
171
- # config.death_handlers << ->(job, ex) do
172
- # end
173
- # end
174
- def self.death_handlers
175
- options[:death_handlers]
85
+ def self.default_job_options
86
+ @default_job_options ||= {"retry" => true, "queue" => "default"}
176
87
  end
177
88
 
178
- def self.load_json(string)
179
- JSON.parse(string)
180
- end
181
- def self.dump_json(object)
182
- JSON.generate(object)
89
+ def self.default_configuration
90
+ @config ||= Sidekiq::Config.new
183
91
  end
184
92
 
185
93
  def self.logger
186
- Sidekiq::Logging.logger
94
+ default_configuration.logger
187
95
  end
188
- def self.logger=(log)
189
- Sidekiq::Logging.logger = log
96
+
97
+ def self.configure_server(&block)
98
+ (@config_blocks ||= []) << block
99
+ yield default_configuration if server?
190
100
  end
191
101
 
192
- # How frequently Redis should be checked by a random Sidekiq process for
193
- # scheduled and retriable jobs. Each individual process will take turns by
194
- # waiting some multiple of this value.
195
- #
196
- # See sidekiq/scheduled.rb for an in-depth explanation of this value
197
- def self.average_scheduled_poll_interval=(interval)
198
- self.options[:average_scheduled_poll_interval] = interval
102
+ def self.freeze!
103
+ @frozen = true
104
+ @config_blocks = nil
199
105
  end
200
106
 
201
- # Register a proc to handle any error which occurs within the Sidekiq process.
107
+ # Creates a Sidekiq::Config instance that is more tuned for embedding
108
+ # within an arbitrary Ruby process. Notably it reduces concurrency by
109
+ # default so there is less contention for CPU time with other threads.
202
110
  #
203
- # Sidekiq.configure_server do |config|
204
- # config.error_handlers << proc {|ex,ctx_hash| MyErrorService.notify(ex, ctx_hash) }
111
+ # inst = Sidekiq.configure_embed do |config|
112
+ # config.queues = %w[critical default low]
205
113
  # end
114
+ # inst.run
115
+ # sleep 10
116
+ # inst.stop
117
+ #
118
+ # NB: it is really easy to overload a Ruby process with threads due to the GIL.
119
+ # I do not recommend setting concurrency higher than 2-3.
206
120
  #
207
- # The default error handler logs errors to Sidekiq.logger.
208
- def self.error_handlers
209
- self.options[:error_handlers]
121
+ # NB: Sidekiq only supports one instance in memory. You will get undefined behavior
122
+ # if you try to embed Sidekiq twice in the same process.
123
+ def self.configure_embed(&block)
124
+ raise "Sidekiq global configuration is frozen, you must create all embedded instances BEFORE calling `run`" if @frozen
125
+
126
+ require "sidekiq/embedded"
127
+ cfg = default_configuration
128
+ cfg.concurrency = 2
129
+ @config_blocks&.each { |block| block.call(cfg) }
130
+ yield cfg
131
+
132
+ Sidekiq::Embedded.new(cfg)
210
133
  end
211
134
 
212
- # Register a block to run at a point in the Sidekiq lifecycle.
213
- # :startup, :quiet or :shutdown are valid events.
214
- #
215
- # Sidekiq.configure_server do |config|
216
- # config.on(:shutdown) do
217
- # puts "Goodbye cruel world!"
218
- # end
219
- # end
220
- def self.on(event, &block)
221
- raise ArgumentError, "Symbols only please: #{event}" unless event.is_a?(Symbol)
222
- raise ArgumentError, "Invalid event name: #{event}" unless options[:lifecycle_events].key?(event)
223
- options[:lifecycle_events][event] << block
135
+ def self.configure_client
136
+ yield default_configuration unless server?
224
137
  end
225
138
 
226
- # We are shutting down Sidekiq but what about workers that
139
+ # We are shutting down Sidekiq but what about threads that
227
140
  # are working on some long job? This error is
228
- # raised in workers that have not finished within the hard
141
+ # raised in jobs that have not finished within the hard
229
142
  # timeout limit. This is needed to rollback db transactions,
230
143
  # otherwise Ruby's Thread#kill will commit. See #377.
231
- # DO NOT RESCUE THIS ERROR IN YOUR WORKERS
144
+ # DO NOT RESCUE THIS ERROR IN YOUR JOBS
232
145
  class Shutdown < Interrupt; end
233
146
  end
234
147
 
235
- require 'sidekiq/rails' if defined?(::Rails::Engine)
148
+ require "sidekiq/rails" if defined?(::Rails::Engine)