sidekiq 5.2.8 → 6.0.0
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/.gitignore +0 -2
- data/.standard.yml +20 -0
- data/6.0-Upgrade.md +70 -0
- data/Changes.md +31 -3
- data/Ent-2.0-Upgrade.md +37 -0
- data/Ent-Changes.md +12 -0
- data/Gemfile +12 -11
- data/Gemfile.lock +196 -0
- data/Pro-5.0-Upgrade.md +25 -0
- data/Pro-Changes.md +12 -3
- data/README.md +16 -30
- data/Rakefile +5 -4
- data/bin/sidekiqload +26 -22
- data/bin/sidekiqmon +9 -0
- data/lib/generators/sidekiq/templates/worker_test.rb.erb +1 -1
- data/lib/generators/sidekiq/worker_generator.rb +12 -14
- data/lib/sidekiq/api.rb +138 -151
- data/lib/sidekiq/cli.rb +97 -162
- data/lib/sidekiq/client.rb +45 -46
- data/lib/sidekiq/delay.rb +5 -6
- data/lib/sidekiq/exception_handler.rb +10 -12
- data/lib/sidekiq/extensions/action_mailer.rb +10 -20
- data/lib/sidekiq/extensions/active_record.rb +9 -7
- data/lib/sidekiq/extensions/class_methods.rb +9 -7
- data/lib/sidekiq/extensions/generic_proxy.rb +4 -4
- data/lib/sidekiq/fetch.rb +5 -6
- data/lib/sidekiq/job_logger.rb +37 -7
- data/lib/sidekiq/job_retry.rb +45 -58
- data/lib/sidekiq/launcher.rb +59 -51
- data/lib/sidekiq/logger.rb +69 -0
- data/lib/sidekiq/manager.rb +7 -9
- data/lib/sidekiq/middleware/chain.rb +3 -2
- data/lib/sidekiq/middleware/i18n.rb +5 -7
- data/lib/sidekiq/monitor.rb +148 -0
- data/lib/sidekiq/paginator.rb +11 -12
- data/lib/sidekiq/processor.rb +52 -49
- data/lib/sidekiq/rails.rb +23 -29
- data/lib/sidekiq/redis_connection.rb +31 -37
- data/lib/sidekiq/scheduled.rb +17 -19
- data/lib/sidekiq/testing/inline.rb +2 -1
- data/lib/sidekiq/testing.rb +22 -23
- data/lib/sidekiq/util.rb +17 -14
- data/lib/sidekiq/version.rb +2 -1
- data/lib/sidekiq/web/action.rb +14 -10
- data/lib/sidekiq/web/application.rb +60 -57
- data/lib/sidekiq/web/helpers.rb +66 -67
- data/lib/sidekiq/web/router.rb +17 -14
- data/lib/sidekiq/web.rb +41 -49
- data/lib/sidekiq/worker.rb +124 -97
- data/lib/sidekiq.rb +53 -42
- data/sidekiq.gemspec +16 -16
- data/web/assets/javascripts/dashboard.js +2 -21
- data/web/locales/ja.yml +2 -1
- metadata +21 -31
- data/.travis.yml +0 -11
- data/bin/sidekiqctl +0 -20
- data/lib/sidekiq/core_ext.rb +0 -1
- data/lib/sidekiq/ctl.rb +0 -221
- data/lib/sidekiq/logging.rb +0 -122
- data/lib/sidekiq/middleware/server/active_record.rb +0 -23
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "logger"
|
4
|
+
require "time"
|
5
|
+
|
6
|
+
module Sidekiq
|
7
|
+
class Logger < ::Logger
|
8
|
+
def initialize(*args)
|
9
|
+
super
|
10
|
+
|
11
|
+
self.formatter = Sidekiq.log_formatter
|
12
|
+
end
|
13
|
+
|
14
|
+
def with_context(hash)
|
15
|
+
ctx.merge!(hash)
|
16
|
+
yield
|
17
|
+
ensure
|
18
|
+
hash.keys.each { |key| ctx.delete(key) }
|
19
|
+
end
|
20
|
+
|
21
|
+
def ctx
|
22
|
+
Thread.current[:sidekiq_context] ||= {}
|
23
|
+
end
|
24
|
+
|
25
|
+
module Formatters
|
26
|
+
class Base < ::Logger::Formatter
|
27
|
+
def tid
|
28
|
+
Thread.current["sidekiq_tid"] ||= (Thread.current.object_id ^ ::Process.pid).to_s(36)
|
29
|
+
end
|
30
|
+
|
31
|
+
def ctx
|
32
|
+
Thread.current[:sidekiq_context] ||= {}
|
33
|
+
end
|
34
|
+
|
35
|
+
def format_context
|
36
|
+
" " + ctx.compact.map { |k, v| "#{k}=#{v}" }.join(" ") if ctx.any?
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class Pretty < Base
|
41
|
+
def call(severity, time, program_name, message)
|
42
|
+
"#{time.utc.iso8601(3)} pid=#{::Process.pid} tid=#{tid}#{format_context} #{severity}: #{message}\n"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class WithoutTimestamp < Pretty
|
47
|
+
def call(severity, time, program_name, message)
|
48
|
+
"pid=#{::Process.pid} tid=#{tid}#{format_context} #{severity}: #{message}\n"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class JSON < Base
|
53
|
+
def call(severity, time, program_name, message)
|
54
|
+
hash = {
|
55
|
+
ts: time.utc.iso8601(3),
|
56
|
+
pid: ::Process.pid,
|
57
|
+
tid: tid,
|
58
|
+
lvl: severity,
|
59
|
+
msg: message,
|
60
|
+
}
|
61
|
+
c = ctx
|
62
|
+
hash["ctx"] = c unless c.empty?
|
63
|
+
|
64
|
+
Sidekiq.dump_json(hash) << "\n"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
data/lib/sidekiq/manager.rb
CHANGED
@@ -1,12 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
require 'sidekiq/util'
|
3
|
-
require 'sidekiq/processor'
|
4
|
-
require 'sidekiq/fetch'
|
5
|
-
require 'thread'
|
6
|
-
require 'set'
|
7
2
|
|
8
|
-
|
3
|
+
require "sidekiq/util"
|
4
|
+
require "sidekiq/processor"
|
5
|
+
require "sidekiq/fetch"
|
6
|
+
require "set"
|
9
7
|
|
8
|
+
module Sidekiq
|
10
9
|
##
|
11
10
|
# The Manager is the central coordination point in Sidekiq, controlling
|
12
11
|
# the lifecycle of the Processors.
|
@@ -27,7 +26,7 @@ module Sidekiq
|
|
27
26
|
attr_reader :workers
|
28
27
|
attr_reader :options
|
29
28
|
|
30
|
-
def initialize(options={})
|
29
|
+
def initialize(options = {})
|
31
30
|
logger.debug { options.inspect }
|
32
31
|
@options = options
|
33
32
|
@count = options[:concurrency] || 10
|
@@ -113,7 +112,7 @@ module Sidekiq
|
|
113
112
|
end
|
114
113
|
|
115
114
|
if cleanup.size > 0
|
116
|
-
jobs = cleanup.map {|p| p.job }.compact
|
115
|
+
jobs = cleanup.map { |p| p.job }.compact
|
117
116
|
|
118
117
|
logger.warn { "Terminating #{cleanup.size} busy worker threads" }
|
119
118
|
logger.warn { "Work still in progress #{jobs.inspect}" }
|
@@ -132,6 +131,5 @@ module Sidekiq
|
|
132
131
|
processor.kill
|
133
132
|
end
|
134
133
|
end
|
135
|
-
|
136
134
|
end
|
137
135
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Sidekiq
|
3
4
|
# Middleware is code configured to run before/after
|
4
5
|
# a message is processed. It is patterned after Rack
|
@@ -106,7 +107,7 @@ module Sidekiq
|
|
106
107
|
i = entries.index { |entry| entry.klass == newklass }
|
107
108
|
new_entry = i.nil? ? Entry.new(newklass, *args) : entries.delete_at(i)
|
108
109
|
i = entries.index { |entry| entry.klass == oldklass } || entries.count - 1
|
109
|
-
entries.insert(i+1, new_entry)
|
110
|
+
entries.insert(i + 1, new_entry)
|
110
111
|
end
|
111
112
|
|
112
113
|
def exists?(klass)
|
@@ -139,7 +140,7 @@ module Sidekiq
|
|
139
140
|
|
140
141
|
def initialize(klass, *args)
|
141
142
|
@klass = klass
|
142
|
-
@args
|
143
|
+
@args = args
|
143
144
|
end
|
144
145
|
|
145
146
|
def make_new
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
#
|
3
4
|
# Simple middleware to save the current locale and restore it when the job executes.
|
4
5
|
# Use it by requiring it in your initializer:
|
@@ -9,19 +10,16 @@ module Sidekiq::Middleware::I18n
|
|
9
10
|
# Get the current locale and store it in the message
|
10
11
|
# to be sent to Sidekiq.
|
11
12
|
class Client
|
12
|
-
def call(
|
13
|
-
msg[
|
13
|
+
def call(_worker, msg, _queue, _redis)
|
14
|
+
msg["locale"] ||= I18n.locale
|
14
15
|
yield
|
15
16
|
end
|
16
17
|
end
|
17
18
|
|
18
19
|
# Pull the msg locale out and set the current thread to use it.
|
19
20
|
class Server
|
20
|
-
def call(
|
21
|
-
I18n.
|
22
|
-
yield
|
23
|
-
ensure
|
24
|
-
I18n.locale = I18n.default_locale
|
21
|
+
def call(_worker, msg, _queue, &block)
|
22
|
+
I18n.with_locale(msg.fetch("locale", I18n.default_locale), &block)
|
25
23
|
end
|
26
24
|
end
|
27
25
|
end
|
@@ -0,0 +1,148 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "fileutils"
|
4
|
+
require "sidekiq/api"
|
5
|
+
|
6
|
+
class Sidekiq::Monitor
|
7
|
+
CMD = File.basename($PROGRAM_NAME)
|
8
|
+
|
9
|
+
attr_reader :stage
|
10
|
+
|
11
|
+
def self.print_usage
|
12
|
+
puts "#{CMD} - monitor Sidekiq from the command line."
|
13
|
+
puts
|
14
|
+
puts "Usage: #{CMD} status <section>"
|
15
|
+
puts
|
16
|
+
puts " <section> (optional) view a specific section of the status output"
|
17
|
+
puts " Valid sections are: #{Sidekiq::Monitor::Status::VALID_SECTIONS.join(", ")}"
|
18
|
+
puts
|
19
|
+
puts "Set REDIS_URL to the location of your Redis server if not monitoring localhost."
|
20
|
+
end
|
21
|
+
|
22
|
+
class Status
|
23
|
+
VALID_SECTIONS = %w[all version overview processes queues]
|
24
|
+
COL_PAD = 2
|
25
|
+
|
26
|
+
def display(section = nil)
|
27
|
+
section ||= "all"
|
28
|
+
unless VALID_SECTIONS.include? section
|
29
|
+
puts "I don't know how to check the status of '#{section}'!"
|
30
|
+
puts "Try one of these: #{VALID_SECTIONS.join(", ")}"
|
31
|
+
return
|
32
|
+
end
|
33
|
+
send(section)
|
34
|
+
rescue => e
|
35
|
+
puts "Couldn't get status: #{e}"
|
36
|
+
end
|
37
|
+
|
38
|
+
def all
|
39
|
+
version
|
40
|
+
puts
|
41
|
+
overview
|
42
|
+
puts
|
43
|
+
processes
|
44
|
+
puts
|
45
|
+
queues
|
46
|
+
end
|
47
|
+
|
48
|
+
def version
|
49
|
+
puts "Sidekiq #{Sidekiq::VERSION}"
|
50
|
+
puts Time.now
|
51
|
+
end
|
52
|
+
|
53
|
+
def overview
|
54
|
+
puts "---- Overview ----"
|
55
|
+
puts " Processed: #{delimit stats.processed}"
|
56
|
+
puts " Failed: #{delimit stats.failed}"
|
57
|
+
puts " Busy: #{delimit stats.workers_size}"
|
58
|
+
puts " Enqueued: #{delimit stats.enqueued}"
|
59
|
+
puts " Retries: #{delimit stats.retry_size}"
|
60
|
+
puts " Scheduled: #{delimit stats.scheduled_size}"
|
61
|
+
puts " Dead: #{delimit stats.dead_size}"
|
62
|
+
end
|
63
|
+
|
64
|
+
def processes
|
65
|
+
puts "---- Processes (#{process_set.size}) ----"
|
66
|
+
process_set.each_with_index do |process, index|
|
67
|
+
puts "#{process["identity"]} #{tags_for(process)}"
|
68
|
+
puts " Started: #{Time.at(process["started_at"])} (#{time_ago(process["started_at"])})"
|
69
|
+
puts " Threads: #{process["concurrency"]} (#{process["busy"]} busy)"
|
70
|
+
puts " Queues: #{split_multiline(process["queues"].sort, pad: 11)}"
|
71
|
+
puts "" unless (index + 1) == process_set.size
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def queues
|
76
|
+
puts "---- Queues (#{queue_data.size}) ----"
|
77
|
+
columns = {
|
78
|
+
name: [:ljust, (["name"] + queue_data.map(&:name)).map(&:length).max + COL_PAD],
|
79
|
+
size: [:rjust, (["size"] + queue_data.map(&:size)).map(&:length).max + COL_PAD],
|
80
|
+
latency: [:rjust, (["latency"] + queue_data.map(&:latency)).map(&:length).max + COL_PAD],
|
81
|
+
}
|
82
|
+
columns.each { |col, (dir, width)| print col.to_s.upcase.public_send(dir, width) }
|
83
|
+
puts
|
84
|
+
queue_data.each do |q|
|
85
|
+
columns.each do |col, (dir, width)|
|
86
|
+
print q.send(col).public_send(dir, width)
|
87
|
+
end
|
88
|
+
puts
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def delimit(number)
|
95
|
+
number.to_s.reverse.scan(/.{1,3}/).join(",").reverse
|
96
|
+
end
|
97
|
+
|
98
|
+
def split_multiline(values, opts = {})
|
99
|
+
return "none" unless values
|
100
|
+
pad = opts[:pad] || 0
|
101
|
+
max_length = opts[:max_length] || (80 - pad)
|
102
|
+
out = []
|
103
|
+
line = ""
|
104
|
+
values.each do |value|
|
105
|
+
if (line.length + value.length) > max_length
|
106
|
+
out << line
|
107
|
+
line = " " * pad
|
108
|
+
end
|
109
|
+
line << value + ", "
|
110
|
+
end
|
111
|
+
out << line[0..-3]
|
112
|
+
out.join("\n")
|
113
|
+
end
|
114
|
+
|
115
|
+
def tags_for(process)
|
116
|
+
tags = [
|
117
|
+
process["tag"],
|
118
|
+
process["labels"],
|
119
|
+
(process["quiet"] == "true" ? "quiet" : nil),
|
120
|
+
].flatten.compact
|
121
|
+
tags.any? ? "[#{tags.join("] [")}]" : nil
|
122
|
+
end
|
123
|
+
|
124
|
+
def time_ago(timestamp)
|
125
|
+
seconds = Time.now - Time.at(timestamp)
|
126
|
+
return "just now" if seconds < 60
|
127
|
+
return "a minute ago" if seconds < 120
|
128
|
+
return "#{seconds.floor / 60} minutes ago" if seconds < 3600
|
129
|
+
return "an hour ago" if seconds < 7200
|
130
|
+
"#{seconds.floor / 60 / 60} hours ago"
|
131
|
+
end
|
132
|
+
|
133
|
+
QUEUE_STRUCT = Struct.new(:name, :size, :latency)
|
134
|
+
def queue_data
|
135
|
+
@queue_data ||= Sidekiq::Queue.all.map { |q|
|
136
|
+
QUEUE_STRUCT.new(q.name, q.size.to_s, sprintf("%#.2f", q.latency))
|
137
|
+
}
|
138
|
+
end
|
139
|
+
|
140
|
+
def process_set
|
141
|
+
@process_set ||= Sidekiq::ProcessSet.new
|
142
|
+
end
|
143
|
+
|
144
|
+
def stats
|
145
|
+
@stats ||= Sidekiq::Stats.new
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
data/lib/sidekiq/paginator.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Sidekiq
|
3
4
|
module Paginator
|
4
|
-
|
5
|
-
def page(key, pageidx=1, page_size=25, opts=nil)
|
5
|
+
def page(key, pageidx = 1, page_size = 25, opts = nil)
|
6
6
|
current_page = pageidx.to_i < 1 ? 1 : pageidx.to_i
|
7
7
|
pageidx = current_page - 1
|
8
8
|
total_size = 0
|
@@ -14,30 +14,29 @@ module Sidekiq
|
|
14
14
|
type = conn.type(key)
|
15
15
|
|
16
16
|
case type
|
17
|
-
when
|
17
|
+
when "zset"
|
18
18
|
rev = opts && opts[:reverse]
|
19
|
-
total_size, items = conn.multi
|
19
|
+
total_size, items = conn.multi {
|
20
20
|
conn.zcard(key)
|
21
21
|
if rev
|
22
|
-
conn.zrevrange(key, starting, ending, :
|
22
|
+
conn.zrevrange(key, starting, ending, with_scores: true)
|
23
23
|
else
|
24
|
-
conn.zrange(key, starting, ending, :
|
24
|
+
conn.zrange(key, starting, ending, with_scores: true)
|
25
25
|
end
|
26
|
-
|
26
|
+
}
|
27
27
|
[current_page, total_size, items]
|
28
|
-
when
|
29
|
-
total_size, items = conn.multi
|
28
|
+
when "list"
|
29
|
+
total_size, items = conn.multi {
|
30
30
|
conn.llen(key)
|
31
31
|
conn.lrange(key, starting, ending)
|
32
|
-
|
32
|
+
}
|
33
33
|
[current_page, total_size, items]
|
34
|
-
when
|
34
|
+
when "none"
|
35
35
|
[1, 0, []]
|
36
36
|
else
|
37
37
|
raise "can't page a #{type}"
|
38
38
|
end
|
39
39
|
end
|
40
40
|
end
|
41
|
-
|
42
41
|
end
|
43
42
|
end
|
data/lib/sidekiq/processor.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
2
|
+
|
3
|
+
require "sidekiq/util"
|
4
|
+
require "sidekiq/fetch"
|
5
|
+
require "sidekiq/job_logger"
|
6
|
+
require "sidekiq/job_retry"
|
7
7
|
|
8
8
|
module Sidekiq
|
9
9
|
##
|
@@ -23,7 +23,6 @@ module Sidekiq
|
|
23
23
|
# to replace itself and exits.
|
24
24
|
#
|
25
25
|
class Processor
|
26
|
-
|
27
26
|
include Util
|
28
27
|
|
29
28
|
attr_reader :thread
|
@@ -37,19 +36,19 @@ module Sidekiq
|
|
37
36
|
@thread = nil
|
38
37
|
@strategy = (mgr.options[:fetch] || Sidekiq::BasicFetch).new(mgr.options)
|
39
38
|
@reloader = Sidekiq.options[:reloader]
|
40
|
-
@
|
39
|
+
@job_logger = (mgr.options[:job_logger] || Sidekiq::JobLogger).new
|
41
40
|
@retrier = Sidekiq::JobRetry.new
|
42
41
|
end
|
43
42
|
|
44
|
-
def terminate(wait=false)
|
43
|
+
def terminate(wait = false)
|
45
44
|
@done = true
|
46
|
-
return
|
45
|
+
return unless @thread
|
47
46
|
@thread.value if wait
|
48
47
|
end
|
49
48
|
|
50
|
-
def kill(wait=false)
|
49
|
+
def kill(wait = false)
|
51
50
|
@done = true
|
52
|
-
return
|
51
|
+
return unless @thread
|
53
52
|
# unlike the other actors, terminate does not wait
|
54
53
|
# for the thread to finish because we don't know how
|
55
54
|
# long the job will take to finish. Instead we
|
@@ -66,16 +65,12 @@ module Sidekiq
|
|
66
65
|
private unless $TESTING
|
67
66
|
|
68
67
|
def run
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
@mgr.processor_stopped(self)
|
76
|
-
rescue Exception => ex
|
77
|
-
@mgr.processor_died(self, ex)
|
78
|
-
end
|
68
|
+
process_one until @done
|
69
|
+
@mgr.processor_stopped(self)
|
70
|
+
rescue Sidekiq::Shutdown
|
71
|
+
@mgr.processor_stopped(self)
|
72
|
+
rescue Exception => ex
|
73
|
+
@mgr.processor_died(self, ex)
|
79
74
|
end
|
80
75
|
|
81
76
|
def process_one
|
@@ -85,14 +80,15 @@ module Sidekiq
|
|
85
80
|
end
|
86
81
|
|
87
82
|
def get_one
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
rescue Sidekiq::Shutdown
|
93
|
-
rescue => ex
|
94
|
-
handle_fetch_exception(ex)
|
83
|
+
work = @strategy.retrieve_work
|
84
|
+
if @down
|
85
|
+
logger.info { "Redis is online, #{::Process.clock_gettime(::Process::CLOCK_MONOTONIC) - @down} sec downtime" }
|
86
|
+
@down = nil
|
95
87
|
end
|
88
|
+
work
|
89
|
+
rescue Sidekiq::Shutdown
|
90
|
+
rescue => ex
|
91
|
+
handle_fetch_exception(ex)
|
96
92
|
end
|
97
93
|
|
98
94
|
def fetch
|
@@ -106,7 +102,7 @@ module Sidekiq
|
|
106
102
|
end
|
107
103
|
|
108
104
|
def handle_fetch_exception(ex)
|
109
|
-
|
105
|
+
unless @down
|
110
106
|
@down = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
111
107
|
logger.error("Error fetching job: #{ex}")
|
112
108
|
handle_exception(ex)
|
@@ -121,18 +117,18 @@ module Sidekiq
|
|
121
117
|
# job structure to the Web UI
|
122
118
|
pristine = cloned(job_hash)
|
123
119
|
|
124
|
-
|
120
|
+
@job_logger.with_job_hash_context(job_hash) do
|
125
121
|
@retrier.global(pristine, queue) do
|
126
|
-
@
|
122
|
+
@job_logger.call(job_hash, queue) do
|
127
123
|
stats(pristine, queue) do
|
128
124
|
# Rails 5 requires a Reloader to wrap code execution. In order to
|
129
125
|
# constantize the worker and instantiate an instance, we have to call
|
130
126
|
# the Reloader. It handles code loading, db connection management, etc.
|
131
127
|
# Effectively this block denotes a "unit of work" to Rails.
|
132
128
|
@reloader.call do
|
133
|
-
klass
|
129
|
+
klass = constantize(job_hash["class"])
|
134
130
|
worker = klass.new
|
135
|
-
worker.jid = job_hash[
|
131
|
+
worker.jid = job_hash["jid"]
|
136
132
|
@retrier.local(worker, pristine, queue) do
|
137
133
|
yield worker
|
138
134
|
end
|
@@ -152,39 +148,44 @@ module Sidekiq
|
|
152
148
|
begin
|
153
149
|
job_hash = Sidekiq.load_json(jobstr)
|
154
150
|
rescue => ex
|
155
|
-
handle_exception(ex, {
|
151
|
+
handle_exception(ex, {context: "Invalid JSON for job", jobstr: jobstr})
|
156
152
|
# we can't notify because the job isn't a valid hash payload.
|
157
153
|
DeadSet.new.kill(jobstr, notify_failure: false)
|
158
154
|
return work.acknowledge
|
159
155
|
end
|
160
156
|
|
161
|
-
ack =
|
157
|
+
ack = false
|
162
158
|
begin
|
163
159
|
dispatch(job_hash, queue) do |worker|
|
164
160
|
Sidekiq.server_middleware.invoke(worker, job_hash, queue) do
|
165
|
-
execute_job(worker, cloned(job_hash[
|
161
|
+
execute_job(worker, cloned(job_hash["args"]))
|
166
162
|
end
|
167
163
|
end
|
164
|
+
ack = true
|
168
165
|
rescue Sidekiq::Shutdown
|
169
166
|
# Had to force kill this job because it didn't finish
|
170
167
|
# within the timeout. Don't acknowledge the work since
|
171
168
|
# we didn't properly finish it.
|
172
|
-
ack = false
|
173
169
|
rescue Sidekiq::JobRetry::Handled => h
|
174
170
|
# this is the common case: job raised error and Sidekiq::JobRetry::Handled
|
175
171
|
# signals that we created a retry successfully. We can acknowlege the job.
|
176
|
-
|
177
|
-
|
172
|
+
ack = true
|
173
|
+
e = h.cause || h
|
174
|
+
handle_exception(e, {context: "Job raised exception", job: job_hash, jobstr: jobstr})
|
178
175
|
raise e
|
179
176
|
rescue Exception => ex
|
180
177
|
# Unexpected error! This is very bad and indicates an exception that got past
|
181
178
|
# the retry subsystem (e.g. network partition). We won't acknowledge the job
|
182
179
|
# so it can be rescued when using Sidekiq Pro.
|
183
|
-
|
184
|
-
handle_exception(ex, { :context => "Internal exception!", :job => job_hash, :jobstr => jobstr })
|
180
|
+
handle_exception(ex, {context: "Internal exception!", job: job_hash, jobstr: jobstr})
|
185
181
|
raise e
|
186
182
|
ensure
|
187
|
-
|
183
|
+
if ack
|
184
|
+
# We don't want a shutdown signal to interrupt job acknowledgment.
|
185
|
+
Thread.handle_interrupt(Sidekiq::Shutdown => :never) do
|
186
|
+
work.acknowledge
|
187
|
+
end
|
188
|
+
end
|
188
189
|
end
|
189
190
|
end
|
190
191
|
|
@@ -201,12 +202,16 @@ module Sidekiq
|
|
201
202
|
@lock = Mutex.new
|
202
203
|
end
|
203
204
|
|
204
|
-
def incr(amount=1)
|
205
|
-
@lock.synchronize { @value
|
205
|
+
def incr(amount = 1)
|
206
|
+
@lock.synchronize { @value += amount }
|
206
207
|
end
|
207
208
|
|
208
209
|
def reset
|
209
|
-
@lock.synchronize {
|
210
|
+
@lock.synchronize {
|
211
|
+
val = @value
|
212
|
+
@value = 0
|
213
|
+
val
|
214
|
+
}
|
210
215
|
end
|
211
216
|
end
|
212
217
|
|
@@ -243,8 +248,7 @@ module Sidekiq
|
|
243
248
|
WORKER_STATE = SharedWorkerState.new
|
244
249
|
|
245
250
|
def stats(job_hash, queue)
|
246
|
-
tid
|
247
|
-
WORKER_STATE.set(tid, {:queue => queue, :payload => job_hash, :run_at => Time.now.to_i })
|
251
|
+
WORKER_STATE.set(tid, {queue: queue, payload: job_hash, run_at: Time.now.to_i})
|
248
252
|
|
249
253
|
begin
|
250
254
|
yield
|
@@ -265,7 +269,7 @@ module Sidekiq
|
|
265
269
|
end
|
266
270
|
|
267
271
|
def constantize(str)
|
268
|
-
names = str.split(
|
272
|
+
names = str.split("::")
|
269
273
|
names.shift if names.empty? || names.first.empty?
|
270
274
|
|
271
275
|
names.inject(Object) do |constant, name|
|
@@ -274,6 +278,5 @@ module Sidekiq
|
|
274
278
|
constant.const_defined?(name, false) ? constant.const_get(name, false) : constant.const_missing(name)
|
275
279
|
end
|
276
280
|
end
|
277
|
-
|
278
281
|
end
|
279
282
|
end
|
data/lib/sidekiq/rails.rb
CHANGED
@@ -1,35 +1,35 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "sidekiq/worker"
|
4
|
+
|
3
5
|
module Sidekiq
|
4
6
|
class Rails < ::Rails::Engine
|
5
|
-
#
|
6
|
-
#
|
7
|
+
# By including the Options module, we allow AJs to directly control sidekiq features
|
8
|
+
# via the *sidekiq_options* class method and, for instance, not use AJ's retry system.
|
9
|
+
# AJ retries don't show up in the Sidekiq UI Retries tab, save any error data, can't be
|
10
|
+
# manually retried, don't automatically die, etc.
|
7
11
|
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
end
|
12
|
+
# class SomeJob < ActiveJob::Base
|
13
|
+
# queue_as :default
|
14
|
+
# sidekiq_options retry: 3, backtrace: 10
|
15
|
+
# def perform
|
16
|
+
# end
|
17
|
+
# end
|
18
|
+
initializer "sidekiq.active_job_integration" do
|
19
|
+
ActiveSupport.on_load(:active_job) do
|
20
|
+
include ::Sidekiq::Worker::Options unless respond_to?(:sidekiq_options)
|
18
21
|
end
|
19
22
|
end
|
20
23
|
|
24
|
+
# This hook happens after all initializers are run, just before returning
|
25
|
+
# from config/environment.rb back to sidekiq/cli.rb.
|
26
|
+
# We have to add the reloader after initialize to see if cache_classes has
|
27
|
+
# been turned on.
|
28
|
+
#
|
29
|
+
# None of this matters on the client-side, only within the Sidekiq process itself.
|
21
30
|
config.after_initialize do
|
22
|
-
# This hook happens after all initializers are run, just before returning
|
23
|
-
# from config/environment.rb back to sidekiq/cli.rb.
|
24
|
-
# We have to add the reloader after initialize to see if cache_classes has
|
25
|
-
# been turned on.
|
26
|
-
#
|
27
|
-
# None of this matters on the client-side, only within the Sidekiq process itself.
|
28
|
-
#
|
29
31
|
Sidekiq.configure_server do |_|
|
30
|
-
|
31
|
-
Sidekiq.options[:reloader] = Sidekiq::Rails::Reloader.new
|
32
|
-
end
|
32
|
+
Sidekiq.options[:reloader] = Sidekiq::Rails::Reloader.new
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
@@ -48,11 +48,5 @@ module Sidekiq
|
|
48
48
|
"#<Sidekiq::Rails::Reloader @app=#{@app.class.name}>"
|
49
49
|
end
|
50
50
|
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
if defined?(::Rails) && ::Rails::VERSION::MAJOR < 4
|
55
|
-
$stderr.puts("**************************************************")
|
56
|
-
$stderr.puts("⛔️ WARNING: Sidekiq server is no longer supported by Rails 3.2 - please ensure your server/workers are updated")
|
57
|
-
$stderr.puts("**************************************************")
|
51
|
+
end
|
58
52
|
end
|