sidekiq 5.2.10 → 6.5.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (126) hide show
  1. checksums.yaml +4 -4
  2. data/Changes.md +422 -1
  3. data/LICENSE +3 -3
  4. data/README.md +24 -35
  5. data/bin/sidekiq +27 -3
  6. data/bin/sidekiqload +79 -67
  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 +527 -310
  13. data/lib/sidekiq/cli.rb +204 -208
  14. data/lib/sidekiq/client.rb +78 -82
  15. data/lib/sidekiq/component.rb +65 -0
  16. data/lib/sidekiq/delay.rb +8 -7
  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 +7 -5
  21. data/lib/sidekiq/fetch.rb +50 -40
  22. data/lib/sidekiq/job.rb +13 -0
  23. data/lib/sidekiq/job_logger.rb +33 -7
  24. data/lib/sidekiq/job_retry.rb +126 -106
  25. data/lib/sidekiq/job_util.rb +71 -0
  26. data/lib/sidekiq/launcher.rb +177 -83
  27. data/lib/sidekiq/logger.rb +156 -0
  28. data/lib/sidekiq/manager.rb +40 -41
  29. data/lib/sidekiq/metrics/deploy.rb +47 -0
  30. data/lib/sidekiq/metrics/query.rb +153 -0
  31. data/lib/sidekiq/metrics/shared.rb +94 -0
  32. data/lib/sidekiq/metrics/tracking.rb +134 -0
  33. data/lib/sidekiq/middleware/chain.rb +102 -46
  34. data/lib/sidekiq/middleware/current_attributes.rb +63 -0
  35. data/lib/sidekiq/middleware/i18n.rb +7 -7
  36. data/lib/sidekiq/middleware/modules.rb +21 -0
  37. data/lib/sidekiq/monitor.rb +133 -0
  38. data/lib/sidekiq/paginator.rb +28 -16
  39. data/lib/sidekiq/processor.rb +104 -97
  40. data/lib/sidekiq/rails.rb +46 -37
  41. data/lib/sidekiq/redis_client_adapter.rb +154 -0
  42. data/lib/sidekiq/redis_connection.rb +108 -77
  43. data/lib/sidekiq/ring_buffer.rb +29 -0
  44. data/lib/sidekiq/scheduled.rb +105 -42
  45. data/lib/sidekiq/sd_notify.rb +149 -0
  46. data/lib/sidekiq/systemd.rb +24 -0
  47. data/lib/sidekiq/testing/inline.rb +6 -5
  48. data/lib/sidekiq/testing.rb +68 -58
  49. data/lib/sidekiq/transaction_aware_client.rb +45 -0
  50. data/lib/sidekiq/version.rb +2 -1
  51. data/lib/sidekiq/web/action.rb +15 -11
  52. data/lib/sidekiq/web/application.rb +103 -77
  53. data/lib/sidekiq/web/csrf_protection.rb +180 -0
  54. data/lib/sidekiq/web/helpers.rb +125 -95
  55. data/lib/sidekiq/web/router.rb +23 -19
  56. data/lib/sidekiq/web.rb +65 -105
  57. data/lib/sidekiq/worker.rb +259 -109
  58. data/lib/sidekiq.rb +170 -62
  59. data/sidekiq.gemspec +23 -16
  60. data/web/assets/images/apple-touch-icon.png +0 -0
  61. data/web/assets/javascripts/application.js +113 -61
  62. data/web/assets/javascripts/chart.min.js +13 -0
  63. data/web/assets/javascripts/chartjs-plugin-annotation.min.js +7 -0
  64. data/web/assets/javascripts/dashboard.js +53 -89
  65. data/web/assets/javascripts/graph.js +16 -0
  66. data/web/assets/javascripts/metrics.js +262 -0
  67. data/web/assets/stylesheets/application-dark.css +143 -0
  68. data/web/assets/stylesheets/application-rtl.css +0 -4
  69. data/web/assets/stylesheets/application.css +88 -233
  70. data/web/locales/ar.yml +8 -2
  71. data/web/locales/de.yml +14 -2
  72. data/web/locales/el.yml +43 -19
  73. data/web/locales/en.yml +13 -1
  74. data/web/locales/es.yml +18 -2
  75. data/web/locales/fr.yml +10 -3
  76. data/web/locales/ja.yml +14 -1
  77. data/web/locales/lt.yml +83 -0
  78. data/web/locales/pl.yml +4 -4
  79. data/web/locales/pt-br.yml +27 -9
  80. data/web/locales/ru.yml +4 -0
  81. data/web/locales/vi.yml +83 -0
  82. data/web/locales/zh-cn.yml +36 -11
  83. data/web/locales/zh-tw.yml +32 -7
  84. data/web/views/_footer.erb +1 -1
  85. data/web/views/_job_info.erb +3 -2
  86. data/web/views/_nav.erb +1 -1
  87. data/web/views/_poll_link.erb +2 -5
  88. data/web/views/_summary.erb +7 -7
  89. data/web/views/busy.erb +61 -22
  90. data/web/views/dashboard.erb +23 -14
  91. data/web/views/dead.erb +3 -3
  92. data/web/views/layout.erb +3 -1
  93. data/web/views/metrics.erb +69 -0
  94. data/web/views/metrics_for_job.erb +87 -0
  95. data/web/views/morgue.erb +9 -6
  96. data/web/views/queue.erb +23 -10
  97. data/web/views/queues.erb +10 -2
  98. data/web/views/retries.erb +11 -8
  99. data/web/views/retry.erb +3 -3
  100. data/web/views/scheduled.erb +5 -2
  101. metadata +58 -63
  102. data/.circleci/config.yml +0 -61
  103. data/.github/contributing.md +0 -32
  104. data/.github/issue_template.md +0 -11
  105. data/.gitignore +0 -15
  106. data/.travis.yml +0 -11
  107. data/3.0-Upgrade.md +0 -70
  108. data/4.0-Upgrade.md +0 -53
  109. data/5.0-Upgrade.md +0 -56
  110. data/COMM-LICENSE +0 -97
  111. data/Ent-Changes.md +0 -238
  112. data/Gemfile +0 -19
  113. data/Pro-2.0-Upgrade.md +0 -138
  114. data/Pro-3.0-Upgrade.md +0 -44
  115. data/Pro-4.0-Upgrade.md +0 -35
  116. data/Pro-Changes.md +0 -759
  117. data/Rakefile +0 -9
  118. data/bin/sidekiqctl +0 -20
  119. data/code_of_conduct.md +0 -50
  120. data/lib/generators/sidekiq/worker_generator.rb +0 -49
  121. data/lib/sidekiq/core_ext.rb +0 -1
  122. data/lib/sidekiq/ctl.rb +0 -221
  123. data/lib/sidekiq/exception_handler.rb +0 -29
  124. data/lib/sidekiq/logging.rb +0 -122
  125. data/lib/sidekiq/middleware/server/active_record.rb +0 -23
  126. data/lib/sidekiq/util.rb +0 -66
