sidekiq 6.3.1 → 6.4.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of sidekiq might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/Changes.md +34 -0
- data/LICENSE +3 -3
- data/README.md +6 -1
- data/bin/sidekiq +3 -3
- data/bin/sidekiqload +56 -58
- data/bin/sidekiqmon +1 -1
- 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 +49 -43
- data/lib/sidekiq/cli.rb +13 -3
- data/lib/sidekiq/client.rb +21 -40
- data/lib/sidekiq/delay.rb +2 -0
- data/lib/sidekiq/fetch.rb +2 -2
- data/lib/sidekiq/job_logger.rb +15 -27
- data/lib/sidekiq/job_retry.rb +6 -4
- data/lib/sidekiq/job_util.rb +65 -0
- data/lib/sidekiq/launcher.rb +27 -27
- data/lib/sidekiq/logger.rb +4 -0
- data/lib/sidekiq/manager.rb +7 -9
- data/lib/sidekiq/middleware/current_attributes.rb +6 -1
- data/lib/sidekiq/paginator.rb +8 -8
- data/lib/sidekiq/rails.rb +1 -1
- data/lib/sidekiq/scheduled.rb +13 -3
- data/lib/sidekiq/util.rb +13 -0
- data/lib/sidekiq/version.rb +1 -1
- data/lib/sidekiq/web/application.rb +5 -2
- data/lib/sidekiq/web/helpers.rb +1 -1
- data/lib/sidekiq/web.rb +3 -3
- data/lib/sidekiq/worker.rb +59 -8
- data/lib/sidekiq.rb +6 -0
- data/web/assets/stylesheets/application-dark.css +13 -17
- data/web/assets/stylesheets/application.css +5 -5
- metadata +8 -7
- data/lib/generators/sidekiq/worker_generator.rb +0 -57
data/lib/sidekiq/client.rb
CHANGED
@@ -2,9 +2,12 @@
|
|
2
2
|
|
3
3
|
require "securerandom"
|
4
4
|
require "sidekiq/middleware/chain"
|
5
|
+
require "sidekiq/job_util"
|
5
6
|
|
6
7
|
module Sidekiq
|
7
8
|
class Client
|
9
|
+
include Sidekiq::JobUtil
|
10
|
+
|
8
11
|
##
|
9
12
|
# Define client-side middleware:
|
10
13
|
#
|
@@ -100,7 +103,7 @@ module Sidekiq
|
|
100
103
|
|
101
104
|
normed = normalize_item(items)
|
102
105
|
payloads = args.map.with_index { |job_args, index|
|
103
|
-
copy = normed.merge("args" => job_args, "jid" => SecureRandom.hex(12)
|
106
|
+
copy = normed.merge("args" => job_args, "jid" => SecureRandom.hex(12))
|
104
107
|
copy["at"] = (at.is_a?(Array) ? at[index] : at) if at
|
105
108
|
|
106
109
|
result = process_single(items["class"], copy)
|
@@ -186,8 +189,23 @@ module Sidekiq
|
|
186
189
|
|
187
190
|
def raw_push(payloads)
|
188
191
|
@redis_pool.with do |conn|
|
189
|
-
|
190
|
-
|
192
|
+
retryable = true
|
193
|
+
begin
|
194
|
+
conn.pipelined do |pipeline|
|
195
|
+
atomic_push(pipeline, payloads)
|
196
|
+
end
|
197
|
+
rescue Redis::BaseError => ex
|
198
|
+
# 2550 Failover can cause the server to become a replica, need
|
199
|
+
# to disconnect and reopen the socket to get back to the primary.
|
200
|
+
# 4495 Use the same logic if we have a "Not enough replicas" error from the primary
|
201
|
+
# 4985 Use the same logic when a blocking command is force-unblocked
|
202
|
+
# The retry logic is copied from sidekiq.rb
|
203
|
+
if retryable && ex.message =~ /READONLY|NOREPLICAS|UNBLOCKED/
|
204
|
+
conn.disconnect!
|
205
|
+
retryable = false
|
206
|
+
retry
|
207
|
+
end
|
208
|
+
raise
|
191
209
|
end
|
192
210
|
end
|
193
211
|
true
|
@@ -218,42 +236,5 @@ module Sidekiq
|
|
218
236
|
item
|
219
237
|
end
|
220
238
|
end
|
221
|
-
|
222
|
-
def validate(item)
|
223
|
-
raise(ArgumentError, "Job must be a Hash with 'class' and 'args' keys: `#{item}`") unless item.is_a?(Hash) && item.key?("class") && item.key?("args")
|
224
|
-
raise(ArgumentError, "Job args must be an Array: `#{item}`") unless item["args"].is_a?(Array)
|
225
|
-
raise(ArgumentError, "Job class must be either a Class or String representation of the class name: `#{item}`") unless item["class"].is_a?(Class) || item["class"].is_a?(String)
|
226
|
-
raise(ArgumentError, "Job 'at' must be a Numeric timestamp: `#{item}`") if item.key?("at") && !item["at"].is_a?(Numeric)
|
227
|
-
raise(ArgumentError, "Job tags must be an Array: `#{item}`") if item["tags"] && !item["tags"].is_a?(Array)
|
228
|
-
end
|
229
|
-
|
230
|
-
def normalize_item(item)
|
231
|
-
validate(item)
|
232
|
-
# raise(ArgumentError, "Arguments must be native JSON types, see https://github.com/mperham/sidekiq/wiki/Best-Practices") unless JSON.load(JSON.dump(item['args'])) == item['args']
|
233
|
-
|
234
|
-
# merge in the default sidekiq_options for the item's class and/or wrapped element
|
235
|
-
# this allows ActiveJobs to control sidekiq_options too.
|
236
|
-
defaults = normalized_hash(item["class"])
|
237
|
-
defaults = defaults.merge(item["wrapped"].get_sidekiq_options) if item["wrapped"].respond_to?("get_sidekiq_options")
|
238
|
-
item = defaults.merge(item)
|
239
|
-
|
240
|
-
raise(ArgumentError, "Job must include a valid queue name") if item["queue"].nil? || item["queue"] == ""
|
241
|
-
|
242
|
-
item["class"] = item["class"].to_s
|
243
|
-
item["queue"] = item["queue"].to_s
|
244
|
-
item["jid"] ||= SecureRandom.hex(12)
|
245
|
-
item["created_at"] ||= Time.now.to_f
|
246
|
-
|
247
|
-
item
|
248
|
-
end
|
249
|
-
|
250
|
-
def normalized_hash(item_class)
|
251
|
-
if item_class.is_a?(Class)
|
252
|
-
raise(ArgumentError, "Message must include a Sidekiq::Worker class, not class name: #{item_class.ancestors.inspect}") unless item_class.respond_to?("get_sidekiq_options")
|
253
|
-
item_class.get_sidekiq_options
|
254
|
-
else
|
255
|
-
Sidekiq.default_worker_options
|
256
|
-
end
|
257
|
-
end
|
258
239
|
end
|
259
240
|
end
|
data/lib/sidekiq/delay.rb
CHANGED
data/lib/sidekiq/fetch.rb
CHANGED
data/lib/sidekiq/job_logger.rb
CHANGED
@@ -12,46 +12,34 @@ module Sidekiq
|
|
12
12
|
|
13
13
|
yield
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
end
|
15
|
+
Sidekiq::Context.add(:elapsed, elapsed(start))
|
16
|
+
@logger.info("done")
|
18
17
|
rescue Exception
|
19
|
-
|
20
|
-
|
21
|
-
end
|
18
|
+
Sidekiq::Context.add(:elapsed, elapsed(start))
|
19
|
+
@logger.info("fail")
|
22
20
|
|
23
21
|
raise
|
24
22
|
end
|
25
23
|
|
26
24
|
def prepare(job_hash, &block)
|
27
|
-
level = job_hash["log_level"]
|
28
|
-
if level
|
29
|
-
@logger.log_at(level) do
|
30
|
-
Sidekiq::Context.with(job_hash_context(job_hash), &block)
|
31
|
-
end
|
32
|
-
else
|
33
|
-
Sidekiq::Context.with(job_hash_context(job_hash), &block)
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
def job_hash_context(job_hash)
|
38
25
|
# If we're using a wrapper class, like ActiveJob, use the "wrapped"
|
39
26
|
# attribute to expose the underlying thing.
|
40
27
|
h = {
|
41
28
|
class: job_hash["display_class"] || job_hash["wrapped"] || job_hash["class"],
|
42
29
|
jid: job_hash["jid"]
|
43
30
|
}
|
44
|
-
h[:bid] = job_hash["bid"] if job_hash
|
45
|
-
h[:tags] = job_hash["tags"] if job_hash
|
46
|
-
h
|
47
|
-
end
|
48
|
-
|
49
|
-
def with_elapsed_time_context(start, &block)
|
50
|
-
Sidekiq::Context.with(elapsed_time_context(start), &block)
|
51
|
-
end
|
31
|
+
h[:bid] = job_hash["bid"] if job_hash.has_key?("bid")
|
32
|
+
h[:tags] = job_hash["tags"] if job_hash.has_key?("tags")
|
52
33
|
|
53
|
-
|
54
|
-
|
34
|
+
Thread.current[:sidekiq_context] = h
|
35
|
+
level = job_hash["log_level"]
|
36
|
+
if level
|
37
|
+
@logger.log_at(level, &block)
|
38
|
+
else
|
39
|
+
yield
|
40
|
+
end
|
41
|
+
ensure
|
42
|
+
Thread.current[:sidekiq_context] = nil
|
55
43
|
end
|
56
44
|
|
57
45
|
private
|
data/lib/sidekiq/job_retry.rb
CHANGED
@@ -34,9 +34,10 @@ module Sidekiq
|
|
34
34
|
# The job will be retried this number of times before giving up. (If simply
|
35
35
|
# 'true', Sidekiq retries 25 times)
|
36
36
|
#
|
37
|
-
#
|
37
|
+
# Relevant options for job retries:
|
38
38
|
#
|
39
|
-
# * 'queue' - the queue
|
39
|
+
# * 'queue' - the queue for the initial job
|
40
|
+
# * 'retry_queue' - if job retries should be pushed to a different (e.g. lower priority) queue
|
40
41
|
# * 'retry_count' - number of times we've retried so far.
|
41
42
|
# * 'error_message' - the message from the exception
|
42
43
|
# * 'error_class' - the exception class
|
@@ -52,11 +53,12 @@ module Sidekiq
|
|
52
53
|
#
|
53
54
|
# Sidekiq.options[:max_retries] = 7
|
54
55
|
#
|
55
|
-
# or limit the number of retries for a particular worker
|
56
|
+
# or limit the number of retries for a particular worker and send retries to
|
57
|
+
# a low priority queue with:
|
56
58
|
#
|
57
59
|
# class MyWorker
|
58
60
|
# include Sidekiq::Worker
|
59
|
-
# sidekiq_options :
|
61
|
+
# sidekiq_options retry: 10, retry_queue: 'low'
|
60
62
|
# end
|
61
63
|
#
|
62
64
|
class JobRetry
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require "securerandom"
|
2
|
+
require "time"
|
3
|
+
|
4
|
+
module Sidekiq
|
5
|
+
module JobUtil
|
6
|
+
# These functions encapsulate various job utilities.
|
7
|
+
# They must be simple and free from side effects.
|
8
|
+
|
9
|
+
def validate(item)
|
10
|
+
raise(ArgumentError, "Job must be a Hash with 'class' and 'args' keys: `#{item}`") unless item.is_a?(Hash) && item.key?("class") && item.key?("args")
|
11
|
+
raise(ArgumentError, "Job args must be an Array: `#{item}`") unless item["args"].is_a?(Array)
|
12
|
+
raise(ArgumentError, "Job class must be either a Class or String representation of the class name: `#{item}`") unless item["class"].is_a?(Class) || item["class"].is_a?(String)
|
13
|
+
raise(ArgumentError, "Job 'at' must be a Numeric timestamp: `#{item}`") if item.key?("at") && !item["at"].is_a?(Numeric)
|
14
|
+
raise(ArgumentError, "Job tags must be an Array: `#{item}`") if item["tags"] && !item["tags"].is_a?(Array)
|
15
|
+
|
16
|
+
if Sidekiq.options[:on_complex_arguments] == :raise
|
17
|
+
msg = <<~EOM
|
18
|
+
Job arguments to #{item["class"]} must be native JSON types, see https://github.com/mperham/sidekiq/wiki/Best-Practices.
|
19
|
+
To disable this error, remove `Sidekiq.strict_args!` from your initializer.
|
20
|
+
EOM
|
21
|
+
raise(ArgumentError, msg) unless json_safe?(item)
|
22
|
+
elsif Sidekiq.options[:on_complex_arguments] == :warn
|
23
|
+
Sidekiq.logger.warn <<~EOM unless json_safe?(item)
|
24
|
+
Job arguments to #{item["class"]} do not serialize to JSON safely. This will raise an error in
|
25
|
+
Sidekiq 7.0. See https://github.com/mperham/sidekiq/wiki/Best-Practices or raise an error today
|
26
|
+
by calling `Sidekiq.strict_args!` during Sidekiq initialization.
|
27
|
+
EOM
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def normalize_item(item)
|
32
|
+
validate(item)
|
33
|
+
|
34
|
+
# merge in the default sidekiq_options for the item's class and/or wrapped element
|
35
|
+
# this allows ActiveJobs to control sidekiq_options too.
|
36
|
+
defaults = normalized_hash(item["class"])
|
37
|
+
defaults = defaults.merge(item["wrapped"].get_sidekiq_options) if item["wrapped"].respond_to?(:get_sidekiq_options)
|
38
|
+
item = defaults.merge(item)
|
39
|
+
|
40
|
+
raise(ArgumentError, "Job must include a valid queue name") if item["queue"].nil? || item["queue"] == ""
|
41
|
+
|
42
|
+
item["class"] = item["class"].to_s
|
43
|
+
item["queue"] = item["queue"].to_s
|
44
|
+
item["jid"] ||= SecureRandom.hex(12)
|
45
|
+
item["created_at"] ||= Time.now.to_f
|
46
|
+
|
47
|
+
item
|
48
|
+
end
|
49
|
+
|
50
|
+
def normalized_hash(item_class)
|
51
|
+
if item_class.is_a?(Class)
|
52
|
+
raise(ArgumentError, "Message must include a Sidekiq::Worker class, not class name: #{item_class.ancestors.inspect}") unless item_class.respond_to?(:get_sidekiq_options)
|
53
|
+
item_class.get_sidekiq_options
|
54
|
+
else
|
55
|
+
Sidekiq.default_worker_options
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def json_safe?(item)
|
62
|
+
JSON.parse(JSON.dump(item["args"])) == item["args"]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
data/lib/sidekiq/launcher.rb
CHANGED
@@ -84,9 +84,9 @@ module Sidekiq
|
|
84
84
|
# Note we don't stop the heartbeat thread; if the process
|
85
85
|
# doesn't actually exit, it'll reappear in the Web UI.
|
86
86
|
Sidekiq.redis do |conn|
|
87
|
-
conn.pipelined do
|
88
|
-
|
89
|
-
|
87
|
+
conn.pipelined do |pipeline|
|
88
|
+
pipeline.srem("processes", identity)
|
89
|
+
pipeline.unlink("#{identity}:workers")
|
90
90
|
end
|
91
91
|
end
|
92
92
|
rescue
|
@@ -107,14 +107,14 @@ module Sidekiq
|
|
107
107
|
nowdate = Time.now.utc.strftime("%Y-%m-%d")
|
108
108
|
begin
|
109
109
|
Sidekiq.redis do |conn|
|
110
|
-
conn.pipelined do
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
110
|
+
conn.pipelined do |pipeline|
|
111
|
+
pipeline.incrby("stat:processed", procd)
|
112
|
+
pipeline.incrby("stat:processed:#{nowdate}", procd)
|
113
|
+
pipeline.expire("stat:processed:#{nowdate}", STATS_TTL)
|
114
|
+
|
115
|
+
pipeline.incrby("stat:failed", fails)
|
116
|
+
pipeline.incrby("stat:failed:#{nowdate}", fails)
|
117
|
+
pipeline.expire("stat:failed:#{nowdate}", STATS_TTL)
|
118
118
|
end
|
119
119
|
end
|
120
120
|
rescue => ex
|
@@ -138,20 +138,20 @@ module Sidekiq
|
|
138
138
|
nowdate = Time.now.utc.strftime("%Y-%m-%d")
|
139
139
|
|
140
140
|
Sidekiq.redis do |conn|
|
141
|
-
conn.multi do
|
142
|
-
|
143
|
-
|
144
|
-
|
141
|
+
conn.multi do |transaction|
|
142
|
+
transaction.incrby("stat:processed", procd)
|
143
|
+
transaction.incrby("stat:processed:#{nowdate}", procd)
|
144
|
+
transaction.expire("stat:processed:#{nowdate}", STATS_TTL)
|
145
145
|
|
146
|
-
|
147
|
-
|
148
|
-
|
146
|
+
transaction.incrby("stat:failed", fails)
|
147
|
+
transaction.incrby("stat:failed:#{nowdate}", fails)
|
148
|
+
transaction.expire("stat:failed:#{nowdate}", STATS_TTL)
|
149
149
|
|
150
|
-
|
150
|
+
transaction.unlink(workers_key)
|
151
151
|
curstate.each_pair do |tid, hash|
|
152
|
-
|
152
|
+
transaction.hset(workers_key, tid, Sidekiq.dump_json(hash))
|
153
153
|
end
|
154
|
-
|
154
|
+
transaction.expire(workers_key, 60)
|
155
155
|
end
|
156
156
|
end
|
157
157
|
|
@@ -161,17 +161,17 @@ module Sidekiq
|
|
161
161
|
kb = memory_usage(::Process.pid)
|
162
162
|
|
163
163
|
_, exists, _, _, msg = Sidekiq.redis { |conn|
|
164
|
-
conn.multi {
|
165
|
-
|
166
|
-
|
167
|
-
|
164
|
+
conn.multi { |transaction|
|
165
|
+
transaction.sadd("processes", key)
|
166
|
+
transaction.exists?(key)
|
167
|
+
transaction.hmset(key, "info", to_json,
|
168
168
|
"busy", curstate.size,
|
169
169
|
"beat", Time.now.to_f,
|
170
170
|
"rtt_us", rtt,
|
171
171
|
"quiet", @done,
|
172
172
|
"rss", kb)
|
173
|
-
|
174
|
-
|
173
|
+
transaction.expire(key, 60)
|
174
|
+
transaction.rpop("#{key}-signals")
|
175
175
|
}
|
176
176
|
}
|
177
177
|
|
data/lib/sidekiq/logger.rb
CHANGED
data/lib/sidekiq/manager.rb
CHANGED
@@ -55,9 +55,6 @@ module Sidekiq
|
|
55
55
|
fire_event(:quiet, reverse: true)
|
56
56
|
end
|
57
57
|
|
58
|
-
# hack for quicker development / testing environment #2774
|
59
|
-
PAUSE_TIME = $stdout.tty? ? 0.1 : 0.5
|
60
|
-
|
61
58
|
def stop(deadline)
|
62
59
|
quiet
|
63
60
|
fire_event(:shutdown, reverse: true)
|
@@ -69,12 +66,7 @@ module Sidekiq
|
|
69
66
|
return if @workers.empty?
|
70
67
|
|
71
68
|
logger.info { "Pausing to allow workers to finish..." }
|
72
|
-
|
73
|
-
while remaining > PAUSE_TIME
|
74
|
-
return if @workers.empty?
|
75
|
-
sleep PAUSE_TIME
|
76
|
-
remaining = deadline - ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
77
|
-
end
|
69
|
+
wait_for(deadline) { @workers.empty? }
|
78
70
|
return if @workers.empty?
|
79
71
|
|
80
72
|
hard_shutdown
|
@@ -130,6 +122,12 @@ module Sidekiq
|
|
130
122
|
cleanup.each do |processor|
|
131
123
|
processor.kill
|
132
124
|
end
|
125
|
+
|
126
|
+
# when this method returns, we immediately call `exit` which may not give
|
127
|
+
# the remaining threads time to run `ensure` blocks, etc. We pause here up
|
128
|
+
# to 3 seconds to give threads a minimal amount of time to run `ensure` blocks.
|
129
|
+
deadline = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC) + 3
|
130
|
+
wait_for(deadline) { @workers.empty? }
|
133
131
|
end
|
134
132
|
end
|
135
133
|
end
|
data/lib/sidekiq/paginator.rb
CHANGED
@@ -16,22 +16,22 @@ module Sidekiq
|
|
16
16
|
|
17
17
|
case type
|
18
18
|
when "zset"
|
19
|
-
total_size, items = conn.multi {
|
20
|
-
|
19
|
+
total_size, items = conn.multi { |transaction|
|
20
|
+
transaction.zcard(key)
|
21
21
|
if rev
|
22
|
-
|
22
|
+
transaction.zrevrange(key, starting, ending, with_scores: true)
|
23
23
|
else
|
24
|
-
|
24
|
+
transaction.zrange(key, starting, ending, with_scores: true)
|
25
25
|
end
|
26
26
|
}
|
27
27
|
[current_page, total_size, items]
|
28
28
|
when "list"
|
29
|
-
total_size, items = conn.multi {
|
30
|
-
|
29
|
+
total_size, items = conn.multi { |transaction|
|
30
|
+
transaction.llen(key)
|
31
31
|
if rev
|
32
|
-
|
32
|
+
transaction.lrange(key, -ending - 1, -starting - 1)
|
33
33
|
else
|
34
|
-
|
34
|
+
transaction.lrange(key, starting, ending)
|
35
35
|
end
|
36
36
|
}
|
37
37
|
items.reverse! if rev
|
data/lib/sidekiq/rails.rb
CHANGED
@@ -42,7 +42,7 @@ module Sidekiq
|
|
42
42
|
# This is the integration code necessary so that if code uses `Rails.logger.info "Hello"`,
|
43
43
|
# it will appear in the Sidekiq console with all of the job context. See #5021 and
|
44
44
|
# https://github.com/rails/rails/blob/b5f2b550f69a99336482739000c58e4e04e033aa/railties/lib/rails/commands/server/server_command.rb#L82-L84
|
45
|
-
unless ::ActiveSupport::Logger.logger_outputs_to?(::Rails.logger, $stdout)
|
45
|
+
unless ::Rails.logger == ::Sidekiq.logger || ::ActiveSupport::Logger.logger_outputs_to?(::Rails.logger, $stdout)
|
46
46
|
::Rails.logger.extend(::ActiveSupport::Logger.broadcast(::Sidekiq.logger))
|
47
47
|
end
|
48
48
|
end
|
data/lib/sidekiq/scheduled.rb
CHANGED
@@ -19,10 +19,11 @@ module Sidekiq
|
|
19
19
|
LUA
|
20
20
|
|
21
21
|
def initialize
|
22
|
+
@done = false
|
22
23
|
@lua_zpopbyscore_sha = nil
|
23
24
|
end
|
24
25
|
|
25
|
-
def enqueue_jobs(
|
26
|
+
def enqueue_jobs(sorted_sets = SETS)
|
26
27
|
# A job's "score" in Redis is the time at which it should be processed.
|
27
28
|
# Just check Redis for the set of jobs with a timestamp before now.
|
28
29
|
Sidekiq.redis do |conn|
|
@@ -31,7 +32,7 @@ module Sidekiq
|
|
31
32
|
# We need to go through the list one at a time to reduce the risk of something
|
32
33
|
# going wrong between the time jobs are popped from the scheduled queue and when
|
33
34
|
# they are pushed onto a work queue and losing the jobs.
|
34
|
-
while (job = zpopbyscore(conn, keys: [sorted_set], argv: [now]))
|
35
|
+
while !@done && (job = zpopbyscore(conn, keys: [sorted_set], argv: [Time.now.to_f.to_s]))
|
35
36
|
Sidekiq::Client.push(Sidekiq.load_json(job))
|
36
37
|
Sidekiq.logger.debug { "enqueued #{sorted_set}: #{job}" }
|
37
38
|
end
|
@@ -39,10 +40,17 @@ module Sidekiq
|
|
39
40
|
end
|
40
41
|
end
|
41
42
|
|
43
|
+
def terminate
|
44
|
+
@done = true
|
45
|
+
end
|
46
|
+
|
42
47
|
private
|
43
48
|
|
44
49
|
def zpopbyscore(conn, keys: nil, argv: nil)
|
45
|
-
|
50
|
+
if @lua_zpopbyscore_sha.nil?
|
51
|
+
raw_conn = conn.respond_to?(:redis) ? conn.redis : conn
|
52
|
+
@lua_zpopbyscore_sha = raw_conn.script(:load, LUA_ZPOPBYSCORE)
|
53
|
+
end
|
46
54
|
|
47
55
|
conn.evalsha(@lua_zpopbyscore_sha, keys: keys, argv: argv)
|
48
56
|
rescue Redis::CommandError => e
|
@@ -74,6 +82,8 @@ module Sidekiq
|
|
74
82
|
# Shut down this instance, will pause until the thread is dead.
|
75
83
|
def terminate
|
76
84
|
@done = true
|
85
|
+
@enq.terminate if @enq.respond_to?(:terminate)
|
86
|
+
|
77
87
|
if @thread
|
78
88
|
t = @thread
|
79
89
|
@thread = nil
|
data/lib/sidekiq/util.rb
CHANGED
@@ -39,6 +39,19 @@ module Sidekiq
|
|
39
39
|
module Util
|
40
40
|
include ExceptionHandler
|
41
41
|
|
42
|
+
# hack for quicker development / testing environment #2774
|
43
|
+
PAUSE_TIME = $stdout.tty? ? 0.1 : 0.5
|
44
|
+
|
45
|
+
# Wait for the orblock to be true or the deadline passed.
|
46
|
+
def wait_for(deadline, &condblock)
|
47
|
+
remaining = deadline - ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
48
|
+
while remaining > PAUSE_TIME
|
49
|
+
return if condblock.call
|
50
|
+
sleep PAUSE_TIME
|
51
|
+
remaining = deadline - ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
42
55
|
def watchdog(last_words)
|
43
56
|
yield
|
44
57
|
rescue Exception => ex
|
data/lib/sidekiq/version.rb
CHANGED
@@ -50,7 +50,10 @@ module Sidekiq
|
|
50
50
|
|
51
51
|
get "/" do
|
52
52
|
@redis_info = redis_info.select { |k, v| REDIS_KEYS.include? k }
|
53
|
-
|
53
|
+
days = (params["days"] || 30).to_i
|
54
|
+
return halt(401) if days < 1 || days > 180
|
55
|
+
|
56
|
+
stats_history = Sidekiq::Stats::History.new(days)
|
54
57
|
@processed_history = stats_history.processed
|
55
58
|
@failed_history = stats_history.failed
|
56
59
|
|
@@ -299,7 +302,7 @@ module Sidekiq
|
|
299
302
|
return [404, {"Content-Type" => "text/plain", "X-Cascade" => "pass"}, ["Not Found"]] unless action
|
300
303
|
|
301
304
|
app = @klass
|
302
|
-
resp = catch(:halt) do
|
305
|
+
resp = catch(:halt) do
|
303
306
|
self.class.run_befores(app, action)
|
304
307
|
action.instance_exec env, &action.block
|
305
308
|
ensure
|
data/lib/sidekiq/web/helpers.rb
CHANGED
@@ -242,7 +242,7 @@ module Sidekiq
|
|
242
242
|
queue class args retry_count retried_at failed_at
|
243
243
|
jid error_message error_class backtrace
|
244
244
|
error_backtrace enqueued_at retry wrapped
|
245
|
-
created_at tags
|
245
|
+
created_at tags display_class
|
246
246
|
])
|
247
247
|
|
248
248
|
def retry_extra_items(retry_job)
|
data/lib/sidekiq/web.rb
CHANGED
@@ -148,9 +148,9 @@ module Sidekiq
|
|
148
148
|
|
149
149
|
::Rack::Builder.new do
|
150
150
|
use Rack::Static, urls: ["/stylesheets", "/images", "/javascripts"],
|
151
|
-
|
152
|
-
|
153
|
-
|
151
|
+
root: ASSETS,
|
152
|
+
cascade: true,
|
153
|
+
header_rules: rules
|
154
154
|
m.each { |middleware, block| use(*middleware, &block) }
|
155
155
|
use Sidekiq::Web::CsrfProtection unless $TESTING
|
156
156
|
run WebApplication.new(klass)
|