sidekiq 7.0.9 → 7.1.6
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 +71 -0
- data/README.md +2 -2
- data/bin/sidekiqload +21 -3
- data/lib/sidekiq/api.rb +32 -9
- data/lib/sidekiq/cli.rb +2 -1
- data/lib/sidekiq/client.rb +37 -21
- data/lib/sidekiq/component.rb +1 -1
- data/lib/sidekiq/config.rb +25 -8
- data/lib/sidekiq/job.rb +1 -5
- data/lib/sidekiq/job_retry.rb +21 -4
- data/lib/sidekiq/job_util.rb +4 -2
- data/lib/sidekiq/launcher.rb +1 -1
- data/lib/sidekiq/metrics/shared.rb +1 -1
- data/lib/sidekiq/middleware/current_attributes.rb +55 -16
- data/lib/sidekiq/paginator.rb +1 -1
- data/lib/sidekiq/processor.rb +27 -26
- data/lib/sidekiq/rails.rb +12 -7
- data/lib/sidekiq/redis_client_adapter.rb +6 -26
- data/lib/sidekiq/redis_connection.rb +1 -0
- data/lib/sidekiq/scheduled.rb +1 -1
- data/lib/sidekiq/testing.rb +19 -6
- data/lib/sidekiq/version.rb +1 -1
- data/lib/sidekiq/web/action.rb +3 -3
- data/lib/sidekiq/web/application.rb +55 -5
- data/lib/sidekiq/web/csrf_protection.rb +1 -1
- data/lib/sidekiq/web/helpers.rb +23 -9
- data/lib/sidekiq/web.rb +13 -1
- data/sidekiq.gemspec +1 -10
- data/web/assets/javascripts/application.js +1 -0
- data/web/assets/javascripts/dashboard-charts.js +3 -1
- data/web/locales/en.yml +2 -0
- data/web/locales/fr.yml +14 -0
- data/web/locales/pt-br.yml +20 -0
- data/web/views/_job_info.erb +1 -1
- data/web/views/filtering.erb +7 -0
- data/web/views/metrics_for_job.erb +1 -1
- metadata +7 -14
|
@@ -7,26 +7,32 @@ module Sidekiq
|
|
|
7
7
|
# This can be useful for multi-tenancy, i18n locale, timezone, any implicit
|
|
8
8
|
# per-request attribute. See +ActiveSupport::CurrentAttributes+.
|
|
9
9
|
#
|
|
10
|
+
# For multiple current attributes, pass an array of current attributes.
|
|
11
|
+
#
|
|
10
12
|
# @example
|
|
11
13
|
#
|
|
12
14
|
# # in your initializer
|
|
13
15
|
# require "sidekiq/middleware/current_attributes"
|
|
14
16
|
# Sidekiq::CurrentAttributes.persist("Myapp::Current")
|
|
17
|
+
# # or multiple current attributes
|
|
18
|
+
# Sidekiq::CurrentAttributes.persist(["Myapp::Current", "Myapp::OtherCurrent"])
|
|
15
19
|
#
|
|
16
20
|
module CurrentAttributes
|
|
17
21
|
class Save
|
|
18
22
|
include Sidekiq::ClientMiddleware
|
|
19
23
|
|
|
20
|
-
def initialize(
|
|
21
|
-
@
|
|
24
|
+
def initialize(cattrs)
|
|
25
|
+
@cattrs = cattrs
|
|
22
26
|
end
|
|
23
27
|
|
|
24
28
|
def call(_, job, _, _)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
@cattrs.each do |(key, strklass)|
|
|
30
|
+
if !job.has_key?(key)
|
|
31
|
+
attrs = strklass.constantize.attributes
|
|
32
|
+
# Retries can push the job N times, we don't
|
|
33
|
+
# want retries to reset cattr. #5692, #5090
|
|
34
|
+
job[key] = attrs if attrs.any?
|
|
35
|
+
end
|
|
30
36
|
end
|
|
31
37
|
yield
|
|
32
38
|
end
|
|
@@ -35,22 +41,55 @@ module Sidekiq
|
|
|
35
41
|
class Load
|
|
36
42
|
include Sidekiq::ServerMiddleware
|
|
37
43
|
|
|
38
|
-
def initialize(
|
|
39
|
-
@
|
|
44
|
+
def initialize(cattrs)
|
|
45
|
+
@cattrs = cattrs
|
|
40
46
|
end
|
|
41
47
|
|
|
42
48
|
def call(_, job, _, &block)
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
49
|
+
cattrs_to_reset = []
|
|
50
|
+
|
|
51
|
+
@cattrs.each do |(key, strklass)|
|
|
52
|
+
if job.has_key?(key)
|
|
53
|
+
constklass = strklass.constantize
|
|
54
|
+
cattrs_to_reset << constklass
|
|
55
|
+
|
|
56
|
+
job[key].each do |(attribute, value)|
|
|
57
|
+
constklass.public_send("#{attribute}=", value)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
47
60
|
end
|
|
61
|
+
|
|
62
|
+
yield
|
|
63
|
+
ensure
|
|
64
|
+
cattrs_to_reset.each(&:reset)
|
|
48
65
|
end
|
|
49
66
|
end
|
|
50
67
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
68
|
+
class << self
|
|
69
|
+
def persist(klass_or_array, config = Sidekiq.default_configuration)
|
|
70
|
+
cattrs = build_cattrs_hash(klass_or_array)
|
|
71
|
+
|
|
72
|
+
config.client_middleware.add Save, cattrs
|
|
73
|
+
config.server_middleware.add Load, cattrs
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
private
|
|
77
|
+
|
|
78
|
+
def build_cattrs_hash(klass_or_array)
|
|
79
|
+
if klass_or_array.is_a?(Array)
|
|
80
|
+
{}.tap do |hash|
|
|
81
|
+
klass_or_array.each_with_index do |klass, index|
|
|
82
|
+
hash[key_at(index)] = klass.to_s
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
else
|
|
86
|
+
{key_at(0) => klass_or_array.to_s}
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def key_at(index)
|
|
91
|
+
(index == 0) ? "cattr" : "cattr_#{index}"
|
|
92
|
+
end
|
|
54
93
|
end
|
|
55
94
|
end
|
|
56
95
|
end
|
data/lib/sidekiq/paginator.rb
CHANGED
|
@@ -19,7 +19,7 @@ module Sidekiq
|
|
|
19
19
|
total_size, items = conn.multi { |transaction|
|
|
20
20
|
transaction.zcard(key)
|
|
21
21
|
if rev
|
|
22
|
-
transaction.
|
|
22
|
+
transaction.zrange(key, starting, ending, "REV", withscores: true)
|
|
23
23
|
else
|
|
24
24
|
transaction.zrange(key, starting, ending, withscores: true)
|
|
25
25
|
end
|
data/lib/sidekiq/processor.rb
CHANGED
|
@@ -148,6 +148,8 @@ module Sidekiq
|
|
|
148
148
|
|
|
149
149
|
IGNORE_SHUTDOWN_INTERRUPTS = {Sidekiq::Shutdown => :never}
|
|
150
150
|
private_constant :IGNORE_SHUTDOWN_INTERRUPTS
|
|
151
|
+
ALLOW_SHUTDOWN_INTERRUPTS = {Sidekiq::Shutdown => :immediate}
|
|
152
|
+
private_constant :ALLOW_SHUTDOWN_INTERRUPTS
|
|
151
153
|
|
|
152
154
|
def process(uow)
|
|
153
155
|
jobstr = uow.job
|
|
@@ -171,36 +173,35 @@ module Sidekiq
|
|
|
171
173
|
end
|
|
172
174
|
|
|
173
175
|
ack = false
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
176
|
+
Thread.handle_interrupt(IGNORE_SHUTDOWN_INTERRUPTS) do
|
|
177
|
+
Thread.handle_interrupt(ALLOW_SHUTDOWN_INTERRUPTS) do
|
|
178
|
+
dispatch(job_hash, queue, jobstr) do |inst|
|
|
179
|
+
config.server_middleware.invoke(inst, job_hash, queue) do
|
|
180
|
+
execute_job(inst, job_hash["args"])
|
|
181
|
+
end
|
|
178
182
|
end
|
|
183
|
+
ack = true
|
|
184
|
+
rescue Sidekiq::Shutdown
|
|
185
|
+
# Had to force kill this job because it didn't finish
|
|
186
|
+
# within the timeout. Don't acknowledge the work since
|
|
187
|
+
# we didn't properly finish it.
|
|
188
|
+
rescue Sidekiq::JobRetry::Handled => h
|
|
189
|
+
# this is the common case: job raised error and Sidekiq::JobRetry::Handled
|
|
190
|
+
# signals that we created a retry successfully. We can acknowlege the job.
|
|
191
|
+
ack = true
|
|
192
|
+
e = h.cause || h
|
|
193
|
+
handle_exception(e, {context: "Job raised exception", job: job_hash})
|
|
194
|
+
raise e
|
|
195
|
+
rescue Exception => ex
|
|
196
|
+
# Unexpected error! This is very bad and indicates an exception that got past
|
|
197
|
+
# the retry subsystem (e.g. network partition). We won't acknowledge the job
|
|
198
|
+
# so it can be rescued when using Sidekiq Pro.
|
|
199
|
+
handle_exception(ex, {context: "Internal exception!", job: job_hash, jobstr: jobstr})
|
|
200
|
+
raise ex
|
|
179
201
|
end
|
|
180
|
-
ack = true
|
|
181
|
-
rescue Sidekiq::Shutdown
|
|
182
|
-
# Had to force kill this job because it didn't finish
|
|
183
|
-
# within the timeout. Don't acknowledge the work since
|
|
184
|
-
# we didn't properly finish it.
|
|
185
|
-
rescue Sidekiq::JobRetry::Handled => h
|
|
186
|
-
# this is the common case: job raised error and Sidekiq::JobRetry::Handled
|
|
187
|
-
# signals that we created a retry successfully. We can acknowlege the job.
|
|
188
|
-
ack = true
|
|
189
|
-
e = h.cause || h
|
|
190
|
-
handle_exception(e, {context: "Job raised exception", job: job_hash})
|
|
191
|
-
raise e
|
|
192
|
-
rescue Exception => ex
|
|
193
|
-
# Unexpected error! This is very bad and indicates an exception that got past
|
|
194
|
-
# the retry subsystem (e.g. network partition). We won't acknowledge the job
|
|
195
|
-
# so it can be rescued when using Sidekiq Pro.
|
|
196
|
-
handle_exception(ex, {context: "Internal exception!", job: job_hash, jobstr: jobstr})
|
|
197
|
-
raise ex
|
|
198
202
|
ensure
|
|
199
203
|
if ack
|
|
200
|
-
|
|
201
|
-
Thread.handle_interrupt(IGNORE_SHUTDOWN_INTERRUPTS) do
|
|
202
|
-
uow.acknowledge
|
|
203
|
-
end
|
|
204
|
+
uow.acknowledge
|
|
204
205
|
end
|
|
205
206
|
end
|
|
206
207
|
end
|
data/lib/sidekiq/rails.rb
CHANGED
|
@@ -39,14 +39,9 @@ module Sidekiq
|
|
|
39
39
|
end
|
|
40
40
|
end
|
|
41
41
|
|
|
42
|
-
initializer "sidekiq.
|
|
42
|
+
initializer "sidekiq.backtrace_cleaner" do
|
|
43
43
|
Sidekiq.configure_server do |config|
|
|
44
|
-
|
|
45
|
-
# it will appear in the Sidekiq console with all of the job context. See #5021 and
|
|
46
|
-
# https://github.com/rails/rails/blob/b5f2b550f69a99336482739000c58e4e04e033aa/railties/lib/rails/commands/server/server_command.rb#L82-L84
|
|
47
|
-
unless ::Rails.logger == config.logger || ::ActiveSupport::Logger.logger_outputs_to?(::Rails.logger, $stdout)
|
|
48
|
-
::Rails.logger.extend(::ActiveSupport::Logger.broadcast(config.logger))
|
|
49
|
-
end
|
|
44
|
+
config[:backtrace_cleaner] = ->(backtrace) { ::Rails.backtrace_cleaner.clean(backtrace) }
|
|
50
45
|
end
|
|
51
46
|
end
|
|
52
47
|
|
|
@@ -57,6 +52,16 @@ module Sidekiq
|
|
|
57
52
|
config.after_initialize do
|
|
58
53
|
Sidekiq.configure_server do |config|
|
|
59
54
|
config[:reloader] = Sidekiq::Rails::Reloader.new
|
|
55
|
+
|
|
56
|
+
# This is the integration code necessary so that if a job uses `Rails.logger.info "Hello"`,
|
|
57
|
+
# it will appear in the Sidekiq console with all of the job context.
|
|
58
|
+
unless ::Rails.logger == config.logger || ::ActiveSupport::Logger.logger_outputs_to?(::Rails.logger, $stdout)
|
|
59
|
+
if ::Rails::VERSION::STRING < "7.1"
|
|
60
|
+
::Rails.logger.extend(::ActiveSupport::Logger.broadcast(config.logger))
|
|
61
|
+
else
|
|
62
|
+
::Rails.logger.broadcast_to(config.logger)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
60
65
|
end
|
|
61
66
|
end
|
|
62
67
|
end
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "set"
|
|
3
4
|
require "redis_client"
|
|
4
5
|
require "redis_client/decorator"
|
|
5
6
|
|
|
@@ -8,13 +9,14 @@ module Sidekiq
|
|
|
8
9
|
BaseError = RedisClient::Error
|
|
9
10
|
CommandError = RedisClient::CommandError
|
|
10
11
|
|
|
12
|
+
# You can add/remove items or clear the whole thing if you don't want deprecation warnings.
|
|
13
|
+
DEPRECATED_COMMANDS = %i[rpoplpush zrangebyscore zrevrange zrevrangebyscore getset hmset setex setnx].to_set
|
|
14
|
+
|
|
11
15
|
module CompatMethods
|
|
12
|
-
# TODO Deprecate and remove this
|
|
13
16
|
def info
|
|
14
17
|
@client.call("INFO") { |i| i.lines(chomp: true).map { |l| l.split(":", 2) }.select { |l| l.size == 2 }.to_h }
|
|
15
18
|
end
|
|
16
19
|
|
|
17
|
-
# TODO Deprecate and remove this
|
|
18
20
|
def evalsha(sha, keys, argv)
|
|
19
21
|
@client.call("EVALSHA", sha, keys.size, *keys, *argv)
|
|
20
22
|
end
|
|
@@ -24,6 +26,7 @@ module Sidekiq
|
|
|
24
26
|
# this allows us to use methods like `conn.hmset(...)` instead of having to use
|
|
25
27
|
# redis-client's native `conn.call("hmset", ...)`
|
|
26
28
|
def method_missing(*args, &block)
|
|
29
|
+
warn("[sidekiq#5788] Redis has deprecated the `#{args.first}`command, called at #{caller(1..1)}") if DEPRECATED_COMMANDS.include?(args.first)
|
|
27
30
|
@client.call(*args, *block)
|
|
28
31
|
end
|
|
29
32
|
ruby2_keywords :method_missing if respond_to?(:ruby2_keywords, true)
|
|
@@ -39,28 +42,6 @@ module Sidekiq
|
|
|
39
42
|
def config
|
|
40
43
|
@client.config
|
|
41
44
|
end
|
|
42
|
-
|
|
43
|
-
def message
|
|
44
|
-
yield nil, @queue.pop
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
# NB: this method does not return
|
|
48
|
-
def subscribe(chan)
|
|
49
|
-
@queue = ::Queue.new
|
|
50
|
-
|
|
51
|
-
pubsub = @client.pubsub
|
|
52
|
-
pubsub.call("subscribe", chan)
|
|
53
|
-
|
|
54
|
-
loop do
|
|
55
|
-
evt = pubsub.next_event
|
|
56
|
-
next if evt.nil?
|
|
57
|
-
next unless evt[0] == "message" && evt[1] == chan
|
|
58
|
-
|
|
59
|
-
(_, _, msg) = evt
|
|
60
|
-
@queue << msg
|
|
61
|
-
yield self
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
45
|
end
|
|
65
46
|
|
|
66
47
|
def initialize(options)
|
|
@@ -82,8 +63,7 @@ module Sidekiq
|
|
|
82
63
|
opts = options.dup
|
|
83
64
|
|
|
84
65
|
if opts[:namespace]
|
|
85
|
-
raise ArgumentError, "Your Redis configuration uses the namespace '#{opts[:namespace]}' but this feature
|
|
86
|
-
"Either use the redis adapter or remove the namespace."
|
|
66
|
+
raise ArgumentError, "Your Redis configuration uses the namespace '#{opts[:namespace]}' but this feature is no longer supported in Sidekiq 7+. See https://github.com/sidekiq/sidekiq/blob/main/docs/7.0-Upgrade.md#redis-namespace."
|
|
87
67
|
end
|
|
88
68
|
|
|
89
69
|
opts.delete(:size)
|
|
@@ -14,6 +14,7 @@ module Sidekiq
|
|
|
14
14
|
logger = symbolized_options.delete(:logger)
|
|
15
15
|
logger&.info { "Sidekiq #{Sidekiq::VERSION} connecting to Redis with options #{scrub(symbolized_options)}" }
|
|
16
16
|
|
|
17
|
+
raise "Sidekiq 7+ does not support Redis protocol 2" if symbolized_options[:protocol] == 2
|
|
17
18
|
size = symbolized_options.delete(:size) || 5
|
|
18
19
|
pool_timeout = symbolized_options.delete(:pool_timeout) || 1
|
|
19
20
|
pool_name = symbolized_options.delete(:pool_name)
|
data/lib/sidekiq/scheduled.rb
CHANGED
|
@@ -12,7 +12,7 @@ module Sidekiq
|
|
|
12
12
|
|
|
13
13
|
LUA_ZPOPBYSCORE = <<~LUA
|
|
14
14
|
local key, now = KEYS[1], ARGV[1]
|
|
15
|
-
local jobs = redis.call("
|
|
15
|
+
local jobs = redis.call("zrange", key, "-inf", now, "byscore", "limit", 0, 1)
|
|
16
16
|
if jobs[1] then
|
|
17
17
|
redis.call("zrem", key, jobs[1])
|
|
18
18
|
return jobs[1]
|
data/lib/sidekiq/testing.rb
CHANGED
|
@@ -6,22 +6,35 @@ require "sidekiq"
|
|
|
6
6
|
module Sidekiq
|
|
7
7
|
class Testing
|
|
8
8
|
class << self
|
|
9
|
-
attr_accessor :
|
|
9
|
+
attr_accessor :__global_test_mode
|
|
10
10
|
|
|
11
|
+
# Calling without a block sets the global test mode, affecting
|
|
12
|
+
# all threads. Calling with a block only affects the current Thread.
|
|
11
13
|
def __set_test_mode(mode)
|
|
12
14
|
if block_given?
|
|
13
|
-
current_mode = __test_mode
|
|
14
15
|
begin
|
|
15
|
-
self.
|
|
16
|
+
self.__local_test_mode = mode
|
|
16
17
|
yield
|
|
17
18
|
ensure
|
|
18
|
-
self.
|
|
19
|
+
self.__local_test_mode = nil
|
|
19
20
|
end
|
|
20
21
|
else
|
|
21
|
-
self.
|
|
22
|
+
self.__global_test_mode = mode
|
|
22
23
|
end
|
|
23
24
|
end
|
|
24
25
|
|
|
26
|
+
def __test_mode
|
|
27
|
+
__local_test_mode || __global_test_mode
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def __local_test_mode
|
|
31
|
+
Thread.current[:__sidekiq_test_mode]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def __local_test_mode=(value)
|
|
35
|
+
Thread.current[:__sidekiq_test_mode] = value
|
|
36
|
+
end
|
|
37
|
+
|
|
25
38
|
def disable!(&block)
|
|
26
39
|
__set_test_mode(:disable, &block)
|
|
27
40
|
end
|
|
@@ -64,7 +77,7 @@ module Sidekiq
|
|
|
64
77
|
class EmptyQueueError < RuntimeError; end
|
|
65
78
|
|
|
66
79
|
module TestingClient
|
|
67
|
-
def
|
|
80
|
+
def atomic_push(conn, payloads)
|
|
68
81
|
if Sidekiq::Testing.fake?
|
|
69
82
|
payloads.each do |job|
|
|
70
83
|
job = Sidekiq.load_json(Sidekiq.dump_json(job))
|
data/lib/sidekiq/version.rb
CHANGED
data/lib/sidekiq/web/action.rb
CHANGED
|
@@ -15,11 +15,11 @@ module Sidekiq
|
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
def halt(res)
|
|
18
|
-
throw :halt, [res, {
|
|
18
|
+
throw :halt, [res, {Rack::CONTENT_TYPE => "text/plain"}, [res.to_s]]
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def redirect(location)
|
|
22
|
-
throw :halt, [302, {
|
|
22
|
+
throw :halt, [302, {Web::LOCATION => "#{request.base_url}#{location}"}, []]
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
def params
|
|
@@ -68,7 +68,7 @@ module Sidekiq
|
|
|
68
68
|
end
|
|
69
69
|
|
|
70
70
|
def json(payload)
|
|
71
|
-
[200, {
|
|
71
|
+
[200, {Rack::CONTENT_TYPE => "application/json", Rack::CACHE_CONTROL => "private, no-store"}, [Sidekiq.dump_json(payload)]]
|
|
72
72
|
end
|
|
73
73
|
|
|
74
74
|
def initialize(env, block)
|
|
@@ -328,9 +328,59 @@ module Sidekiq
|
|
|
328
328
|
json Sidekiq::Stats.new.queues
|
|
329
329
|
end
|
|
330
330
|
|
|
331
|
+
########
|
|
332
|
+
# Filtering
|
|
333
|
+
get "/filter/retries" do
|
|
334
|
+
x = params[:substr]
|
|
335
|
+
return redirect "#{root_path}retries" unless x && x != ""
|
|
336
|
+
|
|
337
|
+
@retries = search(Sidekiq::RetrySet.new, params[:substr])
|
|
338
|
+
erb :retries
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
post "/filter/retries" do
|
|
342
|
+
x = params[:substr]
|
|
343
|
+
return redirect "#{root_path}retries" unless x && x != ""
|
|
344
|
+
|
|
345
|
+
@retries = search(Sidekiq::RetrySet.new, params[:substr])
|
|
346
|
+
erb :retries
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
get "/filter/scheduled" do
|
|
350
|
+
x = params[:substr]
|
|
351
|
+
return redirect "#{root_path}scheduled" unless x && x != ""
|
|
352
|
+
|
|
353
|
+
@scheduled = search(Sidekiq::ScheduledSet.new, params[:substr])
|
|
354
|
+
erb :scheduled
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
post "/filter/scheduled" do
|
|
358
|
+
x = params[:substr]
|
|
359
|
+
return redirect "#{root_path}scheduled" unless x && x != ""
|
|
360
|
+
|
|
361
|
+
@scheduled = search(Sidekiq::ScheduledSet.new, params[:substr])
|
|
362
|
+
erb :scheduled
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
get "/filter/dead" do
|
|
366
|
+
x = params[:substr]
|
|
367
|
+
return redirect "#{root_path}morgue" unless x && x != ""
|
|
368
|
+
|
|
369
|
+
@dead = search(Sidekiq::DeadSet.new, params[:substr])
|
|
370
|
+
erb :morgue
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
post "/filter/dead" do
|
|
374
|
+
x = params[:substr]
|
|
375
|
+
return redirect "#{root_path}morgue" unless x && x != ""
|
|
376
|
+
|
|
377
|
+
@dead = search(Sidekiq::DeadSet.new, params[:substr])
|
|
378
|
+
erb :morgue
|
|
379
|
+
end
|
|
380
|
+
|
|
331
381
|
def call(env)
|
|
332
382
|
action = self.class.match(env)
|
|
333
|
-
return [404, {
|
|
383
|
+
return [404, {Rack::CONTENT_TYPE => "text/plain", Web::X_CASCADE => "pass"}, ["Not Found"]] unless action
|
|
334
384
|
|
|
335
385
|
app = @klass
|
|
336
386
|
resp = catch(:halt) do
|
|
@@ -347,10 +397,10 @@ module Sidekiq
|
|
|
347
397
|
else
|
|
348
398
|
# rendered content goes here
|
|
349
399
|
headers = {
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
400
|
+
Rack::CONTENT_TYPE => "text/html",
|
|
401
|
+
Rack::CACHE_CONTROL => "private, no-store",
|
|
402
|
+
Web::CONTENT_LANGUAGE => action.locale,
|
|
403
|
+
Web::CONTENT_SECURITY_POLICY => CSP_HEADER
|
|
354
404
|
}
|
|
355
405
|
# we'll let Rack calculate Content-Length for us.
|
|
356
406
|
[200, headers, [resp]]
|
data/lib/sidekiq/web/helpers.rb
CHANGED
|
@@ -49,8 +49,29 @@ module Sidekiq
|
|
|
49
49
|
locale_files.select { |file| file =~ /\/#{lang}\.yml$/ }
|
|
50
50
|
end
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
def search(jobset, substr)
|
|
53
|
+
resultset = jobset.scan(substr).to_a
|
|
54
|
+
@current_page = 1
|
|
55
|
+
@count = @total_size = resultset.size
|
|
56
|
+
resultset
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def filtering(which)
|
|
60
|
+
erb(:filtering, locals: {which: which})
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def filter_link(jid, within = "retries")
|
|
64
|
+
if within.nil?
|
|
65
|
+
::Rack::Utils.escape_html(jid)
|
|
66
|
+
else
|
|
67
|
+
"<a href='#{root_path}filter/#{within}?substr=#{jid}'>#{::Rack::Utils.escape_html(jid)}</a>"
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def display_tags(job, within = "retries")
|
|
72
|
+
job.tags.map { |tag|
|
|
73
|
+
"<span class='label label-info jobtag'>#{filter_link(tag, within)}</span>"
|
|
74
|
+
}.join(" ")
|
|
54
75
|
end
|
|
55
76
|
|
|
56
77
|
# This view helper provide ability display you html code in
|
|
@@ -111,13 +132,6 @@ module Sidekiq
|
|
|
111
132
|
end
|
|
112
133
|
end
|
|
113
134
|
|
|
114
|
-
# within is used by Sidekiq Pro
|
|
115
|
-
def display_tags(job, within = nil)
|
|
116
|
-
job.tags.map { |tag|
|
|
117
|
-
"<span class='label label-info jobtag'>#{::Rack::Utils.escape_html(tag)}</span>"
|
|
118
|
-
}.join(" ")
|
|
119
|
-
end
|
|
120
|
-
|
|
121
135
|
# sidekiq/sidekiq#3243
|
|
122
136
|
def unfiltered?
|
|
123
137
|
yield unless env["PATH_INFO"].start_with?("/filter/")
|
data/lib/sidekiq/web.rb
CHANGED
|
@@ -34,6 +34,18 @@ module Sidekiq
|
|
|
34
34
|
"Metrics" => "metrics"
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
if Gem::Version.new(Rack::RELEASE) < Gem::Version.new("3")
|
|
38
|
+
CONTENT_LANGUAGE = "Content-Language"
|
|
39
|
+
CONTENT_SECURITY_POLICY = "Content-Security-Policy"
|
|
40
|
+
LOCATION = "Location"
|
|
41
|
+
X_CASCADE = "X-Cascade"
|
|
42
|
+
else
|
|
43
|
+
CONTENT_LANGUAGE = "content-language"
|
|
44
|
+
CONTENT_SECURITY_POLICY = "content-security-policy"
|
|
45
|
+
LOCATION = "location"
|
|
46
|
+
X_CASCADE = "x-cascade"
|
|
47
|
+
end
|
|
48
|
+
|
|
37
49
|
class << self
|
|
38
50
|
def settings
|
|
39
51
|
self
|
|
@@ -137,7 +149,7 @@ module Sidekiq
|
|
|
137
149
|
m = middlewares
|
|
138
150
|
|
|
139
151
|
rules = []
|
|
140
|
-
rules = [[:all, {
|
|
152
|
+
rules = [[:all, {Rack::CACHE_CONTROL => "private, max-age=86400"}]] unless ENV["SIDEKIQ_WEB_TESTING"]
|
|
141
153
|
|
|
142
154
|
::Rack::Builder.new do
|
|
143
155
|
use Rack::Static, urls: ["/stylesheets", "/images", "/javascripts"],
|
data/sidekiq.gemspec
CHANGED
|
@@ -23,17 +23,8 @@ Gem::Specification.new do |gem|
|
|
|
23
23
|
"rubygems_mfa_required" => "true"
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
gem.add_dependency "redis-client", ">= 0.
|
|
26
|
+
gem.add_dependency "redis-client", ">= 0.14.0"
|
|
27
27
|
gem.add_dependency "connection_pool", ">= 2.3.0"
|
|
28
28
|
gem.add_dependency "rack", ">= 2.2.4"
|
|
29
29
|
gem.add_dependency "concurrent-ruby", "< 2"
|
|
30
|
-
gem.post_install_message = <<~EOM
|
|
31
|
-
|
|
32
|
-
Welcome to Sidekiq 7.0!
|
|
33
|
-
|
|
34
|
-
1. Use `gem 'sidekiq', '<7'` in your Gemfile if you don't want this new version.
|
|
35
|
-
2. Read the release notes at https://github.com/sidekiq/sidekiq/blob/main/docs/7.0-Upgrade.md
|
|
36
|
-
3. If you have problems, search for open/closed issues at https://github.com/sidekiq/sidekiq/issues/
|
|
37
|
-
|
|
38
|
-
EOM
|
|
39
30
|
end
|
|
@@ -57,7 +57,9 @@ class DashboardChart extends BaseChart {
|
|
|
57
57
|
class RealtimeChart extends DashboardChart {
|
|
58
58
|
constructor(el, options) {
|
|
59
59
|
super(el, options);
|
|
60
|
-
|
|
60
|
+
let d = parseInt(localStorage.sidekiqTimeInterval) || 5000;
|
|
61
|
+
if (d < 2000) { d = 2000; }
|
|
62
|
+
this.delay = d
|
|
61
63
|
this.startPolling();
|
|
62
64
|
document.addEventListener("interval:update", this.handleUpdate.bind(this));
|
|
63
65
|
}
|
data/web/locales/en.yml
CHANGED
data/web/locales/fr.yml
CHANGED
|
@@ -17,14 +17,17 @@ fr:
|
|
|
17
17
|
DeadJobs: Tâches mortes
|
|
18
18
|
Delete: Supprimer
|
|
19
19
|
DeleteAll: Tout supprimer
|
|
20
|
+
Deploy: Déploiement
|
|
20
21
|
Enqueued: En attente
|
|
21
22
|
Error: Erreur
|
|
22
23
|
ErrorBacktrace: Backtrace d’erreur
|
|
23
24
|
ErrorClass: Classe d’erreur
|
|
24
25
|
ErrorMessage: Message d’erreur
|
|
26
|
+
ExecutionTime: Temps d'exécution
|
|
25
27
|
Extras: Extras
|
|
26
28
|
Failed: Échouées
|
|
27
29
|
Failures: Echecs
|
|
30
|
+
Failure: Echec
|
|
28
31
|
GoBack: ← Retour
|
|
29
32
|
History: Historique
|
|
30
33
|
Job: Tâche
|
|
@@ -35,6 +38,7 @@ fr:
|
|
|
35
38
|
Latency: Latence
|
|
36
39
|
LivePoll: Temps réel
|
|
37
40
|
MemoryUsage: Mémoire utilisée
|
|
41
|
+
Name: Nom
|
|
38
42
|
Namespace: Namespace
|
|
39
43
|
NextRetry: Prochain essai
|
|
40
44
|
NoDeadJobsFound: Aucune tâche morte n'a été trouvée
|
|
@@ -63,6 +67,7 @@ fr:
|
|
|
63
67
|
RetryNow: Réessayer maintenant
|
|
64
68
|
Scheduled: Planifiées
|
|
65
69
|
ScheduledJobs: Tâches planifiées
|
|
70
|
+
Seconds: Secondes
|
|
66
71
|
ShowAll: Tout montrer
|
|
67
72
|
SixMonths: 6 mois
|
|
68
73
|
Size: Taille
|
|
@@ -71,6 +76,8 @@ fr:
|
|
|
71
76
|
Stop: Arrêter
|
|
72
77
|
StopAll: Tout arrêter
|
|
73
78
|
StopPolling: Arrêt du temps réel
|
|
79
|
+
Success: Succès
|
|
80
|
+
Summary: Résumé
|
|
74
81
|
Thread: Thread
|
|
75
82
|
Threads: Threads
|
|
76
83
|
ThreeMonths: 3 mois
|
|
@@ -83,3 +90,10 @@ fr:
|
|
|
83
90
|
Worker: Travailleur
|
|
84
91
|
active: actif
|
|
85
92
|
idle: inactif
|
|
93
|
+
Metrics: Métriques
|
|
94
|
+
NoDataFound: Aucune donnée disponible
|
|
95
|
+
TotalExecutionTime: Temps d'exécution total
|
|
96
|
+
AvgExecutionTime: Temps d'exécution moyen
|
|
97
|
+
Context: Contexte
|
|
98
|
+
Bucket: Bucket
|
|
99
|
+
NoJobMetricsFound: Aucune statistique de tâche récente n'a été trouvée
|