data/lib/sidekiq/cli.rb CHANGED
@@ -1,33 +1,42 @@
1
1
  # frozen_string_literal: true
2
- $stdout.sync = true
3
-
4
- require 'yaml'
5
- require 'singleton'
6
- require 'optparse'
7
- require 'erb'
8
- require 'fileutils'
9
2
 
10
- require 'sidekiq'
11
- require 'sidekiq/util'
12
- require 'sidekiq/launcher'
3
+ $stdout.sync = true
13
4
 
14
- module Sidekiq
5
+ require "yaml"
6
+ require "singleton"
7
+ require "optparse"
8
+ require "erb"
9
+ require "fileutils"
10
+
11
+ require "sidekiq"
12
+ require "sidekiq/component"
13
+ require "sidekiq/launcher"
14
+
15
+ # module ScoutApm
16
+ # VERSION = "5.3.1"
17
+ # end
18
+ fail <<~EOM if defined?(ScoutApm::VERSION) && ScoutApm::VERSION < "5.2.0"
19
+
20
+
21
+ scout_apm v#{ScoutApm::VERSION} is unsafe with Sidekiq 6.5. Please run `bundle up scout_apm` to upgrade to 5.2.0 or greater.
22
+
23
+
24
+ EOM
25
+
26
+ module Sidekiq # :nodoc:
15
27
  class CLI
16
- include Util
28
+ include Sidekiq::Component
17
29
  include Singleton unless $TESTING
18
30
 
19
- PROCTITLES = [
20
- proc { 'sidekiq' },
21
- proc { Sidekiq::VERSION },
22
- proc { |me, data| data['tag'] },
23
- proc { |me, data| "[#{Processor::WORKER_STATE.size} of #{data['concurrency']} busy]" },
24
- proc { |me, data| "stopping" if me.stopping? },
25
- ]
26
-
27
31
  attr_accessor :launcher
