sidekiq 6.0.4 → 7.2.0

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 (147) hide show
  1. checksums.yaml +4 -4
  2. data/Changes.md +491 -10
  3. data/LICENSE.txt +9 -0
  4. data/README.md +47 -38
  5. data/bin/sidekiq +22 -3
  6. data/bin/sidekiqload +207 -117
  7. data/bin/sidekiqmon +4 -1
  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 +418 -233
  13. data/lib/sidekiq/capsule.rb +127 -0
  14. data/lib/sidekiq/cli.rb +122 -86
  15. data/lib/sidekiq/client.rb +109 -97
  16. data/lib/sidekiq/{util.rb → component.rb} +14 -13
  17. data/lib/sidekiq/config.rb +287 -0
  18. data/lib/sidekiq/deploy.rb +62 -0
  19. data/lib/sidekiq/embedded.rb +61 -0
  20. data/lib/sidekiq/fetch.rb +43 -35
  21. data/lib/sidekiq/{worker.rb → job.rb} +161 -34
  22. data/lib/sidekiq/job_logger.rb +18 -30
  23. data/lib/sidekiq/job_retry.rb +102 -63
  24. data/lib/sidekiq/job_util.rb +107 -0
  25. data/lib/sidekiq/launcher.rb +180 -88
  26. data/lib/sidekiq/logger.rb +13 -47
  27. data/lib/sidekiq/manager.rb +40 -41
  28. data/lib/sidekiq/metrics/query.rb +155 -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 +99 -52
  32. data/lib/sidekiq/middleware/current_attributes.rb +95 -0
  33. data/lib/sidekiq/middleware/i18n.rb +6 -4
  34. data/lib/sidekiq/middleware/modules.rb +21 -0
  35. data/lib/sidekiq/monitor.rb +18 -5
  36. data/lib/sidekiq/paginator.rb +17 -9
  37. data/lib/sidekiq/processor.rb +81 -80
  38. data/lib/sidekiq/rails.rb +37 -21
  39. data/lib/sidekiq/redis_client_adapter.rb +111 -0
  40. data/lib/sidekiq/redis_connection.rb +22 -87
  41. data/lib/sidekiq/ring_buffer.rb +29 -0
  42. data/lib/sidekiq/scheduled.rb +102 -39
  43. data/lib/sidekiq/sd_notify.rb +149 -0
  44. data/lib/sidekiq/systemd.rb +24 -0
  45. data/lib/sidekiq/testing/inline.rb +4 -4
  46. data/lib/sidekiq/testing.rb +68 -78
  47. data/lib/sidekiq/transaction_aware_client.rb +44 -0
  48. data/lib/sidekiq/version.rb +2 -1
  49. data/lib/sidekiq/web/action.rb +3 -3
  50. data/lib/sidekiq/web/application.rb +132 -28
  51. data/lib/sidekiq/web/csrf_protection.rb +180 -0
  52. data/lib/sidekiq/web/helpers.rb +93 -65
  53. data/lib/sidekiq/web/router.rb +6 -5
  54. data/lib/sidekiq/web.rb +43 -74
  55. data/lib/sidekiq/worker_compatibility_alias.rb +13 -0
  56. data/lib/sidekiq.rb +86 -199
  57. data/sidekiq.gemspec +17 -8
  58. data/web/assets/images/apple-touch-icon.png +0 -0
  59. data/web/assets/javascripts/application.js +146 -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 +182 -0
  64. data/web/assets/javascripts/dashboard.js +35 -274
  65. data/web/assets/javascripts/metrics.js +298 -0
  66. data/web/assets/stylesheets/application-dark.css +146 -124
  67. data/web/assets/stylesheets/application-rtl.css +2 -95
  68. data/web/assets/stylesheets/application.css +109 -529
  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 -65
  73. data/web/locales/el.yml +43 -24
  74. data/web/locales/en.yml +85 -67
  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 -65
  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 +19 -3
  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 +73 -26
  106. data/web/views/dashboard.erb +48 -18
  107. data/web/views/dead.erb +1 -1
  108. data/web/views/filtering.erb +7 -0
  109. data/web/views/layout.erb +3 -2
  110. data/web/views/metrics.erb +91 -0
  111. data/web/views/metrics_for_job.erb +59 -0
  112. data/web/views/morgue.erb +11 -15
  113. data/web/views/queue.erb +25 -25
  114. data/web/views/queues.erb +13 -7
  115. data/web/views/retries.erb +12 -16
  116. data/web/views/retry.erb +1 -1
  117. data/web/views/scheduled.erb +13 -14
  118. metadata +65 -56
  119. data/.circleci/config.yml +0 -82
  120. data/.github/contributing.md +0 -32
  121. data/.github/issue_template.md +0 -11
  122. data/.gitignore +0 -13
  123. data/.standard.yml +0 -20
  124. data/3.0-Upgrade.md +0 -70
  125. data/4.0-Upgrade.md +0 -53
  126. data/5.0-Upgrade.md +0 -56
  127. data/6.0-Upgrade.md +0 -72
  128. data/COMM-LICENSE +0 -97
  129. data/Ent-2.0-Upgrade.md +0 -37
  130. data/Ent-Changes.md +0 -256
  131. data/Gemfile +0 -24
  132. data/Gemfile.lock +0 -199
  133. data/LICENSE +0 -9
  134. data/Pro-2.0-Upgrade.md +0 -138
  135. data/Pro-3.0-Upgrade.md +0 -44
  136. data/Pro-4.0-Upgrade.md +0 -35
  137. data/Pro-5.0-Upgrade.md +0 -25
  138. data/Pro-Changes.md +0 -776
  139. data/Rakefile +0 -10
  140. data/code_of_conduct.md +0 -50
  141. data/lib/generators/sidekiq/worker_generator.rb +0 -57
  142. data/lib/sidekiq/delay.rb +0 -41
  143. data/lib/sidekiq/exception_handler.rb +0 -27
  144. data/lib/sidekiq/extensions/action_mailer.rb +0 -47
  145. data/lib/sidekiq/extensions/active_record.rb +0 -42
  146. data/lib/sidekiq/extensions/class_methods.rb +0 -42
  147. data/lib/sidekiq/extensions/generic_proxy.rb +0 -31
