sidekiq 6.5.10 → 8.1.7
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 +568 -18
- data/README.md +66 -38
- data/bin/kiq +17 -0
- data/bin/lint-herb +13 -0
- data/bin/multi_queue_bench +271 -0
- data/bin/sidekiq +3 -8
- data/bin/sidekiqload +215 -118
- data/bin/sidekiqmon +3 -0
- data/bin/webload +69 -0
- data/lib/active_job/queue_adapters/sidekiq_adapter.rb +124 -0
- data/lib/generators/sidekiq/job_generator.rb +17 -3
- data/lib/generators/sidekiq/templates/job.rb.erb +1 -1
- data/lib/sidekiq/api.rb +526 -218
- data/lib/sidekiq/capsule.rb +135 -0
- data/lib/sidekiq/cli.rb +88 -96
- data/lib/sidekiq/client.rb +114 -47
- data/lib/sidekiq/component.rb +71 -4
- data/lib/sidekiq/config.rb +316 -0
- data/lib/sidekiq/deploy.rb +64 -0
- data/lib/sidekiq/embedded.rb +64 -0
- data/lib/sidekiq/fetch.rb +12 -14
- data/lib/sidekiq/iterable_job.rb +56 -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 +322 -0
- data/lib/sidekiq/job.rb +397 -10
- data/lib/sidekiq/job_logger.rb +14 -11
- data/lib/sidekiq/job_retry.rb +78 -28
- data/lib/sidekiq/job_util.rb +58 -16
- data/lib/sidekiq/launcher.rb +86 -70
- data/lib/sidekiq/loader.rb +57 -0
- data/lib/sidekiq/logger.rb +25 -94
- data/lib/sidekiq/manager.rb +11 -13
- data/lib/sidekiq/metrics/query.rb +84 -48
- data/lib/sidekiq/metrics/shared.rb +29 -14
- data/lib/sidekiq/metrics/tracking.rb +49 -30
- data/lib/sidekiq/middleware/chain.rb +19 -18
- data/lib/sidekiq/middleware/current_attributes.rb +78 -21
- data/lib/sidekiq/middleware/i18n.rb +2 -0
- data/lib/sidekiq/middleware/modules.rb +2 -0
- data/lib/sidekiq/monitor.rb +15 -5
- data/lib/sidekiq/paginator.rb +26 -4
- data/lib/sidekiq/processor.rb +78 -65
- data/lib/sidekiq/profiler.rb +73 -0
- data/lib/sidekiq/rails.rb +45 -60
- data/lib/sidekiq/redis_client_adapter.rb +35 -72
- data/lib/sidekiq/redis_connection.rb +54 -114
- data/lib/sidekiq/ring_buffer.rb +3 -0
- data/lib/sidekiq/scheduled.rb +28 -30
- data/lib/sidekiq/systemd.rb +2 -0
- data/lib/sidekiq/test_api.rb +331 -0
- data/lib/sidekiq/testing/inline.rb +2 -30
- data/lib/sidekiq/testing.rb +2 -342
- data/lib/sidekiq/transaction_aware_client.rb +23 -9
- data/lib/sidekiq/tui/controls.rb +53 -0
- data/lib/sidekiq/tui/filtering.rb +53 -0
- data/lib/sidekiq/tui/tabs/base_tab.rb +230 -0
- data/lib/sidekiq/tui/tabs/busy.rb +104 -0
- data/lib/sidekiq/tui/tabs/dead.rb +14 -0
- data/lib/sidekiq/tui/tabs/home.rb +115 -0
- data/lib/sidekiq/tui/tabs/metrics.rb +114 -0
- data/lib/sidekiq/tui/tabs/queues.rb +83 -0
- data/lib/sidekiq/tui/tabs/retries.rb +14 -0
- data/lib/sidekiq/tui/tabs/scheduled.rb +14 -0
- data/lib/sidekiq/tui/tabs/set_tab.rb +91 -0
- data/lib/sidekiq/tui/tabs.rb +15 -0
- data/lib/sidekiq/tui.rb +390 -0
- data/lib/sidekiq/version.rb +6 -1
- data/lib/sidekiq/web/action.rb +149 -64
- data/lib/sidekiq/web/application.rb +379 -281
- data/lib/sidekiq/web/config.rb +117 -0
- data/lib/sidekiq/web/helpers.rb +190 -71
- data/lib/sidekiq/web/router.rb +62 -75
- data/lib/sidekiq/web.rb +68 -101
- data/lib/sidekiq/worker_compatibility_alias.rb +13 -0
- data/lib/sidekiq.rb +85 -269
- data/sidekiq.gemspec +15 -11
- data/web/assets/images/logo.png +0 -0
- data/web/assets/images/status.png +0 -0
- data/web/assets/javascripts/application.js +84 -24
- data/web/assets/javascripts/base-charts.js +120 -0
- data/web/assets/javascripts/chartjs-adapter-date-fns.min.js +7 -0
- data/web/assets/javascripts/dashboard-charts.js +194 -0
- data/web/assets/javascripts/dashboard.js +18 -234
- data/web/assets/javascripts/metrics.js +138 -120
- data/web/assets/stylesheets/style.css +776 -0
- data/web/locales/ar.yml +72 -70
- data/web/locales/cs.yml +64 -62
- data/web/locales/da.yml +62 -53
- data/web/locales/de.yml +67 -65
- data/web/locales/el.yml +4 -7
- data/web/locales/en.yml +89 -71
- data/web/locales/es.yml +91 -68
- data/web/locales/fa.yml +67 -65
- data/web/locales/fr.yml +82 -67
- data/web/locales/gd.yml +110 -0
- data/web/locales/he.yml +67 -64
- data/web/locales/hi.yml +95 -59
- data/web/locales/it.yml +94 -54
- data/web/locales/ja.yml +69 -70
- data/web/locales/ko.yml +54 -52
- data/web/locales/lt.yml +68 -66
- data/web/locales/nb.yml +63 -61
- data/web/locales/nl.yml +54 -52
- data/web/locales/pl.yml +47 -45
- data/web/locales/{pt-br.yml → pt-BR.yml} +81 -70
- data/web/locales/pt.yml +53 -51
- data/web/locales/ru.yml +69 -66
- data/web/locales/sv.yml +55 -53
- data/web/locales/ta.yml +100 -64
- data/web/locales/tr.yml +102 -0
- data/web/locales/uk.yml +87 -61
- data/web/locales/ur.yml +66 -64
- data/web/locales/vi.yml +69 -67
- data/web/locales/zh-CN.yml +107 -0
- data/web/locales/{zh-tw.yml → zh-TW.yml} +13 -3
- data/web/views/_footer.html.erb +32 -0
- data/web/views/_job_info.html.erb +115 -0
- data/web/views/_metrics_period_select.html.erb +15 -0
- data/web/views/_nav.html.erb +45 -0
- data/web/views/_paging.html.erb +26 -0
- data/web/views/_poll_link.html.erb +4 -0
- data/web/views/_summary.html.erb +40 -0
- data/web/views/busy.html.erb +155 -0
- data/web/views/dashboard.html.erb +104 -0
- data/web/views/dead.html.erb +38 -0
- data/web/views/filtering.html.erb +6 -0
- data/web/views/layout.html.erb +26 -0
- data/web/views/metrics.html.erb +85 -0
- data/web/views/metrics_for_job.html.erb +58 -0
- data/web/views/morgue.html.erb +69 -0
- data/web/views/profiles.html.erb +43 -0
- data/web/views/queue.html.erb +57 -0
- data/web/views/queues.html.erb +46 -0
- data/web/views/retries.html.erb +77 -0
- data/web/views/retry.html.erb +39 -0
- data/web/views/scheduled.html.erb +64 -0
- data/web/views/{scheduled_job_info.erb → scheduled_job_info.html.erb} +3 -3
- metadata +113 -70
- data/lib/sidekiq/delay.rb +0 -43
- data/lib/sidekiq/extensions/action_mailer.rb +0 -48
- data/lib/sidekiq/extensions/active_record.rb +0 -43
- data/lib/sidekiq/extensions/class_methods.rb +0 -43
- data/lib/sidekiq/extensions/generic_proxy.rb +0 -33
- data/lib/sidekiq/metrics/deploy.rb +0 -47
- data/lib/sidekiq/web/csrf_protection.rb +0 -180
- data/lib/sidekiq/worker.rb +0 -370
- data/web/assets/javascripts/graph.js +0 -16
- data/web/assets/stylesheets/application-dark.css +0 -143
- data/web/assets/stylesheets/application-rtl.css +0 -242
- data/web/assets/stylesheets/application.css +0 -999
- data/web/assets/stylesheets/bootstrap-rtl.min.css +0 -9
- data/web/assets/stylesheets/bootstrap.css +0 -5
- data/web/locales/zh-cn.yml +0 -93
- data/web/views/_footer.erb +0 -20
- data/web/views/_job_info.erb +0 -89
- data/web/views/_nav.erb +0 -52
- data/web/views/_paging.erb +0 -23
- data/web/views/_poll_link.erb +0 -4
- data/web/views/_status.erb +0 -4
- data/web/views/_summary.erb +0 -40
- data/web/views/busy.erb +0 -137
- data/web/views/dashboard.erb +0 -84
- data/web/views/dead.erb +0 -34
- data/web/views/layout.erb +0 -42
- data/web/views/metrics.erb +0 -69
- data/web/views/metrics_for_job.erb +0 -87
- data/web/views/morgue.erb +0 -78
- data/web/views/queue.erb +0 -59
- data/web/views/queues.erb +0 -38
- data/web/views/retries.erb +0 -83
- data/web/views/retry.erb +0 -34
- data/web/views/scheduled.erb +0 -57
- /data/{LICENSE → LICENSE.txt} +0 -0
|
@@ -11,6 +11,7 @@ module Sidekiq::Middleware::I18n
|
|
|
11
11
|
# to be sent to Sidekiq.
|
|
12
12
|
class Client
|
|
13
13
|
include Sidekiq::ClientMiddleware
|
|
14
|
+
|
|
14
15
|
def call(_jobclass, job, _queue, _redis)
|
|
15
16
|
job["locale"] ||= I18n.locale
|
|
16
17
|
yield
|
|
@@ -20,6 +21,7 @@ module Sidekiq::Middleware::I18n
|
|
|
20
21
|
# Pull the msg locale out and set the current thread to use it.
|
|
21
22
|
class Server
|
|
22
23
|
include Sidekiq::ServerMiddleware
|
|
24
|
+
|
|
23
25
|
def call(_jobclass, job, _queue, &block)
|
|
24
26
|
I18n.with_locale(job.fetch("locale", I18n.default_locale), &block)
|
|
25
27
|
end
|
data/lib/sidekiq/monitor.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
2
3
|
|
|
3
4
|
require "fileutils"
|
|
4
5
|
require "sidekiq/api"
|
|
@@ -16,8 +17,6 @@ class Sidekiq::Monitor
|
|
|
16
17
|
return
|
|
17
18
|
end
|
|
18
19
|
send(section)
|
|
19
|
-
rescue => e
|
|
20
|
-
abort "Couldn't get status: #{e}"
|
|
21
20
|
end
|
|
22
21
|
|
|
23
22
|
def all
|
|
@@ -49,10 +48,21 @@ class Sidekiq::Monitor
|
|
|
49
48
|
def processes
|
|
50
49
|
puts "---- Processes (#{process_set.size}) ----"
|
|
51
50
|
process_set.each_with_index do |process, index|
|
|
51
|
+
# Keep compatibility with legacy versions since we don't want to break sidekiqmon during rolling upgrades or downgrades.
|
|
52
|
+
queues =
|
|
53
|
+
if process["capsules"] # 8.0.6+
|
|
54
|
+
process["capsules"].values.map { |x| x["weights"].keys.join(", ") }
|
|
55
|
+
elsif process["weights"]
|
|
56
|
+
process["weights"].sort_by { |queue| queue[0] }.map { |capsule| capsule.map { |name, weight| (weight > 0) ? "#{name}: #{weight}" : name }.join(", ") }
|
|
57
|
+
else
|
|
58
|
+
process["queues"].sort
|
|
59
|
+
end
|
|
60
|
+
|
|
52
61
|
puts "#{process["identity"]} #{tags_for(process)}"
|
|
53
62
|
puts " Started: #{Time.at(process["started_at"])} (#{time_ago(process["started_at"])})"
|
|
54
63
|
puts " Threads: #{process["concurrency"]} (#{process["busy"]} busy)"
|
|
55
|
-
puts " Queues: #{split_multiline(
|
|
64
|
+
puts " Queues: #{split_multiline(queues, pad: 11)}"
|
|
65
|
+
puts " Version: #{process["version"] || "Unknown"}" if process["version"] != Sidekiq::VERSION
|
|
56
66
|
puts "" unless (index + 1) == process_set.size
|
|
57
67
|
end
|
|
58
68
|
end
|
|
@@ -85,13 +95,13 @@ class Sidekiq::Monitor
|
|
|
85
95
|
pad = opts[:pad] || 0
|
|
86
96
|
max_length = opts[:max_length] || (80 - pad)
|
|
87
97
|
out = []
|
|
88
|
-
line = ""
|
|
98
|
+
line = +""
|
|
89
99
|
values.each do |value|
|
|
90
100
|
if (line.length + value.length) > max_length
|
|
91
101
|
out << line
|
|
92
102
|
line = " " * pad
|
|
93
103
|
end
|
|
94
|
-
line << value + "
|
|
104
|
+
line << value + "; "
|
|
95
105
|
end
|
|
96
106
|
out << line[0..-3]
|
|
97
107
|
out.join("\n")
|
data/lib/sidekiq/paginator.rb
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
module Sidekiq
|
|
4
4
|
module Paginator
|
|
5
|
+
TYPE_CACHE = {
|
|
6
|
+
"dead" => "zset",
|
|
7
|
+
"retry" => "zset",
|
|
8
|
+
"schedule" => "zset"
|
|
9
|
+
}
|
|
10
|
+
|
|
5
11
|
def page(key, pageidx = 1, page_size = 25, opts = nil)
|
|
6
12
|
current_page = (pageidx.to_i < 1) ? 1 : pageidx.to_i
|
|
7
13
|
pageidx = current_page - 1
|
|
@@ -11,7 +17,15 @@ module Sidekiq
|
|
|
11
17
|
ending = starting + page_size - 1
|
|
12
18
|
|
|
13
19
|
Sidekiq.redis do |conn|
|
|
14
|
-
|
|
20
|
+
# horrible, think you can make this cleaner?
|
|
21
|
+
type = TYPE_CACHE[key]
|
|
22
|
+
if type
|
|
23
|
+
elsif key.start_with?("queue:")
|
|
24
|
+
type = TYPE_CACHE[key] = "list"
|
|
25
|
+
else
|
|
26
|
+
type = conn.type(key)
|
|
27
|
+
TYPE_CACHE[key] = type unless type == "none"
|
|
28
|
+
end
|
|
15
29
|
rev = opts && opts[:reverse]
|
|
16
30
|
|
|
17
31
|
case type
|
|
@@ -19,9 +33,9 @@ module Sidekiq
|
|
|
19
33
|
total_size, items = conn.multi { |transaction|
|
|
20
34
|
transaction.zcard(key)
|
|
21
35
|
if rev
|
|
22
|
-
transaction.
|
|
36
|
+
transaction.zrange(key, starting, ending, "REV", "withscores")
|
|
23
37
|
else
|
|
24
|
-
transaction.zrange(key, starting, ending, withscores
|
|
38
|
+
transaction.zrange(key, starting, ending, "withscores")
|
|
25
39
|
end
|
|
26
40
|
}
|
|
27
41
|
[current_page, total_size, items]
|
|
@@ -46,10 +60,18 @@ module Sidekiq
|
|
|
46
60
|
|
|
47
61
|
def page_items(items, pageidx = 1, page_size = 25)
|
|
48
62
|
current_page = (pageidx.to_i < 1) ? 1 : pageidx.to_i
|
|
63
|
+
# A negative page_size makes Array#[] return nil instead of a slice, which
|
|
64
|
+
# would crash callers iterating the result (e.g. the Web UI Busy page).
|
|
65
|
+
page_size = 0 if page_size < 0
|
|
49
66
|
pageidx = current_page - 1
|
|
50
67
|
starting = pageidx * page_size
|
|
51
68
|
items = items.to_a
|
|
52
|
-
|
|
69
|
+
total_size = items.size
|
|
70
|
+
if starting > total_size
|
|
71
|
+
starting = 0
|
|
72
|
+
current_page = 1
|
|
73
|
+
end
|
|
74
|
+
[current_page, total_size, items[starting, page_size]]
|
|
53
75
|
end
|
|
54
76
|
end
|
|
55
77
|
end
|
data/lib/sidekiq/processor.rb
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require "sidekiq/fetch"
|
|
4
4
|
require "sidekiq/job_logger"
|
|
5
5
|
require "sidekiq/job_retry"
|
|
6
|
+
require "sidekiq/profiler"
|
|
6
7
|
|
|
7
8
|
module Sidekiq
|
|
8
9
|
##
|
|
@@ -26,18 +27,19 @@ module Sidekiq
|
|
|
26
27
|
|
|
27
28
|
attr_reader :thread
|
|
28
29
|
attr_reader :job
|
|
30
|
+
attr_reader :capsule
|
|
29
31
|
|
|
30
|
-
def initialize(
|
|
32
|
+
def initialize(capsule, &block)
|
|
33
|
+
@config = capsule.config
|
|
34
|
+
@capsule = capsule
|
|
31
35
|
@callback = block
|
|
32
36
|
@down = false
|
|
33
37
|
@done = false
|
|
34
38
|
@job = nil
|
|
35
39
|
@thread = nil
|
|
36
|
-
@
|
|
37
|
-
@
|
|
38
|
-
@
|
|
39
|
-
@job_logger = (options[:job_logger] || Sidekiq::JobLogger).new
|
|
40
|
-
@retrier = Sidekiq::JobRetry.new(options)
|
|
40
|
+
@reloader = Sidekiq.default_configuration[:reloader]
|
|
41
|
+
@job_logger = (capsule.config[:job_logger] || Sidekiq::JobLogger).new(@config)
|
|
42
|
+
@retrier = Sidekiq::JobRetry.new(@capsule)
|
|
41
43
|
end
|
|
42
44
|
|
|
43
45
|
def terminate(wait = false)
|
|
@@ -58,13 +60,21 @@ module Sidekiq
|
|
|
58
60
|
@thread.value if wait
|
|
59
61
|
end
|
|
60
62
|
|
|
63
|
+
def stopping?
|
|
64
|
+
@done
|
|
65
|
+
end
|
|
66
|
+
|
|
61
67
|
def start
|
|
62
|
-
@thread ||= safe_thread("processor", &method(:run))
|
|
68
|
+
@thread ||= safe_thread("#{capsule.name}/processor", &method(:run))
|
|
63
69
|
end
|
|
64
70
|
|
|
65
|
-
private
|
|
71
|
+
private
|
|
66
72
|
|
|
67
73
|
def run
|
|
74
|
+
# By setting this thread-local, Sidekiq.redis will access +Sidekiq::Capsule#redis_pool+
|
|
75
|
+
# instead of the global pool in +Sidekiq::Config#redis_pool+.
|
|
76
|
+
Thread.current[:sidekiq_capsule] = @capsule
|
|
77
|
+
|
|
68
78
|
process_one until @done
|
|
69
79
|
@callback.call(self)
|
|
70
80
|
rescue Sidekiq::Shutdown
|
|
@@ -80,7 +90,7 @@ module Sidekiq
|
|
|
80
90
|
end
|
|
81
91
|
|
|
82
92
|
def get_one
|
|
83
|
-
uow =
|
|
93
|
+
uow = capsule.fetcher.retrieve_work
|
|
84
94
|
if @down
|
|
85
95
|
logger.info { "Redis is online, #{::Process.clock_gettime(::Process::CLOCK_MONOTONIC) - @down} sec downtime" }
|
|
86
96
|
@down = nil
|
|
@@ -104,13 +114,17 @@ module Sidekiq
|
|
|
104
114
|
def handle_fetch_exception(ex)
|
|
105
115
|
unless @down
|
|
106
116
|
@down = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
|
107
|
-
logger.error("Error fetching job: #{ex}")
|
|
108
117
|
handle_exception(ex)
|
|
109
118
|
end
|
|
110
119
|
sleep(1)
|
|
111
120
|
nil
|
|
112
121
|
end
|
|
113
122
|
|
|
123
|
+
def profile(job, &block)
|
|
124
|
+
return yield unless job["profile"]
|
|
125
|
+
Sidekiq::Profiler.new(config).call(job, &block)
|
|
126
|
+
end
|
|
127
|
+
|
|
114
128
|
def dispatch(job_hash, queue, jobstr)
|
|
115
129
|
# since middleware can mutate the job hash
|
|
116
130
|
# we need to clone it to report the original
|
|
@@ -124,16 +138,19 @@ module Sidekiq
|
|
|
124
138
|
@retrier.global(jobstr, queue) do
|
|
125
139
|
@job_logger.call(job_hash, queue) do
|
|
126
140
|
stats(jobstr, queue) do
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
141
|
+
profile(job_hash) do
|
|
142
|
+
# Rails 5 requires a Reloader to wrap code execution. In order to
|
|
143
|
+
# constantize the worker and instantiate an instance, we have to call
|
|
144
|
+
# the Reloader. It handles code loading, db connection management, etc.
|
|
145
|
+
# Effectively this block denotes a "unit of work" to Rails.
|
|
146
|
+
@reloader.call do
|
|
147
|
+
klass = Object.const_get(job_hash["class"])
|
|
148
|
+
instance = klass.new
|
|
149
|
+
instance.jid = job_hash["jid"]
|
|
150
|
+
instance._context = self
|
|
151
|
+
@retrier.local(instance, jobstr, queue) do
|
|
152
|
+
yield instance
|
|
153
|
+
end
|
|
137
154
|
end
|
|
138
155
|
end
|
|
139
156
|
end
|
|
@@ -142,6 +159,11 @@ module Sidekiq
|
|
|
142
159
|
end
|
|
143
160
|
end
|
|
144
161
|
|
|
162
|
+
IGNORE_SHUTDOWN_INTERRUPTS = {Sidekiq::Shutdown => :never}
|
|
163
|
+
private_constant :IGNORE_SHUTDOWN_INTERRUPTS
|
|
164
|
+
ALLOW_SHUTDOWN_INTERRUPTS = {Sidekiq::Shutdown => :immediate}
|
|
165
|
+
private_constant :ALLOW_SHUTDOWN_INTERRUPTS
|
|
166
|
+
|
|
145
167
|
def process(uow)
|
|
146
168
|
jobstr = uow.job
|
|
147
169
|
queue = uow.queue_name
|
|
@@ -151,55 +173,59 @@ module Sidekiq
|
|
|
151
173
|
begin
|
|
152
174
|
job_hash = Sidekiq.load_json(jobstr)
|
|
153
175
|
rescue => ex
|
|
154
|
-
handle_exception(ex, {context: "Invalid JSON for job", jobstr: jobstr})
|
|
155
176
|
now = Time.now.to_f
|
|
156
|
-
|
|
177
|
+
redis do |conn|
|
|
157
178
|
conn.multi do |xa|
|
|
158
179
|
xa.zadd("dead", now.to_s, jobstr)
|
|
159
|
-
xa.zremrangebyscore("dead", "-inf", now - config[:dead_timeout_in_seconds])
|
|
160
|
-
xa.zremrangebyrank("dead", 0, - config[:dead_max_jobs])
|
|
180
|
+
xa.zremrangebyscore("dead", "-inf", now - @capsule.config[:dead_timeout_in_seconds])
|
|
181
|
+
xa.zremrangebyrank("dead", 0, - @capsule.config[:dead_max_jobs])
|
|
161
182
|
end
|
|
162
183
|
end
|
|
184
|
+
handle_exception(ex, {context: "Invalid JSON for job", jobstr: jobstr})
|
|
163
185
|
return uow.acknowledge
|
|
164
186
|
end
|
|
165
187
|
|
|
166
188
|
ack = false
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
189
|
+
Thread.handle_interrupt(IGNORE_SHUTDOWN_INTERRUPTS) do
|
|
190
|
+
Thread.handle_interrupt(ALLOW_SHUTDOWN_INTERRUPTS) do
|
|
191
|
+
dispatch(job_hash, queue, jobstr) do |instance|
|
|
192
|
+
config.server_middleware.invoke(instance, job_hash, queue) do
|
|
193
|
+
execute_job(instance, job_hash["args"])
|
|
194
|
+
end
|
|
171
195
|
end
|
|
196
|
+
ack = true
|
|
197
|
+
rescue Sidekiq::Shutdown
|
|
198
|
+
# Had to force kill this job because it didn't finish
|
|
199
|
+
# within the timeout. Don't acknowledge the work since
|
|
200
|
+
# we didn't properly finish it.
|
|
201
|
+
rescue Sidekiq::JobRetry::Skip => s
|
|
202
|
+
# Skip means we handled this error elsewhere. We don't
|
|
203
|
+
# need to log or report the error.
|
|
204
|
+
ack = true
|
|
205
|
+
raise s
|
|
206
|
+
rescue Sidekiq::JobRetry::Handled => h
|
|
207
|
+
# this is the common case: job raised error and Sidekiq::JobRetry::Handled
|
|
208
|
+
# signals that we created a retry successfully. We can acknowledge the job.
|
|
209
|
+
ack = true
|
|
210
|
+
e = h.cause || h
|
|
211
|
+
handle_exception(e, {context: "Job raised exception", job: job_hash})
|
|
212
|
+
raise e
|
|
213
|
+
rescue Exception => ex
|
|
214
|
+
# Unexpected error! This is very bad and indicates an exception that got past
|
|
215
|
+
# the retry subsystem (e.g. network partition). We won't acknowledge the job
|
|
216
|
+
# so it can be rescued when using Sidekiq Pro.
|
|
217
|
+
handle_exception(ex, {context: "Internal exception!", job: job_hash, jobstr: jobstr})
|
|
218
|
+
raise ex
|
|
172
219
|
end
|
|
173
|
-
ack = true
|
|
174
|
-
rescue Sidekiq::Shutdown
|
|
175
|
-
# Had to force kill this job because it didn't finish
|
|
176
|
-
# within the timeout. Don't acknowledge the work since
|
|
177
|
-
# we didn't properly finish it.
|
|
178
|
-
rescue Sidekiq::JobRetry::Handled => h
|
|
179
|
-
# this is the common case: job raised error and Sidekiq::JobRetry::Handled
|
|
180
|
-
# signals that we created a retry successfully. We can acknowlege the job.
|
|
181
|
-
ack = true
|
|
182
|
-
e = h.cause || h
|
|
183
|
-
handle_exception(e, {context: "Job raised exception", job: job_hash})
|
|
184
|
-
raise e
|
|
185
|
-
rescue Exception => ex
|
|
186
|
-
# Unexpected error! This is very bad and indicates an exception that got past
|
|
187
|
-
# the retry subsystem (e.g. network partition). We won't acknowledge the job
|
|
188
|
-
# so it can be rescued when using Sidekiq Pro.
|
|
189
|
-
handle_exception(ex, {context: "Internal exception!", job: job_hash, jobstr: jobstr})
|
|
190
|
-
raise ex
|
|
191
220
|
ensure
|
|
192
221
|
if ack
|
|
193
|
-
|
|
194
|
-
Thread.handle_interrupt(Sidekiq::Shutdown => :never) do
|
|
195
|
-
uow.acknowledge
|
|
196
|
-
end
|
|
222
|
+
uow.acknowledge
|
|
197
223
|
end
|
|
198
224
|
end
|
|
199
225
|
end
|
|
200
226
|
|
|
201
|
-
def execute_job(
|
|
202
|
-
|
|
227
|
+
def execute_job(instance, cloned_args)
|
|
228
|
+
instance.perform(*cloned_args)
|
|
203
229
|
end
|
|
204
230
|
|
|
205
231
|
# Ruby doesn't provide atomic counters out of the box so we'll
|
|
@@ -269,18 +295,5 @@ module Sidekiq
|
|
|
269
295
|
PROCESSED.incr
|
|
270
296
|
end
|
|
271
297
|
end
|
|
272
|
-
|
|
273
|
-
def constantize(str)
|
|
274
|
-
return Object.const_get(str) unless str.include?("::")
|
|
275
|
-
|
|
276
|
-
names = str.split("::")
|
|
277
|
-
names.shift if names.empty? || names.first.empty?
|
|
278
|
-
|
|
279
|
-
names.inject(Object) do |constant, name|
|
|
280
|
-
# the false flag limits search for name to under the constant namespace
|
|
281
|
-
# which mimics Rails' behaviour
|
|
282
|
-
constant.const_get(name, false)
|
|
283
|
-
end
|
|
284
|
-
end
|
|
285
298
|
end
|
|
286
299
|
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
require "fileutils"
|
|
2
|
+
require "sidekiq/component"
|
|
3
|
+
|
|
4
|
+
module Sidekiq
|
|
5
|
+
# Allows the user to profile jobs running in production.
|
|
6
|
+
# See details in the Profiling wiki page.
|
|
7
|
+
class Profiler
|
|
8
|
+
EXPIRY = 86400 # 1 day
|
|
9
|
+
DEFAULT_OPTIONS = {
|
|
10
|
+
mode: :wall
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
include Sidekiq::Component
|
|
14
|
+
|
|
15
|
+
def initialize(config)
|
|
16
|
+
@config = config
|
|
17
|
+
@vernier_output_dir = ENV.fetch("VERNIER_OUTPUT_DIR") { Dir.tmpdir }
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def call(job, &block)
|
|
21
|
+
return yield unless job["profile"]
|
|
22
|
+
|
|
23
|
+
token = job["profile"]
|
|
24
|
+
type = job["wrapped"] || job["class"]
|
|
25
|
+
jid = job["jid"]
|
|
26
|
+
started_at = Time.now
|
|
27
|
+
|
|
28
|
+
rundata = {
|
|
29
|
+
started_at: started_at.to_i,
|
|
30
|
+
token: token,
|
|
31
|
+
type: type,
|
|
32
|
+
jid: jid,
|
|
33
|
+
# .gz extension tells Vernier to compress the data
|
|
34
|
+
filename: File.join(
|
|
35
|
+
@vernier_output_dir,
|
|
36
|
+
"#{token}-#{type}-#{jid}-#{started_at.strftime("%Y%m%d-%H%M%S")}.json.gz"
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
profiler_options = profiler_options(job, rundata)
|
|
40
|
+
|
|
41
|
+
require "vernier"
|
|
42
|
+
begin
|
|
43
|
+
a = Time.now
|
|
44
|
+
rc = Vernier.profile(**profiler_options, &block)
|
|
45
|
+
b = Time.now
|
|
46
|
+
|
|
47
|
+
# Failed jobs will raise an exception on previous line and skip this
|
|
48
|
+
# block. Only successful jobs will persist profile data to Redis.
|
|
49
|
+
key = "#{token}-#{jid}"
|
|
50
|
+
data = File.read(rundata[:filename])
|
|
51
|
+
redis do |conn|
|
|
52
|
+
conn.multi do |m|
|
|
53
|
+
m.zadd("profiles", Time.now.to_f + EXPIRY, key)
|
|
54
|
+
m.hset(key, rundata.merge(elapsed: (b - a), data: data, size: data.bytesize))
|
|
55
|
+
m.expire(key, EXPIRY)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
rc
|
|
59
|
+
ensure
|
|
60
|
+
FileUtils.rm_f(rundata[:filename])
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
private
|
|
65
|
+
|
|
66
|
+
def profiler_options(job, rundata)
|
|
67
|
+
profiler_options = (job["profiler_options"] || {}).transform_keys(&:to_sym)
|
|
68
|
+
profiler_options[:mode] = profiler_options[:mode].to_sym if profiler_options[:mode]
|
|
69
|
+
|
|
70
|
+
DEFAULT_OPTIONS.merge(profiler_options, {out: rundata[:filename]})
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
data/lib/sidekiq/rails.rb
CHANGED
|
@@ -1,78 +1,63 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "sidekiq/job"
|
|
4
|
-
|
|
5
3
|
module Sidekiq
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
4
|
+
begin
|
|
5
|
+
gem "railties", ">= 7.0"
|
|
6
|
+
require "rails"
|
|
7
|
+
require "sidekiq/job"
|
|
8
|
+
require_relative "../active_job/queue_adapters/sidekiq_adapter"
|
|
9
|
+
|
|
10
|
+
class Rails < ::Rails::Engine
|
|
11
|
+
class Reloader
|
|
12
|
+
def initialize(app = ::Rails.application)
|
|
13
|
+
@app = app
|
|
14
|
+
end
|
|
11
15
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
16
|
+
def call
|
|
17
|
+
params = (::Rails::VERSION::STRING >= "7.1") ? {source: "job.sidekiq"} : {}
|
|
18
|
+
@app.reloader.wrap(**params) do
|
|
19
|
+
yield
|
|
20
|
+
end
|
|
15
21
|
end
|
|
16
|
-
end
|
|
17
22
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
end
|
|
23
|
+
def inspect
|
|
24
|
+
"#<Sidekiq::Rails::Reloader @app=#{@app.class.name}>"
|
|
25
|
+
end
|
|
22
26
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
# manually retried, don't automatically die, etc.
|
|
27
|
-
#
|
|
28
|
-
# class SomeJob < ActiveJob::Base
|
|
29
|
-
# queue_as :default
|
|
30
|
-
# sidekiq_options retry: 3, backtrace: 10
|
|
31
|
-
# def perform
|
|
32
|
-
# end
|
|
33
|
-
# end
|
|
34
|
-
initializer "sidekiq.active_job_integration" do
|
|
35
|
-
ActiveSupport.on_load(:active_job) do
|
|
36
|
-
include ::Sidekiq::Job::Options unless respond_to?(:sidekiq_options)
|
|
27
|
+
def to_hash
|
|
28
|
+
{app: @app.class.name}
|
|
29
|
+
end
|
|
37
30
|
end
|
|
38
|
-
end
|
|
39
31
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
# it will appear in the Sidekiq console with all of the job context. See #5021 and
|
|
44
|
-
# https://github.com/rails/rails/blob/b5f2b550f69a99336482739000c58e4e04e033aa/railties/lib/rails/commands/server/server_command.rb#L82-L84
|
|
45
|
-
unless ::Rails.logger == config.logger || ::ActiveSupport::Logger.logger_outputs_to?(::Rails.logger, $stdout)
|
|
46
|
-
::Rails.logger.extend(::ActiveSupport::Logger.broadcast(config.logger))
|
|
32
|
+
initializer "sidekiq.backtrace_cleaner" do
|
|
33
|
+
Sidekiq.configure_server do |config|
|
|
34
|
+
config[:backtrace_cleaner] = ->(backtrace) { ::Rails.backtrace_cleaner.clean(backtrace) }
|
|
47
35
|
end
|
|
48
36
|
end
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
config.before_configuration do
|
|
52
|
-
dep = ActiveSupport::Deprecation.new("7.0", "Sidekiq")
|
|
53
|
-
dep.deprecate_methods(Sidekiq.singleton_class,
|
|
54
|
-
default_worker_options: :default_job_options,
|
|
55
|
-
"default_worker_options=": :default_job_options=)
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
# This hook happens after all initializers are run, just before returning
|
|
59
|
-
# from config/environment.rb back to sidekiq/cli.rb.
|
|
60
|
-
#
|
|
61
|
-
# None of this matters on the client-side, only within the Sidekiq process itself.
|
|
62
|
-
config.after_initialize do
|
|
63
|
-
Sidekiq.configure_server do |config|
|
|
64
|
-
config[:reloader] = Sidekiq::Rails::Reloader.new
|
|
65
37
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
38
|
+
# This hook happens after all initializers are run, just before returning
|
|
39
|
+
# from config/environment.rb back to sidekiq/cli.rb.
|
|
40
|
+
#
|
|
41
|
+
# None of this matters on the client-side, only within the Sidekiq process itself.
|
|
42
|
+
config.after_initialize do
|
|
43
|
+
Sidekiq.configure_server do |config|
|
|
44
|
+
config[:reloader] = Sidekiq::Rails::Reloader.new
|
|
45
|
+
|
|
46
|
+
# This is the integration code necessary so that if a job uses `Rails.logger.info "Hello"`,
|
|
47
|
+
# it will appear in the Sidekiq console with all of the job context.
|
|
48
|
+
unless ::Rails.logger == config.logger || ::ActiveSupport::Logger.logger_outputs_to?(::Rails.logger, $stdout)
|
|
49
|
+
if ::Rails.logger.respond_to?(:broadcast_to)
|
|
50
|
+
::Rails.logger.broadcast_to(config.logger)
|
|
51
|
+
elsif ::ActiveSupport::Logger.respond_to?(:broadcast)
|
|
52
|
+
::Rails.logger.extend(::ActiveSupport::Logger.broadcast(config.logger))
|
|
53
|
+
else
|
|
54
|
+
::Rails.logger = ::ActiveSupport::BroadcastLogger.new(::Rails.logger, config.logger)
|
|
55
|
+
end
|
|
73
56
|
end
|
|
74
57
|
end
|
|
75
58
|
end
|
|
76
59
|
end
|
|
60
|
+
rescue Gem::LoadError
|
|
61
|
+
# Rails not available or version requirement not met
|
|
77
62
|
end
|
|
78
63
|
end
|