28
32
  attr_accessor :environment
33
+ attr_accessor :config
34
+
35
+ def parse(args = ARGV.dup)
36
+ @config = Sidekiq
37
+ @config[:error_handlers].clear
38
+ @config[:error_handlers] << @config.method(:default_error_handler)
29
39
 
30
- def parse(args = ARGV)
31
40
  setup_options(args)
32
41
  initialize_logger
33
42
  validate!
@@ -40,187 +49,190 @@ module Sidekiq
40
49
  # Code within this method is not tested because it alters
41
50
  # global process state irreversibly. PRs which improve the
42
51
  # test coverage of Sidekiq::CLI are welcomed.
43
- def run
44
- daemonize if options[:daemon]
45
- write_pid
46
- boot_system
47
- print_banner if environment == 'development' && $stdout.tty?
52
+ def run(boot_app: true)
53
+ boot_application if boot_app
48
54
 
49
- self_read, self_write = IO.pipe
50
- sigs = %w(INT TERM TTIN TSTP)
51
- # USR1 and USR2 don't work on the JVM
52
- if !jruby?
53
- sigs << 'USR1'
54
- sigs << 'USR2'
55
+ if environment == "development" && $stdout.tty? && @config.log_formatter.is_a?(Sidekiq::Logger::Formatters::Pretty)
56
+ print_banner
55
57
  end
58
+ logger.info "Booted Rails #{::Rails.version} application in #{environment} environment" if rails_app?
56
59
 
60
+ self_read, self_write = IO.pipe
61
+ sigs = %w[INT TERM TTIN TSTP]
62
+ # USR1 and USR2 don't work on the JVM
63
+ sigs << "USR2" if Sidekiq.pro? && !jruby?
57
64
  sigs.each do |sig|
58
- begin
59
- trap sig do
60
- self_write.write("#{sig}\n")
65
+ old_handler = Signal.trap(sig) do
66
+ if old_handler.respond_to?(:call)
67
+ begin
68
+ old_handler.call
69
+ rescue Exception => exc
70
+ # signal handlers can't use Logger so puts only
71
+ puts ["Error in #{sig} handler", exc].inspect
72
+ end
61
73
  end
62
- rescue ArgumentError
63
- puts "Signal #{sig} not supported"
74
+ self_write.puts(sig)
64
75
  end
76
+ rescue ArgumentError
77
+ puts "Signal #{sig} not supported"
65
78
  end
66
79
 
67
80
  logger.info "Running in #{RUBY_DESCRIPTION}"
68
81
  logger.info Sidekiq::LICENSE
69
- logger.info "Upgrade to Sidekiq Pro for more features and support: http://sidekiq.org" unless defined?(::Sidekiq::Pro)
82
+ logger.info "Upgrade to Sidekiq Pro for more features and support: https://sidekiq.org" unless defined?(::Sidekiq::Pro)
70
83
 
71
84
  # touch the connection pool so it is created before we
72
85
  # fire startup and start multithreading.
73
- ver = Sidekiq.redis_info['redis_version']
74
- raise "You are using Redis v#{ver}, Sidekiq requires Redis v2.8.0 or greater" if ver < '2.8'
75
- logger.warn "Sidekiq 6.0 will require Redis 4.0+, you are using Redis v#{ver}" if ver < '4'
86
+ info = @config.redis_info
87
+ ver = info["redis_version"]
88
+ raise "You are connecting to Redis v#{ver}, Sidekiq requires Redis v4.0.0 or greater" if ver < "4"
89
+
90
+ maxmemory_policy = info["maxmemory_policy"]
91
+ if maxmemory_policy != "noeviction"
92
+ logger.warn <<~EOM
93
+
94
+
95
+ WARNING: Your Redis instance will evict Sidekiq data under heavy load.
96
+ The 'noeviction' maxmemory policy is recommended (current policy: '#{maxmemory_policy}').
97
+ See: https://github.com/mperham/sidekiq/wiki/Using-Redis#memory
98
+
99
+ EOM
100
+ end
76
101
 
77
102
  # Since the user can pass us a connection pool explicitly in the initializer, we
78
103
  # need to verify the size is large enough or else Sidekiq's performance is dramatically slowed.
79
- cursize = Sidekiq.redis_pool.size
80
- needed = Sidekiq.options[:concurrency] + 2
104
+ cursize = @config.redis_pool.size
105
+ needed = @config[:concurrency] + 2
81
106
  raise "Your pool of #{cursize} Redis connections is too small, please increase the size to at least #{needed}" if cursize < needed