@@ -0,0 +1,127 @@
1
+ require "sidekiq/component"
2
+
3
+ module Sidekiq
4
+ # A Sidekiq::Capsule is the set of resources necessary to
5
+ # process one or more queues with a given concurrency.
6
+ # One "default" Capsule is started but the user may declare additional
7
+ # Capsules in their initializer.
8
+ #
9
+ # This capsule will pull jobs from the "single" queue and process
10
+ # the jobs with one thread, meaning the jobs will be processed serially.
11
+ #
12
+ # Sidekiq.configure_server do |config|
13
+ # config.capsule("single-threaded") do |cap|
14
+ # cap.concurrency = 1
15
+ # cap.queues = %w(single)
16
+ # end
17
+ # end
18
+ class Capsule
19
+ include Sidekiq::Component
20
+
21
+ attr_reader :name
22
+ attr_reader :queues
23
+ attr_accessor :concurrency
24
+ attr_reader :mode
25
+ attr_reader :weights
26
+
27
+ def initialize(name, config)
28
+ @name = name
29
+ @config = config
30
+ @queues = ["default"]
31
+ @weights = {"default" => 0}
32
+ @concurrency = config[:concurrency]
33
+ @mode = :strict
34
+ end
35
+
36
+ def fetcher
37
+ @fetcher ||= begin
38
+ inst = (config[:fetch_class] || Sidekiq::BasicFetch).new(self)
39
+ inst.setup(config[:fetch_setup]) if inst.respond_to?(:setup)
40
+ inst
41
+ end
42
+ end
43
+
44
+ def stop
45
+ fetcher&.bulk_requeue([])
46
+ end
47
+
48
+ # Sidekiq checks queues in three modes:
49
+ # - :strict - all queues have 0 weight and are checked strictly in order
50
+ # - :weighted - queues have arbitrary weight between 1 and N
51
+ # - :random - all queues have weight of 1
52
+ def queues=(val)
53
+ @weights = {}
54
+ @queues = Array(val).each_with_object([]) do |qstr, memo|
55
+ arr = qstr
56
+ arr = qstr.split(",") if qstr.is_a?(String)
57
+ name, weight = arr
58
+ @weights[name] = weight.to_i
59
+ [weight.to_i, 1].max.times do
60
+ memo << name
61
+ end
62
+ end
63
+ @mode = if @weights.values.all?(&:zero?)
64
+ :strict
65
+ elsif @weights.values.all? { |x| x == 1 }
66
+ :random
67
+ else
68
+ :weighted
69
+ end
70
+ end
71
+
72
+ # Allow the middleware to be different per-capsule.
73
+ # Avoid if possible and add middleware globally so all
74
+ # capsules share the same chains. Easier to debug that way.
75
+ def client_middleware
76
+ @client_chain ||= config.client_middleware.copy_for(self)
77
+ yield @client_chain if block_given?
78
+ @client_chain
79
+ end
80
+
81
+ def server_middleware
82
+ @server_chain ||= config.server_middleware.copy_for(self)
83
+ yield @server_chain if block_given?
84
+ @server_chain
85
+ end
86
+
87
+ def redis_pool
88
+ Thread.current[:sidekiq_redis_pool] || local_redis_pool
89
+ end
90
+
91
+ def local_redis_pool
92
+ # connection pool is lazy, it will not create connections unless you actually need them
93
+ # so don't be skimpy!
94
+ @redis ||= config.new_redis_pool(@concurrency, name)
95
+ end
96
+
97
+ def redis
98
+ raise ArgumentError, "requires a block" unless block_given?
99
+ redis_pool.with do |conn|
100
+ retryable = true
101
+ begin
102
+ yield conn
103
+ rescue RedisClientAdapter::BaseError => ex
104
+ # 2550 Failover can cause the server to become a replica, need
105
+ # to disconnect and reopen the socket to get back to the primary.
106
+ # 4495 Use the same logic if we have a "Not enough replicas" error from the primary
107
+ # 4985 Use the same logic when a blocking command is force-unblocked
108
+ # The same retry logic is also used in client.rb
109
+ if retryable && ex.message =~ /READONLY|NOREPLICAS|UNBLOCKED/
110
+ conn.close
111
+ retryable = false
112
+ retry
113
+ end
114
+ raise
115
+ end
116
+ end
117
+ end
118
+
119
+ def lookup(name)
120
+ config.lookup(name)
121
+ end
122
+
123
+ def logger
124
+ config.logger
125
+ end
126
+ end
127
+ end
data/lib/sidekiq/cli.rb CHANGED
@@ -9,18 +9,23 @@ require "erb"
9
9
  require "fileutils"
