sidekiq 6.5.1 → 7.0.9
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 +142 -12
- data/README.md +40 -32
- data/bin/sidekiq +3 -8
- data/bin/sidekiqload +186 -118
- data/bin/sidekiqmon +3 -0
- data/lib/sidekiq/api.rb +226 -139
- data/lib/sidekiq/capsule.rb +127 -0
- data/lib/sidekiq/cli.rb +55 -61
- data/lib/sidekiq/client.rb +31 -18
- data/lib/sidekiq/component.rb +5 -1
- data/lib/sidekiq/config.rb +270 -0
- data/lib/sidekiq/deploy.rb +62 -0
- data/lib/sidekiq/embedded.rb +61 -0
- data/lib/sidekiq/fetch.rb +11 -14
- data/lib/sidekiq/job.rb +375 -10
- data/lib/sidekiq/job_logger.rb +2 -2
- data/lib/sidekiq/job_retry.rb +62 -41
- data/lib/sidekiq/job_util.rb +48 -14
- data/lib/sidekiq/launcher.rb +71 -65
- data/lib/sidekiq/logger.rb +1 -26
- data/lib/sidekiq/manager.rb +9 -11
- data/lib/sidekiq/metrics/query.rb +153 -0
- data/lib/sidekiq/metrics/shared.rb +95 -0
- data/lib/sidekiq/metrics/tracking.rb +136 -0
- data/lib/sidekiq/middleware/chain.rb +84 -48
- data/lib/sidekiq/middleware/current_attributes.rb +12 -17
- data/lib/sidekiq/monitor.rb +17 -4
- data/lib/sidekiq/paginator.rb +9 -1
- data/lib/sidekiq/processor.rb +27 -27
- data/lib/sidekiq/rails.rb +4 -9
- data/lib/sidekiq/redis_client_adapter.rb +8 -47
- data/lib/sidekiq/redis_connection.rb +11 -113
- data/lib/sidekiq/scheduled.rb +60 -33
- data/lib/sidekiq/testing.rb +5 -33
- data/lib/sidekiq/transaction_aware_client.rb +4 -5
- data/lib/sidekiq/version.rb +2 -1
- data/lib/sidekiq/web/action.rb +3 -3
- data/lib/sidekiq/web/application.rb +40 -9
- data/lib/sidekiq/web/csrf_protection.rb +1 -1
- data/lib/sidekiq/web/helpers.rb +32 -18
- data/lib/sidekiq/web.rb +7 -14
- data/lib/sidekiq/worker_compatibility_alias.rb +13 -0
- data/lib/sidekiq.rb +76 -266
- data/sidekiq.gemspec +21 -10
- data/web/assets/javascripts/application.js +19 -1
- 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 +166 -0
- data/web/assets/javascripts/dashboard.js +3 -240
- data/web/assets/javascripts/metrics.js +264 -0
- data/web/assets/stylesheets/application-dark.css +4 -0
- data/web/assets/stylesheets/application-rtl.css +2 -91
- data/web/assets/stylesheets/application.css +65 -297
- data/web/locales/ar.yml +70 -70
- data/web/locales/cs.yml +62 -62
- data/web/locales/da.yml +60 -53
- data/web/locales/de.yml +65 -65
- data/web/locales/el.yml +43 -24
- data/web/locales/en.yml +82 -69
- data/web/locales/es.yml +68 -68
- data/web/locales/fa.yml +65 -65
- data/web/locales/fr.yml +67 -67
- 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 +73 -68
- data/web/locales/ko.yml +52 -52
- data/web/locales/lt.yml +66 -66
- 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 +59 -69
- data/web/locales/pt.yml +51 -51
- data/web/locales/ru.yml +67 -66
- 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 +67 -67
- data/web/locales/zh-cn.yml +43 -16
- data/web/locales/zh-tw.yml +42 -8
- data/web/views/_footer.erb +5 -2
- data/web/views/_job_info.erb +18 -2
- 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 +1 -1
- data/web/views/busy.erb +43 -27
- data/web/views/dashboard.erb +36 -4
- data/web/views/metrics.erb +82 -0
- data/web/views/metrics_for_job.erb +68 -0
- data/web/views/morgue.erb +5 -9
- data/web/views/queue.erb +15 -15
- data/web/views/queues.erb +3 -1
- data/web/views/retries.erb +5 -9
- data/web/views/scheduled.erb +12 -13
- metadata +60 -27
- data/lib/sidekiq/.DS_Store +0 -0
- 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/worker.rb +0 -367
- /data/{LICENSE → LICENSE.txt} +0 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "time"
|
|
4
|
+
require "sidekiq"
|
|
5
|
+
require "sidekiq/metrics/shared"
|
|
6
|
+
|
|
7
|
+
# This file contains the components which track execution metrics within Sidekiq.
|
|
8
|
+
module Sidekiq
|
|
9
|
+
module Metrics
|
|
10
|
+
class ExecutionTracker
|
|
11
|
+
include Sidekiq::Component
|
|
12
|
+
|
|
13
|
+
def initialize(config)
|
|
14
|
+
@config = config
|
|
15
|
+
@jobs = Hash.new(0)
|
|
16
|
+
@totals = Hash.new(0)
|
|
17
|
+
@grams = Hash.new { |hash, key| hash[key] = Histogram.new(key) }
|
|
18
|
+
@lock = Mutex.new
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def track(queue, klass)
|
|
22
|
+
start = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :millisecond)
|
|
23
|
+
time_ms = 0
|
|
24
|
+
begin
|
|
25
|
+
begin
|
|
26
|
+
yield
|
|
27
|
+
ensure
|
|
28
|
+
finish = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :millisecond)
|
|
29
|
+
time_ms = finish - start
|
|
30
|
+
end
|
|
31
|
+
# We don't track time for failed jobs as they can have very unpredictable
|
|
32
|
+
# execution times. more important to know average time for successful jobs so we
|
|
33
|
+
# can better recognize when a perf regression is introduced.
|
|
34
|
+
@lock.synchronize {
|
|
35
|
+
@grams[klass].record_time(time_ms)
|
|
36
|
+
@jobs["#{klass}|ms"] += time_ms
|
|
37
|
+
@totals["ms"] += time_ms
|
|
38
|
+
}
|
|
39
|
+
rescue Exception
|
|
40
|
+
@lock.synchronize {
|
|
41
|
+
@jobs["#{klass}|f"] += 1
|
|
42
|
+
@totals["f"] += 1
|
|
43
|
+
}
|
|
44
|
+
raise
|
|
45
|
+
ensure
|
|
46
|
+
@lock.synchronize {
|
|
47
|
+
@jobs["#{klass}|p"] += 1
|
|
48
|
+
@totals["p"] += 1
|
|
49
|
+
}
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# LONG_TERM = 90 * 24 * 60 * 60
|
|
54
|
+
# MID_TERM = 7 * 24 * 60 * 60
|
|
55
|
+
SHORT_TERM = 8 * 60 * 60
|
|
56
|
+
|
|
57
|
+
def flush(time = Time.now)
|
|
58
|
+
totals, jobs, grams = reset
|
|
59
|
+
procd = totals["p"]
|
|
60
|
+
fails = totals["f"]
|
|
61
|
+
return if procd == 0 && fails == 0
|
|
62
|
+
|
|
63
|
+
now = time.utc
|
|
64
|
+
# nowdate = now.strftime("%Y%m%d")
|
|
65
|
+
# nowhour = now.strftime("%Y%m%d|%-H")
|
|
66
|
+
nowmin = now.strftime("%Y%m%d|%-H:%-M")
|
|
67
|
+
count = 0
|
|
68
|
+
|
|
69
|
+
redis do |conn|
|
|
70
|
+
# persist fine-grained histogram data
|
|
71
|
+
if grams.size > 0
|
|
72
|
+
conn.pipelined do |pipe|
|
|
73
|
+
grams.each do |_, gram|
|
|
74
|
+
gram.persist(pipe, now)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# persist coarse grained execution count + execution millis.
|
|
80
|
+
# note as of today we don't use or do anything with the
|
|
81
|
+
# daily or hourly rollups.
|
|
82
|
+
[
|
|
83
|
+
# ["j", jobs, nowdate, LONG_TERM],
|
|
84
|
+
# ["j", jobs, nowhour, MID_TERM],
|
|
85
|
+
["j", jobs, nowmin, SHORT_TERM]
|
|
86
|
+
].each do |prefix, data, bucket, ttl|
|
|
87
|
+
conn.pipelined do |xa|
|
|
88
|
+
stats = "#{prefix}|#{bucket}"
|
|
89
|
+
data.each_pair do |key, value|
|
|
90
|
+
xa.hincrby stats, key, value
|
|
91
|
+
count += 1
|
|
92
|
+
end
|
|
93
|
+
xa.expire(stats, ttl)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
logger.debug "Flushed #{count} metrics"
|
|
97
|
+
count
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
private
|
|
102
|
+
|
|
103
|
+
def reset
|
|
104
|
+
@lock.synchronize {
|
|
105
|
+
array = [@totals, @jobs, @grams]
|
|
106
|
+
@totals = Hash.new(0)
|
|
107
|
+
@jobs = Hash.new(0)
|
|
108
|
+
@grams = Hash.new { |hash, key| hash[key] = Histogram.new(key) }
|
|
109
|
+
array
|
|
110
|
+
}
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
class Middleware
|
|
115
|
+
include Sidekiq::ServerMiddleware
|
|
116
|
+
|
|
117
|
+
def initialize(options)
|
|
118
|
+
@exec = options
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def call(_instance, hash, queue, &block)
|
|
122
|
+
@exec.track(queue, hash["wrapped"] || hash["class"], &block)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
Sidekiq.configure_server do |config|
|
|
129
|
+
exec = Sidekiq::Metrics::ExecutionTracker.new(config)
|
|
130
|
+
config.server_middleware do |chain|
|
|
131
|
+
chain.add Sidekiq::Metrics::Middleware, exec
|
|
132
|
+
end
|
|
133
|
+
config.on(:beat) do
|
|
134
|
+
exec.flush
|
|
135
|
+
end
|
|
136
|
+
end
|
|
@@ -4,84 +4,89 @@ require "sidekiq/middleware/modules"
|
|
|
4
4
|
|
|
5
5
|
module Sidekiq
|
|
6
6
|
# Middleware is code configured to run before/after
|
|
7
|
-
# a
|
|
7
|
+
# a job is processed. It is patterned after Rack
|
|
8
8
|
# middleware. Middleware exists for the client side
|
|
9
9
|
# (pushing jobs onto the queue) as well as the server
|
|
10
10
|
# side (when jobs are actually processed).
|
|
11
11
|
#
|
|
12
|
+
# Callers will register middleware Classes and Sidekiq will
|
|
13
|
+
# create new instances of the middleware for every job. This
|
|
14
|
+
# is important so that instance state is not shared accidentally
|
|
15
|
+
# between job executions.
|
|
16
|
+
#
|
|
12
17
|
# To add middleware for the client:
|
|
13
18
|
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
19
|
+
# Sidekiq.configure_client do |config|
|
|
20
|
+
# config.client_middleware do |chain|
|
|
21
|
+
# chain.add MyClientHook
|
|
22
|
+
# end
|
|
17
23
|
# end
|
|
18
|
-
# end
|
|
19
24
|
#
|
|
20
25
|
# To modify middleware for the server, just call
|
|
21
26
|
# with another block:
|
|
22
27
|
#
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
#
|
|
28
|
+
# Sidekiq.configure_server do |config|
|
|
29
|
+
# config.server_middleware do |chain|
|
|
30
|
+
# chain.add MyServerHook
|
|
31
|
+
# chain.remove ActiveRecord
|
|
32
|
+
# end
|
|
27
33
|
# end
|
|
28
|
-
# end
|
|
29
34
|
#
|
|
30
35
|
# To insert immediately preceding another entry:
|
|
31
36
|
#
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
#
|
|
37
|
+
# Sidekiq.configure_client do |config|
|
|
38
|
+
# config.client_middleware do |chain|
|
|
39
|
+
# chain.insert_before ActiveRecord, MyClientHook
|
|
40
|
+
# end
|
|
35
41
|
# end
|
|
36
|
-
# end
|
|
37
42
|
#
|
|
38
43
|
# To insert immediately after another entry:
|
|
39
44
|
#
|
|
40
|
-
#
|
|
41
|
-
#
|
|
42
|
-
#
|
|
45
|
+
# Sidekiq.configure_client do |config|
|
|
46
|
+
# config.client_middleware do |chain|
|
|
47
|
+
# chain.insert_after ActiveRecord, MyClientHook
|
|
48
|
+
# end
|
|
43
49
|
# end
|
|
44
|
-
# end
|
|
45
50
|
#
|
|
46
51
|
# This is an example of a minimal server middleware:
|
|
47
52
|
#
|
|
48
|
-
#
|
|
49
|
-
#
|
|
50
|
-
#
|
|
51
|
-
#
|
|
52
|
-
#
|
|
53
|
-
#
|
|
54
|
-
#
|
|
53
|
+
# class MyServerHook
|
|
54
|
+
# include Sidekiq::ServerMiddleware
|
|
55
|
+
#
|
|
56
|
+
# def call(job_instance, msg, queue)
|
|
57
|
+
# logger.info "Before job"
|
|
58
|
+
# redis {|conn| conn.get("foo") } # do something in Redis
|
|
59
|
+
# yield
|
|
60
|
+
# logger.info "After job"
|
|
61
|
+
# end
|
|
55
62
|
# end
|
|
56
|
-
# end
|
|
57
63
|
#
|
|
58
64
|
# This is an example of a minimal client middleware, note
|
|
59
65
|
# the method must return the result or the job will not push
|
|
60
66
|
# to Redis:
|
|
61
67
|
#
|
|
62
|
-
#
|
|
63
|
-
#
|
|
64
|
-
#
|
|
65
|
-
#
|
|
66
|
-
#
|
|
67
|
-
#
|
|
68
|
-
#
|
|
68
|
+
# class MyClientHook
|
|
69
|
+
# include Sidekiq::ClientMiddleware
|
|
70
|
+
#
|
|
71
|
+
# def call(job_class, msg, queue, redis_pool)
|
|
72
|
+
# logger.info "Before push"
|
|
73
|
+
# result = yield
|
|
74
|
+
# logger.info "After push"
|
|
75
|
+
# result
|
|
76
|
+
# end
|
|
69
77
|
# end
|
|
70
|
-
# end
|
|
71
78
|
#
|
|
72
79
|
module Middleware
|
|
73
80
|
class Chain
|
|
74
81
|
include Enumerable
|
|
75
82
|
|
|
76
|
-
|
|
77
|
-
copy.instance_variable_set(:@entries, entries.dup)
|
|
78
|
-
end
|
|
79
|
-
|
|
83
|
+
# Iterate through each middleware in the chain
|
|
80
84
|
def each(&block)
|
|
81
85
|
entries.each(&block)
|
|
82
86
|
end
|
|
83
87
|
|
|
84
|
-
|
|
88
|
+
# @api private
|
|
89
|
+
def initialize(config = nil) # :nodoc:
|
|
85
90
|
@config = config
|
|
86
91
|
@entries = nil
|
|
87
92
|
yield self if block_given?
|
|
@@ -91,20 +96,39 @@ module Sidekiq
|
|
|
91
96
|
@entries ||= []
|
|
92
97
|
end
|
|
93
98
|
|
|
99
|
+
def copy_for(capsule)
|
|
100
|
+
chain = Sidekiq::Middleware::Chain.new(capsule)
|
|
101
|
+
chain.instance_variable_set(:@entries, entries.dup)
|
|
102
|
+
chain
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Remove all middleware matching the given Class
|
|
106
|
+
# @param klass [Class]
|
|
94
107
|
def remove(klass)
|
|
95
108
|
entries.delete_if { |entry| entry.klass == klass }
|
|
96
109
|
end
|
|
97
110
|
|
|
111
|
+
# Add the given middleware to the end of the chain.
|
|
112
|
+
# Sidekiq will call `klass.new(*args)` to create a clean
|
|
113
|
+
# copy of your middleware for every job executed.
|
|
114
|
+
#
|
|
115
|
+
# chain.add(Statsd::Metrics, { collector: "localhost:8125" })
|
|
116
|
+
#
|
|
117
|
+
# @param klass [Class] Your middleware class
|
|
118
|
+
# @param *args [Array<Object>] Set of arguments to pass to every instance of your middleware
|
|
98
119
|
def add(klass, *args)
|
|
99
120
|
remove(klass)
|
|
100
121
|
entries << Entry.new(@config, klass, *args)
|
|
101
122
|
end
|
|
102
123
|
|
|
124
|
+
# Identical to {#add} except the middleware is added to the front of the chain.
|
|
103
125
|
def prepend(klass, *args)
|
|
104
126
|
remove(klass)
|
|
105
127
|
entries.insert(0, Entry.new(@config, klass, *args))
|
|
106
128
|
end
|
|
107
129
|
|
|
130
|
+
# Inserts +newklass+ before +oldklass+ in the chain.
|
|
131
|
+
# Useful if one middleware must run before another middleware.
|
|
108
132
|
def insert_before(oldklass, newklass, *args)
|
|
109
133
|
i = entries.index { |entry| entry.klass == newklass }
|
|
110
134
|
new_entry = i.nil? ? Entry.new(@config, newklass, *args) : entries.delete_at(i)
|
|
@@ -112,6 +136,8 @@ module Sidekiq
|
|
|
112
136
|
entries.insert(i, new_entry)
|
|
113
137
|
end
|
|
114
138
|
|
|
139
|
+
# Inserts +newklass+ after +oldklass+ in the chain.
|
|
140
|
+
# Useful if one middleware must run after another middleware.
|
|
115
141
|
def insert_after(oldklass, newklass, *args)
|
|
116
142
|
i = entries.index { |entry| entry.klass == newklass }
|
|
117
143
|
new_entry = i.nil? ? Entry.new(@config, newklass, *args) : entries.delete_at(i)
|
|
@@ -119,10 +145,13 @@ module Sidekiq
|
|
|
119
145
|
entries.insert(i + 1, new_entry)
|
|
120
146
|
end
|
|
121
147
|
|
|
148
|
+
# @return [Boolean] if the given class is already in the chain
|
|
122
149
|
def exists?(klass)
|
|
123
150
|
any? { |entry| entry.klass == klass }
|
|
124
151
|
end
|
|
152
|
+
alias_method :include?, :exists?
|
|
125
153
|
|
|
154
|
+
# @return [Boolean] if the chain contains no middleware
|
|
126
155
|
def empty?
|
|
127
156
|
@entries.nil? || @entries.empty?
|
|
128
157
|
end
|
|
@@ -135,23 +164,30 @@ module Sidekiq
|
|
|
135
164
|
entries.clear
|
|
136
165
|
end
|
|
137
166
|
|
|
138
|
-
|
|
167
|
+
# Used by Sidekiq to execute the middleware at runtime
|
|
168
|
+
# @api private
|
|
169
|
+
def invoke(*args, &block)
|
|
139
170
|
return yield if empty?
|
|
140
171
|
|
|
141
172
|
chain = retrieve
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
173
|
+
traverse(chain, 0, args, &block)
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
private
|
|
177
|
+
|
|
178
|
+
def traverse(chain, index, args, &block)
|
|
179
|
+
if index >= chain.size
|
|
180
|
+
yield
|
|
181
|
+
else
|
|
182
|
+
chain[index].call(*args) do
|
|
183
|
+
traverse(chain, index + 1, args, &block)
|
|
147
184
|
end
|
|
148
185
|
end
|
|
149
|
-
traverse_chain.call
|
|
150
186
|
end
|
|
151
187
|
end
|
|
152
188
|
|
|
153
|
-
|
|
154
|
-
|
|
189
|
+
# Represents each link in the middleware chain
|
|
190
|
+
# @api private
|
|
155
191
|
class Entry
|
|
156
192
|
attr_reader :klass
|
|
157
193
|
|
|
@@ -11,22 +11,22 @@ module Sidekiq
|
|
|
11
11
|
#
|
|
12
12
|
# # in your initializer
|
|
13
13
|
# require "sidekiq/middleware/current_attributes"
|
|
14
|
-
# Sidekiq::CurrentAttributes.persist(Myapp::Current)
|
|
14
|
+
# Sidekiq::CurrentAttributes.persist("Myapp::Current")
|
|
15
15
|
#
|
|
16
16
|
module CurrentAttributes
|
|
17
17
|
class Save
|
|
18
18
|
include Sidekiq::ClientMiddleware
|
|
19
19
|
|
|
20
20
|
def initialize(cattr)
|
|
21
|
-
@
|
|
21
|
+
@strklass = cattr
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
def call(_, job, _, _)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
job
|
|
28
|
-
|
|
29
|
-
job["cattr"] = attrs
|
|
25
|
+
if !job.has_key?("cattr")
|
|
26
|
+
attrs = @strklass.constantize.attributes
|
|
27
|
+
# Retries can push the job N times, we don't
|
|
28
|
+
# want retries to reset cattr. #5692, #5090
|
|
29
|
+
job["cattr"] = attrs if attrs.any?
|
|
30
30
|
end
|
|
31
31
|
yield
|
|
32
32
|
end
|
|
@@ -36,26 +36,21 @@ module Sidekiq
|
|
|
36
36
|
include Sidekiq::ServerMiddleware
|
|
37
37
|
|
|
38
38
|
def initialize(cattr)
|
|
39
|
-
@
|
|
39
|
+
@strklass = cattr
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
def call(_, job, _, &block)
|
|
43
43
|
if job.has_key?("cattr")
|
|
44
|
-
@
|
|
44
|
+
@strklass.constantize.set(job["cattr"], &block)
|
|
45
45
|
else
|
|
46
46
|
yield
|
|
47
47
|
end
|
|
48
48
|
end
|
|
49
49
|
end
|
|
50
50
|
|
|
51
|
-
def self.persist(klass)
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
end
|
|
55
|
-
Sidekiq.configure_server do |config|
|
|
56
|
-
config.client_middleware.add Save, klass
|
|
57
|
-
config.server_middleware.add Load, klass
|
|
58
|
-
end
|
|
51
|
+
def self.persist(klass, config = Sidekiq.default_configuration)
|
|
52
|
+
config.client_middleware.add Save, klass.to_s
|
|
53
|
+
config.server_middleware.add Load, klass.to_s
|
|
59
54
|
end
|
|
60
55
|
end
|
|
61
56
|
end
|
data/lib/sidekiq/monitor.rb
CHANGED
|
@@ -16,8 +16,6 @@ class Sidekiq::Monitor
|
|
|
16
16
|
return
|
|
17
17
|
end
|
|
18
18
|
send(section)
|
|
19
|
-
rescue => e
|
|
20
|
-
abort "Couldn't get status: #{e}"
|
|
21
19
|
end
|
|
22
20
|
|
|
23
21
|
def all
|
|
@@ -49,10 +47,25 @@ class Sidekiq::Monitor
|
|
|
49
47
|
def processes
|
|
50
48
|
puts "---- Processes (#{process_set.size}) ----"
|
|
51
49
|
process_set.each_with_index do |process, index|
|
|
50
|
+
# Keep compatibility with legacy versions since we don't want to break sidekiqmon during rolling upgrades or downgrades.
|
|
51
|
+
#
|
|
52
|
+
# Before:
|
|
53
|
+
# ["default", "critical"]
|
|
54
|
+
#
|
|
55
|
+
# After:
|
|
56
|
+
# {"default" => 1, "critical" => 10}
|
|
57
|
+
queues =
|
|
58
|
+
if process["weights"]
|
|
59
|
+
process["weights"].sort_by { |queue| queue[0] }.map { |capsule| capsule.map { |name, weight| (weight > 0) ? "#{name}: #{weight}" : name }.join(", ") }
|
|
60
|
+
else
|
|
61
|
+
process["queues"].sort
|
|
62
|
+
end
|
|
63
|
+
|
|
52
64
|
puts "#{process["identity"]} #{tags_for(process)}"
|
|
53
65
|
puts " Started: #{Time.at(process["started_at"])} (#{time_ago(process["started_at"])})"
|
|
54
66
|
puts " Threads: #{process["concurrency"]} (#{process["busy"]} busy)"
|
|
55
|
-
puts " Queues: #{split_multiline(
|
|
67
|
+
puts " Queues: #{split_multiline(queues, pad: 11)}"
|
|
68
|
+
puts " Version: #{process["version"] || "Unknown"}" if process["version"] != Sidekiq::VERSION
|
|
56
69
|
puts "" unless (index + 1) == process_set.size
|
|
57
70
|
end
|
|
58
71
|
end
|
|
@@ -101,7 +114,7 @@ class Sidekiq::Monitor
|
|
|
101
114
|
tags = [
|
|
102
115
|
process["tag"],
|
|
103
116
|
process["labels"],
|
|
104
|
-
(process["quiet"] == "true" ? "quiet" : nil)
|
|
117
|
+
((process["quiet"] == "true") ? "quiet" : nil)
|
|
105
118
|
].flatten.compact
|
|
106
119
|
tags.any? ? "[#{tags.join("] [")}]" : nil
|
|
107
120
|
end
|
data/lib/sidekiq/paginator.rb
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module Sidekiq
|
|
4
4
|
module Paginator
|
|
5
5
|
def page(key, pageidx = 1, page_size = 25, opts = nil)
|
|
6
|
-
current_page = pageidx.to_i < 1 ? 1 : pageidx.to_i
|
|
6
|
+
current_page = (pageidx.to_i < 1) ? 1 : pageidx.to_i
|
|
7
7
|
pageidx = current_page - 1
|
|
8
8
|
total_size = 0
|
|
9
9
|
items = []
|
|
@@ -43,5 +43,13 @@ module Sidekiq
|
|
|
43
43
|
end
|
|
44
44
|
end
|
|
45
45
|
end
|
|
46
|
+
|
|
47
|
+
def page_items(items, pageidx = 1, page_size = 25)
|
|
48
|
+
current_page = (pageidx.to_i < 1) ? 1 : pageidx.to_i
|
|
49
|
+
pageidx = current_page - 1
|
|
50
|
+
starting = pageidx * page_size
|
|
51
|
+
items = items.to_a
|
|
52
|
+
[current_page, items.size, items[starting, page_size]]
|
|
53
|
+
end
|
|
46
54
|
end
|
|
47
55
|
end
|
data/lib/sidekiq/processor.rb
CHANGED
|
@@ -26,18 +26,18 @@ module Sidekiq
|
|
|
26
26
|
|
|
27
27
|
attr_reader :thread
|
|
28
28
|
attr_reader :job
|
|
29
|
+
attr_reader :capsule
|
|
29
30
|
|
|
30
|
-
def initialize(
|
|
31
|
+
def initialize(capsule, &block)
|
|
32
|
+
@config = @capsule = capsule
|
|
31
33
|
@callback = block
|
|
32
34
|
@down = false
|
|
33
35
|
@done = false
|
|
34
36
|
@job = nil
|
|
35
37
|
@thread = nil
|
|
36
|
-
@
|
|
37
|
-
@
|
|
38
|
-
@
|
|
39
|
-
@job_logger = (options[:job_logger] || Sidekiq::JobLogger).new
|
|
40
|
-
@retrier = Sidekiq::JobRetry.new(options)
|
|
38
|
+
@reloader = Sidekiq.default_configuration[:reloader]
|
|
39
|
+
@job_logger = (capsule.config[:job_logger] || Sidekiq::JobLogger).new(logger)
|
|
40
|
+
@retrier = Sidekiq::JobRetry.new(capsule)
|
|
41
41
|
end
|
|
42
42
|
|
|
43
43
|
def terminate(wait = false)
|
|
@@ -59,12 +59,16 @@ module Sidekiq
|
|
|
59
59
|
end
|
|
60
60
|
|
|
61
61
|
def start
|
|
62
|
-
@thread ||= safe_thread("processor", &method(:run))
|
|
62
|
+
@thread ||= safe_thread("#{config.name}/processor", &method(:run))
|
|
63
63
|
end
|
|
64
64
|
|
|
65
65
|
private unless $TESTING
|
|
66
66
|
|
|
67
67
|
def run
|
|
68
|
+
# By setting this thread-local, Sidekiq.redis will access +Sidekiq::Capsule#redis_pool+
|
|
69
|
+
# instead of the global pool in +Sidekiq::Config#redis_pool+.
|
|
70
|
+
Thread.current[:sidekiq_capsule] = @capsule
|
|
71
|
+
|
|
68
72
|
process_one until @done
|
|
69
73
|
@callback.call(self)
|
|
70
74
|
rescue Sidekiq::Shutdown
|
|
@@ -80,7 +84,7 @@ module Sidekiq
|
|
|
80
84
|
end
|
|
81
85
|
|
|
82
86
|
def get_one
|
|
83
|
-
uow =
|
|
87
|
+
uow = capsule.fetcher.retrieve_work
|
|
84
88
|
if @down
|
|
85
89
|
logger.info { "Redis is online, #{::Process.clock_gettime(::Process::CLOCK_MONOTONIC) - @down} sec downtime" }
|
|
86
90
|
@down = nil
|
|
@@ -129,7 +133,7 @@ module Sidekiq
|
|
|
129
133
|
# the Reloader. It handles code loading, db connection management, etc.
|
|
130
134
|
# Effectively this block denotes a "unit of work" to Rails.
|
|
131
135
|
@reloader.call do
|
|
132
|
-
klass =
|
|
136
|
+
klass = Object.const_get(job_hash["class"])
|
|
133
137
|
inst = klass.new
|
|
134
138
|
inst.jid = job_hash["jid"]
|
|
135
139
|
@retrier.local(inst, jobstr, queue) do
|
|
@@ -142,6 +146,9 @@ module Sidekiq
|
|
|
142
146
|
end
|
|
143
147
|
end
|
|
144
148
|
|
|
149
|
+
IGNORE_SHUTDOWN_INTERRUPTS = {Sidekiq::Shutdown => :never}
|
|
150
|
+
private_constant :IGNORE_SHUTDOWN_INTERRUPTS
|
|
151
|
+
|
|
145
152
|
def process(uow)
|
|
146
153
|
jobstr = uow.job
|
|
147
154
|
queue = uow.queue_name
|
|
@@ -152,15 +159,21 @@ module Sidekiq
|
|
|
152
159
|
job_hash = Sidekiq.load_json(jobstr)
|
|
153
160
|
rescue => ex
|
|
154
161
|
handle_exception(ex, {context: "Invalid JSON for job", jobstr: jobstr})
|
|
155
|
-
|
|
156
|
-
|
|
162
|
+
now = Time.now.to_f
|
|
163
|
+
redis do |conn|
|
|
164
|
+
conn.multi do |xa|
|
|
165
|
+
xa.zadd("dead", now.to_s, jobstr)
|
|
166
|
+
xa.zremrangebyscore("dead", "-inf", now - @capsule.config[:dead_timeout_in_seconds])
|
|
167
|
+
xa.zremrangebyrank("dead", 0, - @capsule.config[:dead_max_jobs])
|
|
168
|
+
end
|
|
169
|
+
end
|
|
157
170
|
return uow.acknowledge
|
|
158
171
|
end
|
|
159
172
|
|
|
160
173
|
ack = false
|
|
161
174
|
begin
|
|
162
175
|
dispatch(job_hash, queue, jobstr) do |inst|
|
|
163
|
-
|
|
176
|
+
config.server_middleware.invoke(inst, job_hash, queue) do
|
|
164
177
|
execute_job(inst, job_hash["args"])
|
|
165
178
|
end
|
|
166
179
|
end
|
|
@@ -174,7 +187,7 @@ module Sidekiq
|
|
|
174
187
|
# signals that we created a retry successfully. We can acknowlege the job.
|
|
175
188
|
ack = true
|
|
176
189
|
e = h.cause || h
|
|
177
|
-
handle_exception(e, {context: "Job raised exception", job: job_hash
|
|
190
|
+
handle_exception(e, {context: "Job raised exception", job: job_hash})
|
|
178
191
|
raise e
|
|
179
192
|
rescue Exception => ex
|
|
180
193
|
# Unexpected error! This is very bad and indicates an exception that got past
|
|
@@ -185,7 +198,7 @@ module Sidekiq
|
|
|
185
198
|
ensure
|
|
186
199
|
if ack
|
|
187
200
|
# We don't want a shutdown signal to interrupt job acknowledgment.
|
|
188
|
-
Thread.handle_interrupt(
|
|
201
|
+
Thread.handle_interrupt(IGNORE_SHUTDOWN_INTERRUPTS) do
|
|
189
202
|
uow.acknowledge
|
|
190
203
|
end
|
|
191
204
|
end
|
|
@@ -263,18 +276,5 @@ module Sidekiq
|
|
|
263
276
|
PROCESSED.incr
|
|
264
277
|
end
|
|
265
278
|
end
|
|
266
|
-
|
|
267
|
-
def constantize(str)
|
|
268
|
-
return Object.const_get(str) unless str.include?("::")
|
|
269
|
-
|
|
270
|
-
names = str.split("::")
|
|
271
|
-
names.shift if names.empty? || names.first.empty?
|
|
272
|
-
|
|
273
|
-
names.inject(Object) do |constant, name|
|
|
274
|
-
# the false flag limits search for name to under the constant namespace
|
|
275
|
-
# which mimics Rails' behaviour
|
|
276
|
-
constant.const_get(name, false)
|
|
277
|
-
end
|
|
278
|
-
end
|
|
279
279
|
end
|
|
280
280
|
end
|
data/lib/sidekiq/rails.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "sidekiq/job"
|
|
4
|
+
require "rails"
|
|
4
5
|
|
|
5
6
|
module Sidekiq
|
|
6
7
|
class Rails < ::Rails::Engine
|
|
@@ -10,7 +11,8 @@ module Sidekiq
|
|
|
10
11
|
end
|
|
11
12
|
|
|
12
13
|
def call
|
|
13
|
-
|
|
14
|
+
params = (::Rails::VERSION::STRING >= "7.1") ? {source: "job.sidekiq"} : {}
|
|
15
|
+
@app.reloader.wrap(**params) do
|
|
14
16
|
yield
|
|
15
17
|
end
|
|
16
18
|
end
|
|
@@ -22,7 +24,7 @@ module Sidekiq
|
|
|
22
24
|
|
|
23
25
|
# By including the Options module, we allow AJs to directly control sidekiq features
|
|
24
26
|
# via the *sidekiq_options* class method and, for instance, not use AJ's retry system.
|
|
25
|
-
# AJ retries don't show up in the Sidekiq UI Retries tab, save any error data, can't be
|
|
27
|
+
# AJ retries don't show up in the Sidekiq UI Retries tab, don't save any error data, can't be
|
|
26
28
|
# manually retried, don't automatically die, etc.
|
|
27
29
|
#
|
|
28
30
|
# class SomeJob < ActiveJob::Base
|
|
@@ -48,13 +50,6 @@ module Sidekiq
|
|
|
48
50
|
end
|
|
49
51
|
end
|
|
50
52
|
|
|
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
53
|
# This hook happens after all initializers are run, just before returning
|
|
59
54
|
# from config/environment.rb back to sidekiq/cli.rb.
|
|
60
55
|
#
|