82
107
 
83
108
  # cache process identity
84
- Sidekiq.options[:identity] = identity
109
+ @config[:identity] = identity
85
110
 
86
111
  # Touch middleware so it isn't lazy loaded by multiple threads, #3043
87
- Sidekiq.server_middleware
112
+ @config.server_middleware
88
113
 
89
114
  # Before this point, the process is initializing with just the main thread.
90
115
  # Starting here the process will now have multiple threads running.
91
116
  fire_event(:startup, reverse: false, reraise: true)
92
117
 
93
- logger.debug { "Client Middleware: #{Sidekiq.client_middleware.map(&:klass).join(', ')}" }
94
- logger.debug { "Server Middleware: #{Sidekiq.server_middleware.map(&:klass).join(', ')}" }
118
+ logger.debug { "Client Middleware: #{@config.client_middleware.map(&:klass).join(", ")}" }
119
+ logger.debug { "Server Middleware: #{@config.server_middleware.map(&:klass).join(", ")}" }
95
120
 
96
121
  launch(self_read)
97
122
  end
98
123
 
99
124
  def launch(self_read)
100
- if !options[:daemon]
101
- logger.info 'Starting processing, hit Ctrl-C to stop'
125
+ if environment == "development" && $stdout.tty?
126
+ logger.info "Starting processing, hit Ctrl-C to stop"
102
127
  end
103
128
 
104
- @launcher = Sidekiq::Launcher.new(options)
129
+ @launcher = Sidekiq::Launcher.new(@config)
105
130
 
106
131
  begin
107
132
  launcher.run
108
133
 
109
- while readable_io = IO.select([self_read])
110
- signal = readable_io.first[0].gets.strip
134
+ while self_read.wait_readable
135
+ signal = self_read.gets.strip
111
136
  handle_signal(signal)
112
137
  end
113
138
  rescue Interrupt
114
- logger.info 'Shutting down'
139
+ logger.info "Shutting down"
115
140
  launcher.stop
116
- # Explicitly exit so busy Processor threads can't block
117
- # process shutdown.
118
141
  logger.info "Bye!"
142
+
143
+ # Explicitly exit so busy Processor threads won't block process shutdown.
144
+ #
145
+ # NB: slow at_exit handlers will prevent a timely exit if they take
146
+ # a while to run. If Sidekiq is getting here but the process isn't exiting,
147
+ # use the TTIN signal to determine where things are stuck.
119
148
  exit(0)
120
149
  end
121
150
  end
122
151
 
152
+ def self.w
153
+ "\e[37m"
154
+ end
155
+
156
+ def self.r
157
+ "\e[31m"
158
+ end
159
+
160
+ def self.b
161
+ "\e[30m"
162
+ end
163
+
164
+ def self.reset
165
+ "\e[0m"
166
+ end
167
+
123
168
  def self.banner