10
10
 
11
11
  require "sidekiq"
12
+ require "sidekiq/config"
13
+ require "sidekiq/component"
14
+ require "sidekiq/capsule"
12
15
  require "sidekiq/launcher"
13
- require "sidekiq/util"
14
16
 
15
- module Sidekiq
17
+ module Sidekiq # :nodoc:
16
18
  class CLI
17
- include Util
19
+ include Sidekiq::Component
18
20
  include Singleton unless $TESTING
19
21
 
20
22
  attr_accessor :launcher
21
23
  attr_accessor :environment
24
+ attr_accessor :config
25
+
26
+ def parse(args = ARGV.dup)
27
+ @config ||= Sidekiq.default_configuration
22
28
 
23
- def parse(args = ARGV)
24
29
  setup_options(args)
25
30
  initialize_logger
26
31
  validate!
@@ -33,9 +38,10 @@ module Sidekiq
33
38
  # Code within this method is not tested because it alters
34
39
  # global process state irreversibly. PRs which improve the
35
40
  # test coverage of Sidekiq::CLI are welcomed.
36
- def run
37
- boot_system
38
- if environment == "development" && $stdout.tty? && Sidekiq.log_formatter.is_a?(Sidekiq::Logger::Formatters::Pretty)
41
+ def run(boot_app: true)
42
+ boot_application if boot_app
43
+
44
+ if environment == "development" && $stdout.tty? && @config.logger.formatter.is_a?(Sidekiq::Logger::Formatters::Pretty)
39
45
  print_banner
40
46
  end
41
47
  logger.info "Booted Rails #{::Rails.version} application in #{environment} environment" if rails_app?
@@ -43,9 +49,17 @@ module Sidekiq
43
49
  self_read, self_write = IO.pipe
44
50
  sigs = %w[INT TERM TTIN TSTP]
45
51
  # USR1 and USR2 don't work on the JVM
46
- sigs << "USR2" unless jruby?
52
+ sigs << "USR2" if Sidekiq.pro? && !jruby?
47
53
  sigs.each do |sig|
