sidekiq 4.2.10 → 7.3.10
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 +5 -5
- data/Changes.md +932 -7
- data/LICENSE.txt +9 -0
- data/README.md +49 -50
- data/bin/multi_queue_bench +271 -0
- data/bin/sidekiq +22 -3
- data/bin/sidekiqload +218 -116
- data/bin/sidekiqmon +11 -0
- data/lib/active_job/queue_adapters/sidekiq_adapter.rb +75 -0
- data/lib/generators/sidekiq/job_generator.rb +59 -0
- data/lib/generators/sidekiq/templates/{worker.rb.erb → job.rb.erb} +2 -2
- data/lib/generators/sidekiq/templates/job_spec.rb.erb +6 -0
- data/lib/generators/sidekiq/templates/{worker_test.rb.erb → job_test.rb.erb} +1 -1
- data/lib/sidekiq/api.rb +710 -322
- data/lib/sidekiq/capsule.rb +132 -0
- data/lib/sidekiq/cli.rb +268 -248
- data/lib/sidekiq/client.rb +153 -101
- data/lib/sidekiq/component.rb +90 -0
- data/lib/sidekiq/config.rb +311 -0
- data/lib/sidekiq/deploy.rb +64 -0
- data/lib/sidekiq/embedded.rb +63 -0
- data/lib/sidekiq/fetch.rb +50 -42
- data/lib/sidekiq/iterable_job.rb +55 -0
- data/lib/sidekiq/job/interrupt_handler.rb +24 -0
- data/lib/sidekiq/job/iterable/active_record_enumerator.rb +53 -0
- data/lib/sidekiq/job/iterable/csv_enumerator.rb +47 -0
- data/lib/sidekiq/job/iterable/enumerators.rb +135 -0
- data/lib/sidekiq/job/iterable.rb +294 -0
- data/lib/sidekiq/job.rb +385 -0
- data/lib/sidekiq/job_logger.rb +52 -0
- data/lib/sidekiq/job_retry.rb +305 -0
- data/lib/sidekiq/job_util.rb +109 -0
- data/lib/sidekiq/launcher.rb +208 -108
- data/lib/sidekiq/logger.rb +131 -0
- data/lib/sidekiq/manager.rb +43 -47
- data/lib/sidekiq/metrics/query.rb +158 -0
- data/lib/sidekiq/metrics/shared.rb +106 -0
- data/lib/sidekiq/metrics/tracking.rb +148 -0
- data/lib/sidekiq/middleware/chain.rb +113 -56
- data/lib/sidekiq/middleware/current_attributes.rb +128 -0
- data/lib/sidekiq/middleware/i18n.rb +9 -7
- data/lib/sidekiq/middleware/modules.rb +23 -0
- data/lib/sidekiq/monitor.rb +147 -0
- data/lib/sidekiq/paginator.rb +33 -15
- data/lib/sidekiq/processor.rb +188 -98
- data/lib/sidekiq/rails.rb +53 -92
- data/lib/sidekiq/redis_client_adapter.rb +114 -0
- data/lib/sidekiq/redis_connection.rb +86 -77
- data/lib/sidekiq/ring_buffer.rb +32 -0
- data/lib/sidekiq/scheduled.rb +140 -51
- data/lib/sidekiq/sd_notify.rb +149 -0
- data/lib/sidekiq/systemd.rb +26 -0
- data/lib/sidekiq/testing/inline.rb +6 -5
- data/lib/sidekiq/testing.rb +95 -85
- data/lib/sidekiq/transaction_aware_client.rb +59 -0
- data/lib/sidekiq/version.rb +7 -1
- data/lib/sidekiq/web/action.rb +40 -18
- data/lib/sidekiq/web/application.rb +189 -89
- data/lib/sidekiq/web/csrf_protection.rb +183 -0
- data/lib/sidekiq/web/helpers.rb +239 -101
- data/lib/sidekiq/web/router.rb +28 -21
- data/lib/sidekiq/web.rb +123 -110
- data/lib/sidekiq/worker_compatibility_alias.rb +13 -0
- data/lib/sidekiq.rb +97 -185
- data/sidekiq.gemspec +26 -27
- data/web/assets/images/apple-touch-icon.png +0 -0
- data/web/assets/javascripts/application.js +157 -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 +194 -0
- data/web/assets/javascripts/dashboard.js +43 -280
- data/web/assets/javascripts/metrics.js +298 -0
- data/web/assets/stylesheets/application-dark.css +147 -0
- data/web/assets/stylesheets/application-rtl.css +163 -0
- data/web/assets/stylesheets/application.css +176 -196
- data/web/assets/stylesheets/bootstrap-rtl.min.css +9 -0
- data/web/assets/stylesheets/bootstrap.css +2 -2
- data/web/locales/ar.yml +87 -0
- 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 +88 -64
- data/web/locales/es.yml +70 -53
- data/web/locales/fa.yml +65 -64
- data/web/locales/fr.yml +82 -62
- data/web/locales/gd.yml +98 -0
- data/web/locales/he.yml +80 -0
- data/web/locales/hi.yml +59 -59
- data/web/locales/it.yml +85 -54
- data/web/locales/ja.yml +74 -62
- 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 +82 -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/tr.yml +100 -0
- data/web/locales/uk.yml +85 -61
- data/web/locales/ur.yml +80 -0
- data/web/locales/vi.yml +83 -0
- data/web/locales/zh-cn.yml +42 -16
- data/web/locales/zh-tw.yml +41 -8
- data/web/views/_footer.erb +20 -3
- data/web/views/_job_info.erb +21 -4
- data/web/views/_metrics_period_select.erb +12 -0
- data/web/views/_nav.erb +5 -19
- data/web/views/_paging.erb +3 -1
- data/web/views/_poll_link.erb +3 -6
- data/web/views/_summary.erb +7 -7
- data/web/views/busy.erb +85 -31
- data/web/views/dashboard.erb +53 -20
- data/web/views/dead.erb +3 -3
- data/web/views/filtering.erb +6 -0
- data/web/views/layout.erb +17 -6
- data/web/views/metrics.erb +90 -0
- data/web/views/metrics_for_job.erb +59 -0
- data/web/views/morgue.erb +15 -16
- data/web/views/queue.erb +35 -25
- data/web/views/queues.erb +20 -4
- data/web/views/retries.erb +19 -16
- data/web/views/retry.erb +3 -3
- data/web/views/scheduled.erb +19 -17
- metadata +103 -194
- data/.github/contributing.md +0 -32
- data/.github/issue_template.md +0 -9
- data/.gitignore +0 -12
- data/.travis.yml +0 -18
- data/3.0-Upgrade.md +0 -70
- data/4.0-Upgrade.md +0 -53
- data/COMM-LICENSE +0 -95
- data/Ent-Changes.md +0 -173
- 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-Changes.md +0 -628
- data/Rakefile +0 -12
- data/bin/sidekiqctl +0 -99
- data/code_of_conduct.md +0 -50
- data/lib/generators/sidekiq/templates/worker_spec.rb.erb +0 -6
- data/lib/generators/sidekiq/worker_generator.rb +0 -49
- data/lib/sidekiq/core_ext.rb +0 -119
- data/lib/sidekiq/exception_handler.rb +0 -31
- 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 -25
- data/lib/sidekiq/logging.rb +0 -106
- data/lib/sidekiq/middleware/server/active_record.rb +0 -13
- data/lib/sidekiq/middleware/server/logging.rb +0 -31
- data/lib/sidekiq/middleware/server/retry_jobs.rb +0 -205
- data/lib/sidekiq/util.rb +0 -63
- data/lib/sidekiq/worker.rb +0 -121
data/lib/sidekiq/cli.rb
CHANGED
|
@@ -1,266 +1,320 @@
|
|
|
1
|
-
# encoding: utf-8
|
|
2
1
|
# frozen_string_literal: true
|
|
2
|
+
|
|
3
3
|
$stdout.sync = true
|
|
4
4
|
|
|
5
|
-
require
|
|
6
|
-
require
|
|
7
|
-
require
|
|
8
|
-
require
|
|
9
|
-
require
|
|
5
|
+
require "yaml"
|
|
6
|
+
require "singleton"
|
|
7
|
+
require "optparse"
|
|
8
|
+
require "erb"
|
|
9
|
+
require "fileutils"
|
|
10
10
|
|
|
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'.freeze },
|
|
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
|
-
# Used for CLI testing
|
|
28
|
-
attr_accessor :code
|
|
29
22
|
attr_accessor :launcher
|
|
30
23
|
attr_accessor :environment
|
|
24
|
+
attr_accessor :config
|
|
31
25
|
|
|
32
|
-
def
|
|
33
|
-
@
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def parse(args=ARGV)
|
|
37
|
-
@code = nil
|
|
26
|
+
def parse(args = ARGV.dup)
|
|
27
|
+
@config ||= Sidekiq.default_configuration
|
|
38
28
|
|
|
39
29
|
setup_options(args)
|
|
40
30
|
initialize_logger
|
|
41
31
|
validate!
|
|
42
|
-
|
|
43
|
-
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def jruby?
|
|
35
|
+
defined?(::JRUBY_VERSION)
|
|
44
36
|
end
|
|
45
37
|
|
|
46
38
|
# Code within this method is not tested because it alters
|
|
47
39
|
# global process state irreversibly. PRs which improve the
|
|
48
40
|
# test coverage of Sidekiq::CLI are welcomed.
|
|
49
|
-
def run
|
|
50
|
-
|
|
51
|
-
print_banner
|
|
41
|
+
def run(boot_app: true, warmup: true)
|
|
42
|
+
boot_application if boot_app
|
|
52
43
|
|
|
53
|
-
|
|
44
|
+
if environment == "development" && $stdout.tty? && @config.logger.formatter.is_a?(Sidekiq::Logger::Formatters::Pretty)
|
|
45
|
+
print_banner
|
|
46
|
+
end
|
|
47
|
+
logger.info "Booted Rails #{::Rails.version} application in #{environment} environment" if rails_app?
|
|
54
48
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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?
|
|
53
|
+
sigs.each do |sig|
|
|
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
|
|
59
62
|
end
|
|
60
|
-
|
|
61
|
-
puts "Signal #{sig} not supported"
|
|
63
|
+
self_write.puts(sig)
|
|
62
64
|
end
|
|
65
|
+
rescue ArgumentError
|
|
66
|
+
puts "Signal #{sig} not supported"
|
|
63
67
|
end
|
|
64
68
|
|
|
65
69
|
logger.info "Running in #{RUBY_DESCRIPTION}"
|
|
66
70
|
logger.info Sidekiq::LICENSE
|
|
67
|
-
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)
|
|
68
72
|
|
|
69
73
|
# touch the connection pool so it is created before we
|
|
70
74
|
# fire startup and start multithreading.
|
|
71
|
-
|
|
72
|
-
|
|
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
|
|
91
|
+
|
|
92
|
+
# Since the user can pass us a connection pool explicitly in the initializer, we
|
|
93
|
+
# need to verify the size is large enough or else Sidekiq's performance is dramatically slowed.
|
|
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
|
|
97
|
+
|
|
98
|
+
# cache process identity
|
|
99
|
+
@config[:identity] = identity
|
|
73
100
|
|
|
74
101
|
# Touch middleware so it isn't lazy loaded by multiple threads, #3043
|
|
75
|
-
|
|
102
|
+
@config.server_middleware
|
|
103
|
+
|
|
104
|
+
::Process.warmup if warmup && ::Process.respond_to?(:warmup) && ENV["RUBY_DISABLE_WARMUP"] != "1"
|
|
76
105
|
|
|
77
106
|
# Before this point, the process is initializing with just the main thread.
|
|
78
107
|
# Starting here the process will now have multiple threads running.
|
|
79
|
-
fire_event(:startup)
|
|
108
|
+
fire_event(:startup, reverse: false, reraise: true)
|
|
109
|
+
|
|
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(", ")}" }
|
|
80
112
|
|
|
81
|
-
|
|
82
|
-
|
|
113
|
+
launch(self_read)
|
|
114
|
+
end
|
|
83
115
|
|
|
84
|
-
|
|
85
|
-
|
|
116
|
+
def launch(self_read)
|
|
117
|
+
if environment == "development" && $stdout.tty?
|
|
118
|
+
logger.info "Starting processing, hit Ctrl-C to stop"
|
|
86
119
|
end
|
|
87
120
|
|
|
88
|
-
|
|
89
|
-
@launcher = Sidekiq::Launcher.new(options)
|
|
121
|
+
@launcher = Sidekiq::Launcher.new(@config)
|
|
90
122
|
|
|
91
123
|
begin
|
|
92
124
|
launcher.run
|
|
93
125
|
|
|
94
|
-
while
|
|
95
|
-
signal =
|
|
126
|
+
while self_read.wait_readable
|
|
127
|
+
signal = self_read.gets.strip
|
|
96
128
|
handle_signal(signal)
|
|
97
129
|
end
|
|
98
130
|
rescue Interrupt
|
|
99
|
-
logger.info
|
|
131
|
+
logger.info "Shutting down"
|
|
100
132
|
launcher.stop
|
|
101
|
-
# Explicitly exit so busy Processor threads can't block
|
|
102
|
-
# process shutdown.
|
|
103
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.
|
|
104
140
|
exit(0)
|
|
105
141
|
end
|
|
106
142
|
end
|
|
107
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
|
+
|
|
108
175
|
def self.banner
|
|
109
|
-
%
|
|
110
|
-
m,
|
|
111
|
-
`$b
|
|
112
|
-
.ss, $$: .,d$
|
|
113
|
-
`$$P,d$P' .,md$P"'
|
|
114
|
-
,$$$$$
|
|
115
|
-
.d
|
|
116
|
-
$$^' `"
|
|
117
|
-
$:
|
|
118
|
-
`b :$$
|
|
119
|
-
$$:
|
|
120
|
-
$$ |____/|_
|
|
121
|
-
.d$$
|
|
122
|
-
}
|
|
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}}
|
|
123
190
|
end
|
|
124
191
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
Sidekiq.logger.info "Received USR1, no longer accepting new work"
|
|
137
|
-
launcher.quiet
|
|
138
|
-
when 'TSTP'
|
|
139
|
-
# USR1 is not available on JVM, allow TSTP as an alternate signal
|
|
140
|
-
Sidekiq.logger.info "Received TSTP, no longer accepting new work"
|
|
141
|
-
launcher.quiet
|
|
142
|
-
when 'USR2'
|
|
143
|
-
if Sidekiq.options[:logfile]
|
|
144
|
-
Sidekiq.logger.info "Received USR2, reopening log file"
|
|
145
|
-
Sidekiq::Logging.reopen_logs
|
|
146
|
-
end
|
|
147
|
-
when 'TTIN'
|
|
192
|
+
SIGNAL_HANDLERS = {
|
|
193
|
+
# Ctrl-C in terminal
|
|
194
|
+
"INT" => ->(cli) { raise Interrupt },
|
|
195
|
+
# TERM is the signal that Sidekiq must exit.
|
|
196
|
+
# Heroku sends TERM and then waits 30 seconds for process to exit.
|
|
197
|
+
"TERM" => ->(cli) { raise Interrupt },
|
|
198
|
+
"TSTP" => ->(cli) {
|
|
199
|
+
cli.logger.info "Received TSTP, no longer accepting new work"
|
|
200
|
+
cli.launcher.quiet
|
|
201
|
+
},
|
|
202
|
+
"TTIN" => ->(cli) {
|
|
148
203
|
Thread.list.each do |thread|
|
|
149
|
-
|
|
204
|
+
cli.logger.warn "Thread TID-#{(thread.object_id ^ ::Process.pid).to_s(36)} #{thread.name}"
|
|
150
205
|
if thread.backtrace
|
|
151
|
-
|
|
206
|
+
cli.logger.warn thread.backtrace.join("\n")
|
|
152
207
|
else
|
|
153
|
-
|
|
208
|
+
cli.logger.warn "<no backtrace available>"
|
|
154
209
|
end
|
|
155
210
|
end
|
|
156
|
-
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
UNHANDLED_SIGNAL_HANDLER = ->(cli) { cli.logger.info "No signal handler registered, ignoring" }
|
|
214
|
+
SIGNAL_HANDLERS.default = UNHANDLED_SIGNAL_HANDLER
|
|
215
|
+
|
|
216
|
+
def handle_signal(sig)
|
|
217
|
+
logger.debug "Got #{sig} signal"
|
|
218
|
+
SIGNAL_HANDLERS[sig].call(self)
|
|
157
219
|
end
|
|
158
220
|
|
|
159
221
|
private
|
|
160
222
|
|
|
161
223
|
def print_banner
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
puts Sidekiq::CLI.banner
|
|
166
|
-
puts "\e[0m"
|
|
167
|
-
end
|
|
224
|
+
puts "\e[31m"
|
|
225
|
+
puts Sidekiq::CLI.banner
|
|
226
|
+
puts "\e[0m"
|
|
168
227
|
end
|
|
169
228
|
|
|
170
|
-
def
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
end
|
|
178
|
-
|
|
179
|
-
::Process.daemon(true, true)
|
|
180
|
-
|
|
181
|
-
files_to_reopen.each do |file|
|
|
182
|
-
begin
|
|
183
|
-
file.reopen file.path, "a+"
|
|
184
|
-
file.sync = true
|
|
185
|
-
rescue ::Exception
|
|
186
|
-
end
|
|
187
|
-
end
|
|
188
|
-
|
|
189
|
-
[$stdout, $stderr].each do |io|
|
|
190
|
-
File.open(options[:logfile], 'ab') do |f|
|
|
191
|
-
io.reopen(f)
|
|
192
|
-
end
|
|
193
|
-
io.sync = true
|
|
194
|
-
end
|
|
195
|
-
$stdin.reopen('/dev/null')
|
|
196
|
-
|
|
197
|
-
initialize_logger
|
|
229
|
+
def set_environment(cli_env)
|
|
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
|
|
198
236
|
end
|
|
199
237
|
|
|
200
|
-
def
|
|
201
|
-
|
|
238
|
+
def symbolize_keys_deep!(hash)
|
|
239
|
+
hash.keys.each do |k|
|
|
240
|
+
symkey = k.respond_to?(:to_sym) ? k.to_sym : k
|
|
241
|
+
hash[symkey] = hash.delete k
|
|
242
|
+
symbolize_keys_deep! hash[symkey] if hash[symkey].is_a? Hash
|
|
243
|
+
end
|
|
202
244
|
end
|
|
203
245
|
|
|
204
246
|
alias_method :die, :exit
|
|
205
247
|
alias_method :☠, :exit
|
|
206
248
|
|
|
207
249
|
def setup_options(args)
|
|
250
|
+
# parse CLI options
|
|
208
251
|
opts = parse_options(args)
|
|
252
|
+
|
|
209
253
|
set_environment opts[:environment]
|
|
210
254
|
|
|
211
|
-
|
|
212
|
-
|
|
255
|
+
# check config file presence
|
|
256
|
+
if opts[:config_file]
|
|
257
|
+
unless File.exist?(opts[:config_file])
|
|
258
|
+
raise ArgumentError, "No such file #{opts[:config_file]}"
|
|
259
|
+
end
|
|
260
|
+
else
|
|
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
|
|
213
266
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
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)
|
|
270
|
+
end
|
|
271
|
+
end
|
|
217
272
|
|
|
218
|
-
options
|
|
219
|
-
|
|
273
|
+
# parse config file options
|
|
274
|
+
opts = parse_config(opts[:config_file]).merge(opts) if opts[:config_file]
|
|
220
275
|
|
|
221
|
-
|
|
222
|
-
|
|
276
|
+
# set defaults
|
|
277
|
+
opts[:queues] = ["default"] if opts[:queues].nil?
|
|
278
|
+
opts[:concurrency] = Integer(ENV["RAILS_MAX_THREADS"]) if opts[:concurrency].nil? && ENV["RAILS_MAX_THREADS"]
|
|
279
|
+
|
|
280
|
+
# merge with defaults
|
|
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
|
|
287
|
+
|
|
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
|
|
223
294
|
end
|
|
224
295
|
|
|
225
|
-
def
|
|
226
|
-
ENV[
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
if ::Rails::VERSION::MAJOR < 4
|
|
233
|
-
require 'sidekiq/rails'
|
|
234
|
-
require File.expand_path("#{options[:require]}/config/environment.rb")
|
|
235
|
-
::Rails.application.eager_load!
|
|
236
|
-
elsif ::Rails::VERSION::MAJOR == 4
|
|
237
|
-
# Painful contortions, see 1791 for discussion
|
|
238
|
-
# No autoloading, we want to force eager load for everything.
|
|
239
|
-
require File.expand_path("#{options[:require]}/config/application.rb")
|
|
240
|
-
::Rails::Application.initializer "sidekiq.eager_load" do
|
|
241
|
-
::Rails.application.config.eager_load = true
|
|
242
|
-
end
|
|
243
|
-
require 'sidekiq/rails'
|
|
244
|
-
require File.expand_path("#{options[:require]}/config/environment.rb")
|
|
245
|
-
else
|
|
246
|
-
# Rails 5+ && development mode, use Reloader
|
|
247
|
-
require 'sidekiq/rails'
|
|
248
|
-
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+"
|
|
249
303
|
end
|
|
250
|
-
|
|
304
|
+
require "sidekiq/rails"
|
|
305
|
+
require File.expand_path("#{@config[:require]}/config/environment.rb")
|
|
306
|
+
@config[:tag] ||= default_tag
|
|
251
307
|
else
|
|
252
|
-
|
|
253
|
-
"./#{options[:require]} or /path/to/#{options[:require]}"
|
|
254
|
-
|
|
255
|
-
require(options[:require]) || raise(ArgumentError, not_required_message)
|
|
308
|
+
require @config[:require]
|
|
256
309
|
end
|
|
257
310
|
end
|
|
258
311
|
|
|
259
312
|
def default_tag
|
|
260
313
|
dir = ::Rails.root
|
|
261
314
|
name = File.basename(dir)
|
|
262
|
-
|
|
263
|
-
|
|
315
|
+
prevdir = File.dirname(dir) # Capistrano release directory?
|
|
316
|
+
if name.to_i != 0 && prevdir
|
|
317
|
+
if File.basename(prevdir) == "releases"
|
|
264
318
|
return File.basename(File.dirname(prevdir))
|
|
265
319
|
end
|
|
266
320
|
end
|
|
@@ -268,57 +322,52 @@ module Sidekiq
|
|
|
268
322
|
end
|
|
269
323
|
|
|
270
324
|
def validate!
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
if !File.exist?(options[:require]) ||
|
|
274
|
-
(File.directory?(options[:require]) && !File.exist?("#{options[:require]}/config/application.rb"))
|
|
325
|
+
if !File.exist?(@config[:require]) ||
|
|
326
|
+
(File.directory?(@config[:require]) && !File.exist?("#{@config[:require]}/config/application.rb"))
|
|
275
327
|
logger.info "=================================================================="
|
|
276
|
-
logger.info " Please point
|
|
277
|
-
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]."
|
|
278
330
|
logger.info "=================================================================="
|
|
279
331
|
logger.info @parser
|
|
280
332
|
die(1)
|
|
281
333
|
end
|
|
282
334
|
|
|
283
335
|
[:concurrency, :timeout].each do |opt|
|
|
284
|
-
raise ArgumentError, "#{opt}: #{
|
|
336
|
+
raise ArgumentError, "#{opt}: #{@config[opt]} is not a valid value" if @config[opt].to_i <= 0
|
|
285
337
|
end
|
|
286
338
|
end
|
|
287
339
|
|
|
288
340
|
def parse_options(argv)
|
|
289
341
|
opts = {}
|
|
342
|
+
@parser = option_parser(opts)
|
|
343
|
+
@parser.parse!(argv)
|
|
344
|
+
opts
|
|
345
|
+
end
|
|
290
346
|
|
|
291
|
-
|
|
292
|
-
|
|
347
|
+
def option_parser(opts)
|
|
348
|
+
parser = OptionParser.new { |o|
|
|
349
|
+
o.on "-c", "--concurrency INT", "processor threads to use" do |arg|
|
|
293
350
|
opts[:concurrency] = Integer(arg)
|
|
294
351
|
end
|
|
295
352
|
|
|
296
|
-
o.on
|
|
297
|
-
opts[:daemon] = arg
|
|
298
|
-
end
|
|
299
|
-
|
|
300
|
-
o.on '-e', '--environment ENV', "Application environment" do |arg|
|
|
353
|
+
o.on "-e", "--environment ENV", "Application environment" do |arg|
|
|
301
354
|
opts[:environment] = arg
|
|
302
355
|
end
|
|
303
356
|
|
|
304
|
-
o.on
|
|
357
|
+
o.on "-g", "--tag TAG", "Process tag for procline" do |arg|
|
|
305
358
|
opts[:tag] = arg
|
|
306
359
|
end
|
|
307
360
|
|
|
308
|
-
o.on '-i', '--index INT', "unique process index on this machine" do |arg|
|
|
309
|
-
opts[:index] = Integer(arg.match(/\d+/)[0])
|
|
310
|
-
end
|
|
311
|
-
|
|
312
361
|
o.on "-q", "--queue QUEUE[,WEIGHT]", "Queues to process with optional weights" do |arg|
|
|
313
|
-
|
|
314
|
-
|
|
362
|
+
opts[:queues] ||= []
|
|
363
|
+
opts[:queues] << arg
|
|
315
364
|
end
|
|
316
365
|
|
|
317
|
-
o.on
|
|
366
|
+
o.on "-r", "--require [PATH|DIR]", "Location of Rails application with jobs or file to require" do |arg|
|
|
318
367
|
opts[:require] = arg
|
|
319
368
|
end
|
|
320
369
|
|
|
321
|
-
o.on
|
|
370
|
+
o.on "-t", "--timeout NUM", "Shutdown timeout" do |arg|
|
|
322
371
|
opts[:timeout] = Integer(arg)
|
|
323
372
|
end
|
|
324
373
|
|
|
@@ -326,81 +375,52 @@ module Sidekiq
|
|
|
326
375
|
opts[:verbose] = arg
|
|
327
376
|
end
|
|
328
377
|
|
|
329
|
-
o.on
|
|
378
|
+
o.on "-C", "--config PATH", "path to YAML config file" do |arg|
|
|
330
379
|
opts[:config_file] = arg
|
|
331
380
|
end
|
|
332
381
|
|
|
333
|
-
o.on
|
|
334
|
-
opts[:logfile] = arg
|
|
335
|
-
end
|
|
336
|
-
|
|
337
|
-
o.on '-P', '--pidfile PATH', "path to pidfile" do |arg|
|
|
338
|
-
opts[:pidfile] = arg
|
|
339
|
-
end
|
|
340
|
-
|
|
341
|
-
o.on '-V', '--version', "Print version and exit" do |arg|
|
|
382
|
+
o.on "-V", "--version", "Print version and exit" do
|
|
342
383
|
puts "Sidekiq #{Sidekiq::VERSION}"
|
|
343
384
|
die(0)
|
|
344
385
|
end
|
|
345
|
-
|
|
386
|
+
}
|
|
346
387
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
logger.info
|
|
388
|
+
parser.banner = "sidekiq [options]"
|
|
389
|
+
parser.on_tail "-h", "--help", "Show help" do
|
|
390
|
+
logger.info parser
|
|
350
391
|
die 1
|
|
351
392
|
end
|
|
352
|
-
@parser.parse!(argv)
|
|
353
|
-
|
|
354
|
-
%w[config/sidekiq.yml config/sidekiq.yml.erb].each do |filename|
|
|
355
|
-
opts[:config_file] ||= filename if File.exist?(filename)
|
|
356
|
-
end
|
|
357
393
|
|
|
358
|
-
|
|
394
|
+
parser
|
|
359
395
|
end
|
|
360
396
|
|
|
361
397
|
def initialize_logger
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
Sidekiq.logger.level = ::Logger::DEBUG if options[:verbose]
|
|
398
|
+
@config.logger.level = ::Logger::DEBUG if @config[:verbose]
|
|
365
399
|
end
|
|
366
400
|
|
|
367
|
-
def
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
f.puts ::Process.pid
|
|
372
|
-
end
|
|
373
|
-
end
|
|
374
|
-
end
|
|
401
|
+
def parse_config(path)
|
|
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) || {}
|
|
375
405
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
if File.exist?(cfile)
|
|
379
|
-
opts = YAML.load(ERB.new(IO.read(cfile)).result) || opts
|
|
380
|
-
opts = opts.merge(opts.delete(environment) || {})
|
|
381
|
-
parse_queues(opts, opts.delete(:queues) || [])
|
|
406
|
+
if opts.respond_to? :deep_symbolize_keys!
|
|
407
|
+
opts.deep_symbolize_keys!
|
|
382
408
|
else
|
|
383
|
-
|
|
384
|
-
# can be deployed by cap with just the defaults.
|
|
385
|
-
end
|
|
386
|
-
ns = opts.delete(:namespace)
|
|
387
|
-
if ns
|
|
388
|
-
# logger hasn't been initialized yet, puts is all we have.
|
|
389
|
-
puts("namespace should be set in your ruby initializer, is ignored in config file")
|
|
390
|
-
puts("config.redis = { :url => ..., :namespace => '#{ns}' }")
|
|
409
|
+
symbolize_keys_deep!(opts)
|
|
391
410
|
end
|
|
392
|
-
opts
|
|
393
|
-
end
|
|
394
411
|
|
|
395
|
-
|
|
396
|
-
|
|
412
|
+
opts = opts.merge(opts.delete(environment.to_sym) || {})
|
|
413
|
+
opts.delete(:strict)
|
|
414
|
+
|
|
415
|
+
opts
|
|
397
416
|
end
|
|
398
417
|
|
|
399
|
-
def
|
|
400
|
-
|
|
401
|
-
(opts[:queues] ||= []) << q
|
|
402
|
-
end
|
|
403
|
-
opts[:strict] = false if weight.to_i > 0
|
|
418
|
+
def rails_app?
|
|
419
|
+
defined?(::Rails) && ::Rails.respond_to?(:application)
|
|
404
420
|
end
|
|
405
421
|
end
|
|
406
422
|
end
|
|
423
|
+
|
|
424
|
+
require "sidekiq/systemd"
|
|
425
|
+
require "sidekiq/metrics/tracking"
|
|
426
|
+
require "sidekiq/job/interrupt_handler"
|