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