48
- trap sig do
54
+ old_handler = Signal.trap(sig) do
55
+ if old_handler.respond_to?(:call)
56
+ begin
57
+ old_handler.call
58
+ rescue Exception => exc
59
+ # signal handlers can't use Logger so puts only
60
+ puts ["Error in #{sig} handler", exc].inspect
61
+ end
62
+ end
49
63
  self_write.puts(sig)
50
64
  end
51
65
  rescue ArgumentError
@@ -54,31 +68,45 @@ module Sidekiq
54
68
 
55
69
  logger.info "Running in #{RUBY_DESCRIPTION}"
56
70
  logger.info Sidekiq::LICENSE
57
- logger.info "Upgrade to Sidekiq Pro for more features and support: http://sidekiq.org" unless defined?(::Sidekiq::Pro)
71
+ logger.info "Upgrade to Sidekiq Pro for more features and support: https://sidekiq.org" unless defined?(::Sidekiq::Pro)
58
72
 
59
73
  # touch the connection pool so it is created before we
60
74
  # fire startup and start multithreading.
61
- ver = Sidekiq.redis_info["redis_version"]
62
- raise "You are connecting to Redis v#{ver}, Sidekiq requires Redis v4.0.0 or greater" if ver < "4"
75
+ info = @config.redis_info
76
+ ver = Gem::Version.new(info["redis_version"])
77
+ raise "You are connecting to Redis #{ver}, Sidekiq requires Redis 6.2.0 or greater" if ver < Gem::Version.new("6.2.0")
78
+
79
+ maxmemory_policy = info["maxmemory_policy"]
80
+ if maxmemory_policy != "noeviction" && maxmemory_policy != ""
81
+ # Redis Enterprise Cloud returns "" for their policy 😳
82
+ logger.warn <<~EOM
83
+
84
+
85
+ WARNING: Your Redis instance will evict Sidekiq data under heavy load.
86
+ The 'noeviction' maxmemory policy is recommended (current policy: '#{maxmemory_policy}').
87
+ See: https://github.com/sidekiq/sidekiq/wiki/Using-Redis#memory
88
+
89
+ EOM
90
+ end
63
91
 
64
92
  # Since the user can pass us a connection pool explicitly in the initializer, we
65
93
  # need to verify the size is large enough or else Sidekiq's performance is dramatically slowed.
66
- cursize = Sidekiq.redis_pool.size
67
- needed = Sidekiq.options[:concurrency] + 2
68
- raise "Your pool of #{cursize} Redis connections is too small, please increase the size to at least #{needed}" if cursize < needed
94
+ @config.capsules.each_pair do |name, cap|
95
+ raise ArgumentError, "Pool size too small for #{name}" if cap.redis_pool.size < cap.concurrency
96
+ end
69
97
 
70
98
  # cache process identity
71
- Sidekiq.options[:identity] = identity
99
+ @config[:identity] = identity
72
100
 
73
101
  # Touch middleware so it isn't lazy loaded by multiple threads, #3043
74
- Sidekiq.server_middleware
102
+ @config.server_middleware
75
103
 
76
104
  # Before this point, the process is initializing with just the main thread.
77
105
  # Starting here the process will now have multiple threads running.
78
106
  fire_event(:startup, reverse: false, reraise: true)
79
107
 
80
- logger.debug { "Client Middleware: #{Sidekiq.client_middleware.map(&:klass).join(", ")}" }
81
- logger.debug { "Server Middleware: #{Sidekiq.server_middleware.map(&:klass).join(", ")}" }
108
+ logger.debug { "Client Middleware: #{@config.default_capsule.client_middleware.map(&:klass).join(", ")}" }
109
+ logger.debug { "Server Middleware: #{@config.default_capsule.server_middleware.map(&:klass).join(", ")}" }
82
110
 
83
111
  launch(self_read)
84
112
  end
@@ -88,13 +116,13 @@ module Sidekiq
88
116
  logger.info "Starting processing, hit Ctrl-C to stop"
89
117
  end
90
118
 
91
- @launcher = Sidekiq::Launcher.new(options)
119
+ @launcher = Sidekiq::Launcher.new(@config)
92
120
 
93
121
  begin
94
122
  launcher.run
95
123
 