124
- %q{
125
- m,
126
- `$b
127
- .ss, $$: .,d$
128
- `$$P,d$P' .,md$P"'
129
- ,$$$$$bmmd$$$P^'
130
- .d$$$$$$$$$$P'
131
- $$^' `"^$$$' ____ _ _ _ _
132
- $: ,$$: / ___|(_) __| | ___| | _(_) __ _
133
- `b :$$ \___ \| |/ _` |/ _ \ |/ / |/ _` |
134
- $$: ___) | | (_| | __/ <| | (_| |
135
- $$ |____/|_|\__,_|\___|_|\_\_|\__, |
136
- .d$$ |_|
137
- }
169
+ %{
170
+ #{w} m,
171
+ #{w} `$b
172
+ #{w} .ss, $$: .,d$
173
+ #{w} `$$P,d$P' .,md$P"'
174
+ #{w} ,$$$$$b#{b}/#{w}md$$$P^'
175
+ #{w} .d$$$$$$#{b}/#{w}$$$P'
176
+ #{w} $$^' `"#{b}/#{w}$$$' #{r}____ _ _ _ _
177
+ #{w} $: ,$$: #{r} / ___|(_) __| | ___| | _(_) __ _
178
+ #{w} `b :$$ #{r} \\___ \\| |/ _` |/ _ \\ |/ / |/ _` |
179
+ #{w} $$: #{r} ___) | | (_| | __/ <| | (_| |
180
+ #{w} $$ #{r}|____/|_|\\__,_|\\___|_|\\_\\_|\\__, |
181
+ #{w} .d$$ #{r} |_|
182
+ #{reset}}
138
183
  end
139
184
 
140
185
  SIGNAL_HANDLERS = {
141
186
  # Ctrl-C in terminal
142
- 'INT' => ->(cli) { raise Interrupt },
187
+ "INT" => ->(cli) { raise Interrupt },
143
188
  # TERM is the signal that Sidekiq must exit.
144
189
  # Heroku sends TERM and then waits 30 seconds for process to exit.
145
- 'TERM' => ->(cli) { raise Interrupt },
146
- 'USR1' => ->(cli) {
147
- Sidekiq.logger.info "Received USR1, no longer accepting new work"
190
+ "TERM" => ->(cli) { raise Interrupt },
191
+ "TSTP" => ->(cli) {
192
+ cli.logger.info "Received TSTP, no longer accepting new work"
148
193
  cli.launcher.quiet
149
194
  },
150
- 'TSTP' => ->(cli) {
151
- Sidekiq.logger.info "Received TSTP, no longer accepting new work"
152
- cli.launcher.quiet
153
- },
154
- 'USR2' => ->(cli) {
155
- if Sidekiq.options[:logfile]
156
- Sidekiq.logger.info "Received USR2, reopening log file"
157
- Sidekiq::Logging.reopen_logs
158
- end
159
- },
160
- 'TTIN' => ->(cli) {
195
+ "TTIN" => ->(cli) {
161
196
  Thread.list.each do |thread|
162
- Sidekiq.logger.warn "Thread TID-#{(thread.object_id ^ ::Process.pid).to_s(36)} #{thread['sidekiq_label']}"
197
+ cli.logger.warn "Thread TID-#{(thread.object_id ^ ::Process.pid).to_s(36)} #{thread.name}"
163
198
  if thread.backtrace
164
- Sidekiq.logger.warn thread.backtrace.join("\n")
199
+ cli.logger.warn thread.backtrace.join("\n")
165
200
  else
166
- Sidekiq.logger.warn "<no backtrace available>"
201
+ cli.logger.warn "<no backtrace available>"
167
202
  end
168
203
  end
169
- },
204
+ }
170
205
  }
206
+ UNHANDLED_SIGNAL_HANDLER = ->(cli) { cli.logger.info "No signal handler registered, ignoring" }
207
+ SIGNAL_HANDLERS.default = UNHANDLED_SIGNAL_HANDLER
171
208
 
172
209
  def handle_signal(sig)
173
- Sidekiq.logger.debug "Got #{sig} signal"
174
- handy = SIGNAL_HANDLERS[sig]
175
- if handy
176
- handy.call(self)
177
- else
178
- Sidekiq.logger.info { "No signal handler for #{sig}" }
179
- end
210
+ logger.debug "Got #{sig} signal"
211
+ SIGNAL_HANDLERS[sig].call(self)
180
212
  end
181
213
 
182
214
  private
183
215
 
184
216
  def print_banner
185
- puts "\e[#{31}m"
217
+ puts "\e[31m"
186
218
  puts Sidekiq::CLI.banner
187
219
  puts "\e[0m"
188
220
  end
189
221
 
190
- def daemonize
191
- raise ArgumentError, "You really should set a logfile if you're going to daemonize" unless options[:logfile]
192
-
193
- files_to_reopen = ObjectSpace.each_object(File).reject { |f| f.closed? }
194
- ::Process.daemon(true, true)
195
-
196
- files_to_reopen.each do |file|
197
- begin
198
- file.reopen file.path, "a+"
199
- file.sync = true
200
- rescue ::Exception
201
- end
202
- end
203
-
204
- [$stdout, $stderr].each do |io|
205
- File.open(options[:logfile], 'ab') do |f|
206
- io.reopen(f)
207
- end
208
- io.sync = true
209
- end
210
- $stdin.reopen('/dev/null')
211
-
212
- initialize_logger
213
- end
214
-
215
222
  def set_environment(cli_env)
216
- @environment = cli_env || ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development'
223
+ # See #984 for discussion.
224
+ # APP_ENV is now the preferred ENV term since it is not tech-specific.
225
+ # Both Sinatra 2.0+ and Sidekiq support this term.
226
+ # RAILS_ENV and RACK_ENV are there for legacy support.
227
+ @environment = cli_env || ENV["APP_ENV"] || ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
228
+ config[:environment] = @environment
217
229
  end
218
230
 
219
231
  def symbolize_keys_deep!(hash)
220
232
  hash.keys.each do |k|
221
233
  symkey = k.respond_to?(:to_sym) ? k.to_sym : k
222
234
  hash[symkey] = hash.delete k
223
- symbolize_keys_deep! hash[symkey] if hash[symkey].kind_of? Hash
235
+ symbolize_keys_deep! hash[symkey] if hash[symkey].is_a? Hash
224
236
  end
225
237
  end
226
238
 
@@ -235,14 +247,14 @@ module Sidekiq
235
247
 
236
248
  # check config file presence
237
249
  if opts[:config_file]
238
- if opts[:config_file] && !File.exist?(opts[:config_file])
250
+ unless File.exist?(opts[:config_file])
239
251
  raise ArgumentError, "No such file #{opts[:config_file]}"
240
252
  end
241
253
  else
242
254
  config_dir = if File.directory?(opts[:require].to_s)
243
- File.join(opts[:require], 'config')
255
+ File.join(opts[:require], "config")
244
256
  else
245
- File.join(options[:require], 'config')
257
+ File.join(@config[:require], "config")
246
258
  end
247
259
 
248
260
  %w[sidekiq.yml sidekiq.yml.erb].each do |config_file|
@@ -255,49 +267,36 @@ module Sidekiq
255
267
  opts = parse_config(opts[:config_file]).merge(opts) if opts[:config_file]
256
268
 
257
269
  # set defaults
258
- opts[:queues] = Array(opts[:queues]) << 'default' if opts[:queues].nil? || opts[:queues].empty?
259
- opts[:strict] = true if opts[:strict].nil?
270
+ opts[:queues] = ["default"] if opts[:queues].nil?
260
271
  opts[:concurrency] = Integer(ENV["RAILS_MAX_THREADS"]) if opts[:concurrency].nil? && ENV["RAILS_MAX_THREADS"]
261
272
 
262
273
  # merge with defaults
263
- options.merge!(opts)
264
- end
265
-
266
- def options
267
- Sidekiq.options
274
+ @config.merge!(opts)
268
275
  end
269
276
 
270
- def boot_system
271
- ENV['RACK_ENV'] = ENV['RAILS_ENV'] = environment
277
+ def boot_application
278
+ ENV["RACK_ENV"] = ENV["RAILS_ENV"] = environment
272
279
 
273
- if File.directory?(options[:require])
274
- require 'rails'
275
- if ::Rails::VERSION::MAJOR < 4
280
+ if File.directory?(@config[:require])
281
+ require "rails"
282
+ if ::Rails::VERSION::MAJOR < 5
276
283
  raise "Sidekiq no longer supports this version of Rails"
277
- elsif ::Rails::VERSION::MAJOR == 4
278
- # Painful contortions, see 1791 for discussion
279
- # No autoloading, we want to force eager load for everything.
280
- require File.expand_path("#{options[:require]}/config/application.rb")
281
- ::Rails::Application.initializer "sidekiq.eager_load" do
282
- ::Rails.application.config.eager_load = true
283
- end
284
- require 'sidekiq/rails'
285
- require File.expand_path("#{options[:require]}/config/environment.rb")
286
284
  else
287
- require 'sidekiq/rails'
288
- require File.expand_path("#{options[:require]}/config/environment.rb")
285
+ require "sidekiq/rails"
286
+ require File.expand_path("#{@config[:require]}/config/environment.rb")
289
287
  end
290
- options[:tag] ||= default_tag
288
+ @config[:tag] ||= default_tag
291
289
  else
292
- require options[:require]
290
+ require @config[:require]
293
291
  end
294
292
  end
295
293
 
296
294
  def default_tag
297
295
  dir = ::Rails.root
298
296
  name = File.basename(dir)
299
- if name.to_i != 0 && prevdir = File.dirname(dir) # Capistrano release directory?
300
- if File.basename(prevdir) == 'releases'
297
+ prevdir = File.dirname(dir) # Capistrano release directory?
298
+ if name.to_i != 0 && prevdir
299
+ if File.basename(prevdir) == "releases"
301
300
  return File.basename(File.dirname(prevdir))
302
301
  end
303
302
  end
@@ -305,58 +304,56 @@ module Sidekiq
305
304
  end
306
305
 
307
306
  def validate!
308
- if !File.exist?(options[:require]) ||
309
- (File.directory?(options[:require]) && !File.exist?("#{options[:require]}/config/application.rb"))
307
+ if !File.exist?(@config[:require]) ||
308
+ (File.directory?(@config[:require]) && !File.exist?("#{@config[:require]}/config/application.rb"))
310
309
  logger.info "=================================================================="
311
- logger.info " Please point sidekiq to a Rails 4/5 application or a Ruby file "
312
- logger.info " to load your worker classes with -r [DIR|FILE]."
310
+ logger.info " Please point Sidekiq to a Rails application or a Ruby file "
311
+ logger.info " to load your job classes with -r [DIR|FILE]."
313
312
  logger.info "=================================================================="
314
313
  logger.info @parser
315
314
  die(1)
316
315
  end
317
316
 
318
317
  [:concurrency, :timeout].each do |opt|
319
- raise ArgumentError, "#{opt}: #{options[opt]} is not a valid value" if options.has_key?(opt) && options[opt].to_i <= 0
318
+ raise ArgumentError, "#{opt}: #{@config[opt]} is not a valid value" if @config[opt].to_i <= 0
320
319
  end
321
320
  end
322
321
 
323
322
  def parse_options(argv)
324
323
  opts = {}
324
+ @parser = option_parser(opts)
325
+ @parser.parse!(argv)
326
+ opts
327
+ end
325
328
 
326
- @parser = OptionParser.new do |o|
327
- o.on '-c', '--concurrency INT', "processor threads to use" do |arg|
329
+ def option_parser(opts)
330
+ parser = OptionParser.new { |o|
331
+ o.on "-c", "--concurrency INT", "processor threads to use" do |arg|
328
332
  opts[:concurrency] = Integer(arg)
329
333
  end
330
334
 
331
- o.on '-d', '--daemon', "Daemonize process" do |arg|
332
- opts[:daemon] = arg
333
- puts "WARNING: Daemonization mode will be removed in Sidekiq 6.0, see #4045. Please use a proper process supervisor to start and manage your services"
335
+ o.on "-d", "--daemon", "Daemonize process" do |arg|
336
+ puts "ERROR: Daemonization mode was removed in Sidekiq 6.0, please use a proper process supervisor to start and manage your services"
334
337
  end
335
338
 
336
- o.on '-e', '--environment ENV', "Application environment" do |arg|
339
+ o.on "-e", "--environment ENV", "Application environment" do |arg|
337
340
  opts[:environment] = arg
338
341
  end
339
342
 
340
- o.on '-g', '--tag TAG', "Process tag for procline" do |arg|
343
+ o.on "-g", "--tag TAG", "Process tag for procline" do |arg|
341
344
  opts[:tag] = arg
342
345
  end
343
346
 
344
- # this index remains here for backwards compatibility but none of the Sidekiq
345
- # family use this value anymore. it was used by Pro's original reliable_fetch.
346
- o.on '-i', '--index INT', "unique process index on this machine" do |arg|
347
- opts[:index] = Integer(arg.match(/\d+/)[0])
348
- end
349
-
350
347
  o.on "-q", "--queue QUEUE[,WEIGHT]", "Queues to process with optional weights" do |arg|
351
348
  queue, weight = arg.split(",")
352
349
  parse_queue opts, queue, weight
353
350
  end
354
351
 
355
- o.on '-r', '--require [PATH|DIR]', "Location of Rails application with workers or file to require" do |arg|
352
+ o.on "-r", "--require [PATH|DIR]", "Location of Rails application with jobs or file to require" do |arg|
356
353
  opts[:require] = arg
357
354
  end
358
355
 
359
- o.on '-t', '--timeout NUM', "Shutdown timeout" do |arg|
356
+ o.on "-t", "--timeout NUM", "Shutdown timeout" do |arg|
360
357
  opts[:timeout] = Integer(arg)
361
358
  end
362
359
 
@@ -364,54 +361,41 @@ module Sidekiq
364
361
  opts[:verbose] = arg
365
362
  end
366
363
 
367
- o.on '-C', '--config PATH', "path to YAML config file" do |arg|
364
+ o.on "-C", "--config PATH", "path to YAML config file" do |arg|
368
365
  opts[:config_file] = arg
369
366
  end
370
367
 
371
- o.on '-L', '--logfile PATH', "path to writable logfile" do |arg|
372
- opts[:logfile] = arg
373
- puts "WARNING: Logfile redirection will be removed in Sidekiq 6.0, see #4045. Sidekiq will only log to STDOUT"
368
+ o.on "-L", "--logfile PATH", "path to writable logfile" do |arg|
369
+ puts "ERROR: Logfile redirection was removed in Sidekiq 6.0, Sidekiq will only log to STDOUT"
374
370
  end
375
371
 
376
- o.on '-P', '--pidfile PATH', "path to pidfile" do |arg|
377
- opts[:pidfile] = arg
378
- puts "WARNING: PID file creation will be removed in Sidekiq 6.0, see #4045. Please use a proper process supervisor to start and manage your services"
372
+ o.on "-P", "--pidfile PATH", "path to pidfile" do |arg|
373
+ puts "ERROR: PID file creation was removed in Sidekiq 6.0, please use a proper process supervisor to start and manage your services"
379
374
  end
380
375
 
381
- o.on '-V', '--version', "Print version and exit" do |arg|
376
+ o.on "-V", "--version", "Print version and exit" do |arg|
382
377
  puts "Sidekiq #{Sidekiq::VERSION}"
383
378
  die(0)
384
379
  end
385
- end
380
+ }
386
381
 
387
- @parser.banner = "sidekiq [options]"
388
- @parser.on_tail "-h", "--help", "Show help" do
389
- logger.info @parser
382
+ parser.banner = "sidekiq [options]"
383
+ parser.on_tail "-h", "--help", "Show help" do
384
+ logger.info parser
390
385
  die 1
391
386
  end
392
387
 
393
- @parser.parse!(argv)
394
-
395
- opts
388
+ parser
396
389
  end
397
390
 
398
391
  def initialize_logger
399
- Sidekiq::Logging.initialize_logger(options[:logfile]) if options[:logfile]
400
-
401
- Sidekiq.logger.level = ::Logger::DEBUG if options[:verbose]
402
- end
403
-
404
- def write_pid
405
- if path = options[:pidfile]
406
- pidfile = File.expand_path(path)
407
- File.open(pidfile, 'w') do |f|
408
- f.puts ::Process.pid
409
- end
410
- end
392
+ @config.logger.level = ::Logger::DEBUG if @config[:verbose]
411
393
  end
412
394
 
413
395
  def parse_config(path)
414
- opts = YAML.load(ERB.new(File.read(path)).result) || {}
396
+ erb = ERB.new(File.read(path))
397
+ erb.filename = File.expand_path(path)
398
+ opts = load_yaml(erb.result) || {}
415
399
 
416
400
  if opts.respond_to? :deep_symbolize_keys!
417
401
  opts.deep_symbolize_keys!
@@ -420,26 +404,38 @@ module Sidekiq
420
404
  end
421
405
 
422
406
  opts = opts.merge(opts.delete(environment.to_sym) || {})
407
+ opts.delete(:strict)
408
+
423
409
  parse_queues(opts, opts.delete(:queues) || [])
424
410
 
425
- ns = opts.delete(:namespace)
426
- if ns
427
- # logger hasn't been initialized yet, puts is all we have.
428
- puts("namespace should be set in your ruby initializer, is ignored in config file")
429
- puts("config.redis = { :url => ..., :namespace => '#{ns}' }")
430
- end
431
411
  opts
432
412
  end
433
413
 
414
+ def load_yaml(src)
415
+ if Psych::VERSION > "4.0"
416
+ YAML.safe_load(src, permitted_classes: [Symbol], aliases: true)
417
+ else
418
+ YAML.load(src)
419
+ end
420
+ end
421
+
434
422
  def parse_queues(opts, queues_and_weights)
435
423
  queues_and_weights.each { |queue_and_weight| parse_queue(opts, *queue_and_weight) }
436
424
  end
437
425
 
438
426
  def parse_queue(opts, queue, weight = nil)
439
427
  opts[:queues] ||= []
428
+ opts[:strict] = true if opts[:strict].nil?
440
429
  raise ArgumentError, "queues: #{queue} cannot be defined twice" if opts[:queues].include?(queue)
441
- [weight.to_i, 1].max.times { opts[:queues] << queue }
430
+ [weight.to_i, 1].max.times { opts[:queues] << queue.to_s }
442
431
  opts[:strict] = false if weight.to_i > 0
443
432
  end
433
+
434
+ def rails_app?
435
+ defined?(::Rails) && ::Rails.respond_to?(:application)
436
+ end
444
437
  end
445
438
  end
439
+
440
+ require "sidekiq/systemd"
441
+ require "sidekiq/metrics/tracking" if ENV["SIDEKIQ_METRICS_BETA"]