96
- while (readable_io = IO.select([self_read]))
97
- signal = readable_io.first[0].gets.strip
124
+ while self_read.wait_readable
125
+ signal = self_read.gets.strip
98
126
  handle_signal(signal)
99
127
  end
100
128
  rescue Interrupt
@@ -111,19 +139,34 @@ module Sidekiq
111
139
  end
112
140
  end
113
141
 
114
- def self.w
115
- "\e[37m"
142
+ HOLIDAY_COLORS = {
143
+ # got other color-specific holidays from around the world?
144
+ # https://developer-book.com/post/definitive-guide-for-colored-text-in-terminal/#256-color-escape-codes
145
+ "3-17" => "\e[1;32m", # St. Patrick's Day green
146
+ "10-31" => "\e[38;5;208m" # Halloween orange
147
+ }
148
+
149
+ def self.day
150
+ @@day ||= begin
151
+ t = Date.today
152
+ "#{t.month}-#{t.day}"
153
+ end
116
154
  end
117
155
 
118
156
  def self.r
119
- "\e[31m"
157
+ @@r ||= HOLIDAY_COLORS[day] || "\e[1;31m"
120
158
  end
121
159
 
122
160
  def self.b
123
- "\e[30m"
161
+ @@b ||= HOLIDAY_COLORS[day] || "\e[30m"
162
+ end
163
+
164
+ def self.w
165
+ "\e[1;37m"
124
166
  end
125
167
 
126
168
  def self.reset
169
+ @@b = @@r = @@day = nil
127
170
  "\e[0m"
128
171
  end
129
172
 
@@ -136,7 +179,7 @@ module Sidekiq
136
179
  #{w} ,$$$$$b#{b}/#{w}md$$$P^'
137
180
  #{w} .d$$$$$$#{b}/#{w}$$$P'
138
181
  #{w} $$^' `"#{b}/#{w}$$$' #{r}____ _ _ _ _
139
- #{w} $: ,$$: #{r} / ___|(_) __| | ___| | _(_) __ _
182
+ #{w} $: #{b}'#{w},$$: #{r} / ___|(_) __| | ___| | _(_) __ _
140
183
  #{w} `b :$$ #{r} \\___ \\| |/ _` |/ _ \\ |/ / |/ _` |
141
184
  #{w} $$: #{r} ___) | | (_| | __/ <| | (_| |
142
185
  #{w} $$ #{r}|____/|_|\\__,_|\\___|_|\\_\\_|\\__, |
@@ -151,25 +194,25 @@ module Sidekiq
151
194
  # Heroku sends TERM and then waits 30 seconds for process to exit.
152
195
  "TERM" => ->(cli) { raise Interrupt },
153
196
  "TSTP" => ->(cli) {
154
- Sidekiq.logger.info "Received TSTP, no longer accepting new work"
197
+ cli.logger.info "Received TSTP, no longer accepting new work"
155
198
  cli.launcher.quiet
156
199
  },
157
200
  "TTIN" => ->(cli) {
158
201
  Thread.list.each do |thread|
159
- Sidekiq.logger.warn "Thread TID-#{(thread.object_id ^ ::Process.pid).to_s(36)} #{thread.name}"
202
+ cli.logger.warn "Thread TID-#{(thread.object_id ^ ::Process.pid).to_s(36)} #{thread.name}"
160
203
  if thread.backtrace
161
- Sidekiq.logger.warn thread.backtrace.join("\n")
204
+ cli.logger.warn thread.backtrace.join("\n")
162
205
  else
163
- Sidekiq.logger.warn "<no backtrace available>"
206
+ cli.logger.warn "<no backtrace available>"
164
207
  end
165
208
  end
166
- },
209
+ }
167
210
  }
168
- UNHANDLED_SIGNAL_HANDLER = ->(cli) { Sidekiq.logger.info "No signal handler registered, ignoring" }
211
+ UNHANDLED_SIGNAL_HANDLER = ->(cli) { cli.logger.info "No signal handler registered, ignoring" }
169
212
  SIGNAL_HANDLERS.default = UNHANDLED_SIGNAL_HANDLER
170
213
 
171
214
  def handle_signal(sig)
172
- Sidekiq.logger.debug "Got #{sig} signal"
215
+ logger.debug "Got #{sig} signal"
173
216
  SIGNAL_HANDLERS[sig].call(self)
174
217
  end
175
218
 
@@ -182,7 +225,12 @@ module Sidekiq
182
225
  end
183
226
 
184
227
  def set_environment(cli_env)
185
- @environment = cli_env || ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
228
+ # See #984 for discussion.
229
+ # APP_ENV is now the preferred ENV term since it is not tech-specific.
230
+ # Both Sinatra 2.0+ and Sidekiq support this term.
231
+ # RAILS_ENV and RACK_ENV are there for legacy support.
232
+ @environment = cli_env || ENV["APP_ENV"] || ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
233
+ config[:environment] = @environment
186
234
  end
187
235
 
188
236
  def symbolize_keys_deep!(hash)
@@ -211,7 +259,7 @@ module Sidekiq
211
259
  config_dir = if File.directory?(opts[:require].to_s)
212
260
  File.join(opts[:require], "config")
213
261
  else
214
- File.join(options[:require], "config")
262
+ File.join(@config[:require], "config")
215
263
  end
216
264
 
217
265
  %w[sidekiq.yml sidekiq.yml.erb].each do |config_file|
@@ -224,32 +272,38 @@ module Sidekiq
224
272
  opts = parse_config(opts[:config_file]).merge(opts) if opts[:config_file]
225
273
 
226
274
  # set defaults
227
- opts[:queues] = ["default"] if opts[:queues].nil? || opts[:queues].empty?
228
- opts[:strict] = true if opts[:strict].nil?
275
+ opts[:queues] = ["default"] if opts[:queues].nil?
229
276
  opts[:concurrency] = Integer(ENV["RAILS_MAX_THREADS"]) if opts[:concurrency].nil? && ENV["RAILS_MAX_THREADS"]
230
277
 
231
278
  # merge with defaults
232
- options.merge!(opts)
233
- end
279
+ @config.merge!(opts)
280
+
281
+ @config.default_capsule.tap do |cap|
282
+ cap.queues = opts[:queues]
283
+ cap.concurrency = opts[:concurrency] || @config[:concurrency]
284
+ end
234
285
 
235
- def options
236
- Sidekiq.options
286
+ opts[:capsules]&.each do |name, cap_config|
287
+ @config.capsule(name.to_s) do |cap|
288
+ cap.queues = cap_config[:queues]
289
+ cap.concurrency = cap_config[:concurrency]
290
+ end
291
+ end
237
292
  end
238
293
 
239
- def boot_system
294
+ def boot_application
240
295
  ENV["RACK_ENV"] = ENV["RAILS_ENV"] = environment
241
296
 
242
- if File.directory?(options[:require])
297
+ if File.directory?(@config[:require])
243
298
  require "rails"
244
- if ::Rails::VERSION::MAJOR < 5
245
- raise "Sidekiq no longer supports this version of Rails"
246
- else
247
- require "sidekiq/rails"
248
- require File.expand_path("#{options[:require]}/config/environment.rb")
299
+ if ::Rails::VERSION::MAJOR < 6
300
+ warn "Sidekiq #{Sidekiq::VERSION} only supports Rails 6+"
249
301
  end
250
- options[:tag] ||= default_tag
302
+ require "sidekiq/rails"
303
+ require File.expand_path("#{@config[:require]}/config/environment.rb")
304
+ @config[:tag] ||= default_tag
251
305
  else
252
- require options[:require]
306
+ require @config[:require]
253
307
  end
254
308
  end
255
309
 
@@ -266,18 +320,18 @@ module Sidekiq
266
320
  end
267
321
 
268
322
  def validate!
269
- if !File.exist?(options[:require]) ||
270
- (File.directory?(options[:require]) && !File.exist?("#{options[:require]}/config/application.rb"))
323
+ if !File.exist?(@config[:require]) ||
324
+ (File.directory?(@config[:require]) && !File.exist?("#{@config[:require]}/config/application.rb"))
271
325
  logger.info "=================================================================="
272
326
  logger.info " Please point Sidekiq to a Rails application or a Ruby file "
273
- logger.info " to load your worker classes with -r [DIR|FILE]."
327
+ logger.info " to load your job classes with -r [DIR|FILE]."
274
328
  logger.info "=================================================================="
275
329
  logger.info @parser
276
330
  die(1)
277
331
  end
278
332
 
279
333
  [:concurrency, :timeout].each do |opt|
280
- raise ArgumentError, "#{opt}: #{options[opt]} is not a valid value" if options.key?(opt) && options[opt].to_i <= 0
334
+ raise ArgumentError, "#{opt}: #{@config[opt]} is not a valid value" if @config[opt].to_i <= 0
281
335
  end
282
336
  end
283
337
 
@@ -294,10 +348,6 @@ module Sidekiq
294
348
  opts[:concurrency] = Integer(arg)
295
349
  end
296
350
 
297
- o.on "-d", "--daemon", "Daemonize process" do |arg|
298
- puts "ERROR: Daemonization mode was removed in Sidekiq 6.0, please use a proper process supervisor to start and manage your services"
299
- end
300
-
301
351
  o.on "-e", "--environment ENV", "Application environment" do |arg|
302
352
  opts[:environment] = arg
303
353
  end
@@ -307,11 +357,11 @@ module Sidekiq
307
357
  end
308
358
 
309
359
  o.on "-q", "--queue QUEUE[,WEIGHT]", "Queues to process with optional weights" do |arg|
310
- queue, weight = arg.split(",")
311
- parse_queue opts, queue, weight
360
+ opts[:queues] ||= []
361
+ opts[:queues] << arg
312
362
  end
313
363
 
314
- o.on "-r", "--require [PATH|DIR]", "Location of Rails application with workers or file to require" do |arg|
364
+ o.on "-r", "--require [PATH|DIR]", "Location of Rails application with jobs or file to require" do |arg|
315
365
  opts[:require] = arg
316
366
  end
317
367
 
@@ -327,15 +377,7 @@ module Sidekiq
327
377
  opts[:config_file] = arg
328
378
  end
329
379
 
330
- o.on "-L", "--logfile PATH", "path to writable logfile" do |arg|
331
- puts "ERROR: Logfile redirection was removed in Sidekiq 6.0, Sidekiq will only log to STDOUT"
332
- end
333
-
334
- o.on "-P", "--pidfile PATH", "path to pidfile" do |arg|
335
- puts "ERROR: PID file creation was removed in Sidekiq 6.0, please use a proper process supervisor to start and manage your services"
336
- end
337
-
338
- o.on "-V", "--version", "Print version and exit" do |arg|
380
+ o.on "-V", "--version", "Print version and exit" do
339
381
  puts "Sidekiq #{Sidekiq::VERSION}"
340
382
  die(0)
341
383
  end
@@ -351,11 +393,13 @@ module Sidekiq
351
393
  end
352
394
 
353
395
  def initialize_logger
354
- Sidekiq.logger.level = ::Logger::DEBUG if options[:verbose]
396
+ @config.logger.level = ::Logger::DEBUG if @config[:verbose]
355
397
  end
356
398
 
357
399
  def parse_config(path)
358
- opts = YAML.load(ERB.new(File.read(path)).result) || {}
400
+ erb = ERB.new(File.read(path), trim_mode: "-")
401
+ erb.filename = File.expand_path(path)
402
+ opts = YAML.safe_load(erb.result, permitted_classes: [Symbol], aliases: true) || {}
359
403
 
360
404
  if opts.respond_to? :deep_symbolize_keys!
361
405
  opts.deep_symbolize_keys!
@@ -364,24 +408,16 @@ module Sidekiq
364
408
  end
365
409
 
366
410
  opts = opts.merge(opts.delete(environment.to_sym) || {})
367
- parse_queues(opts, opts.delete(:queues) || [])
411
+ opts.delete(:strict)
368
412
 
369
413
  opts
370
414
  end
371
415
 
372
- def parse_queues(opts, queues_and_weights)
373
- queues_and_weights.each { |queue_and_weight| parse_queue(opts, *queue_and_weight) }
374
- end
375
-
376
- def parse_queue(opts, queue, weight = nil)
377
- opts[:queues] ||= []
378
- raise ArgumentError, "queues: #{queue} cannot be defined twice" if opts[:queues].include?(queue)
379
- [weight.to_i, 1].max.times { opts[:queues] << queue }
380
- opts[:strict] = false if weight.to_i > 0
381
- end
382
-
383
416
  def rails_app?
384
- defined?(::Rails)
417
+ defined?(::Rails) && ::Rails.respond_to?(:application)
385
418
  end
386
419
  end
387
420
  end
421
+
422
+ require "sidekiq/systemd"
423
+ require "sidekiq/metrics/tracking"