sidekiq 2.14.1 → 2.15.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.
- data/Changes.md +24 -0
- data/README.md +6 -3
- data/lib/sidekiq.rb +8 -0
- data/lib/sidekiq/api.rb +1 -1
- data/lib/sidekiq/cli.rb +1 -1
- data/lib/sidekiq/client.rb +24 -2
- data/lib/sidekiq/exception_handler.rb +2 -2
- data/lib/sidekiq/launcher.rb +38 -9
- data/lib/sidekiq/manager.rb +0 -3
- data/lib/sidekiq/testing.rb +65 -4
- data/lib/sidekiq/testing/inline.rb +27 -40
- data/lib/sidekiq/util.rb +1 -0
- data/lib/sidekiq/version.rb +1 -1
- data/lib/sidekiq/web.rb +16 -3
- data/lib/sidekiq/worker.rb +6 -8
- data/sidekiq.gemspec +4 -4
- data/test/helper.rb +8 -0
- data/test/test_api.rb +1 -1
- data/test/test_cli.rb +1 -1
- data/test/test_client.rb +23 -2
- data/test/test_exception_handler.rb +1 -1
- data/test/test_extensions.rb +1 -1
- data/test/test_fetch.rb +1 -1
- data/test/test_manager.rb +11 -18
- data/test/test_middleware.rb +1 -1
- data/test/test_processor.rb +1 -1
- data/test/test_redis_connection.rb +3 -3
- data/test/test_retry.rb +1 -1
- data/test/test_scheduled.rb +1 -1
- data/test/test_scheduling.rb +10 -2
- data/test/test_sidekiq.rb +2 -2
- data/test/test_testing.rb +43 -230
- data/test/test_testing_fake.rb +265 -0
- data/test/test_testing_inline.rb +4 -7
- data/test/test_util.rb +1 -1
- data/test/test_web.rb +1 -1
- data/web/assets/javascripts/dashboard.js +3 -1
- data/web/assets/stylesheets/application.css +84 -21
- data/web/assets/stylesheets/bootstrap.css +4 -15
- data/web/views/_nav.erb +12 -2
- data/web/views/_poll.erb +14 -0
- data/web/views/_status.erb +1 -1
- data/web/views/_summary.erb +23 -15
- data/web/views/_workers.erb +1 -1
- data/web/views/dashboard.erb +37 -34
- data/web/views/index.erb +2 -2
- data/web/views/layout.erb +3 -23
- data/web/views/queue.erb +4 -4
- data/web/views/queues.erb +1 -1
- data/web/views/retries.erb +10 -6
- data/web/views/retry.erb +1 -1
- data/web/views/scheduled.erb +2 -2
- data/web/views/scheduled_job_info.erb +1 -1
- metadata +13 -10
data/Changes.md
CHANGED
@@ -1,3 +1,27 @@
|
|
1
|
+
2.15.0
|
2
|
+
-----------
|
3
|
+
|
4
|
+
- The Core Sidekiq actors are now monitored. If any crash, the
|
5
|
+
Sidekiq process logs the error and exits immediately. This is to
|
6
|
+
help prevent "stuck" Sidekiq processes which are running but don't
|
7
|
+
appear to be doing any work. [#1194]
|
8
|
+
- Sidekiq's testing behavior is now dynamic. You can choose between
|
9
|
+
`inline` and `fake` behavior in your tests. See
|
10
|
+
[Testing](wiki/Testing) for detail. [#1193]
|
11
|
+
- The Retries table has a new column for the error message.
|
12
|
+
- The Web UI topbar now contains the status and live poll button.
|
13
|
+
- Orphaned worker records are now auto-vacuumed when you vist the
|
14
|
+
Workers page in the Web UI.
|
15
|
+
- Sidekiq.default\_worker\_options allows you to configure default
|
16
|
+
options for all Sidekiq worker types.
|
17
|
+
|
18
|
+
```ruby
|
19
|
+
Sidekiq.default_worker_options = { 'queue' => 'default', 'backtrace' => true }
|
20
|
+
```
|
21
|
+
- Added two Sidekiq::Client class methods for compatibility with resque-scheduler:
|
22
|
+
`enqueue_to_in` and `enqueue_in` [#1212]
|
23
|
+
- Upgrade Web UI to Bootstrap 3.0. [#1211, jeffboek]
|
24
|
+
|
1
25
|
2.14.1
|
2
26
|
-----------
|
3
27
|
|
data/README.md
CHANGED
@@ -8,7 +8,7 @@ Simple, efficient background processing for Ruby.
|
|
8
8
|
|
9
9
|
Sidekiq uses threads to handle many jobs at the same time in the
|
10
10
|
same process. It does not require Rails but will integrate tightly with
|
11
|
-
Rails 3 to make background processing dead simple.
|
11
|
+
Rails 3/4 to make background processing dead simple.
|
12
12
|
|
13
13
|
Sidekiq is compatible with Resque. It uses the exact same
|
14
14
|
message format as Resque so it can integrate into an existing Resque processing farm.
|
@@ -24,8 +24,11 @@ the same CPU and perform the same amount of work.
|
|
24
24
|
Requirements
|
25
25
|
-----------------
|
26
26
|
|
27
|
-
I test
|
28
|
-
untested but
|
27
|
+
I test with the latest Ruby (2.0) and JRuby versions (1.7). Other versions/VMs
|
28
|
+
are untested but might work fine.
|
29
|
+
|
30
|
+
The last two major Rails releases (3.2 and 4.0) are officially supported, other
|
31
|
+
versions might work fine.
|
29
32
|
|
30
33
|
Redis 2.4 or greater is required.
|
31
34
|
|
data/lib/sidekiq.rb
CHANGED
@@ -89,6 +89,14 @@ module Sidekiq
|
|
89
89
|
@server_chain
|
90
90
|
end
|
91
91
|
|
92
|
+
def self.default_worker_options=(hash)
|
93
|
+
@default_worker_options = default_worker_options.merge(hash)
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.default_worker_options
|
97
|
+
@default_worker_options || { 'retry' => true, 'queue' => 'default' }
|
98
|
+
end
|
99
|
+
|
92
100
|
def self.load_json(string)
|
93
101
|
JSON.parse(string)
|
94
102
|
end
|
data/lib/sidekiq/api.rb
CHANGED
data/lib/sidekiq/cli.rb
CHANGED
@@ -220,7 +220,7 @@ module Sidekiq
|
|
220
220
|
if !File.exist?(options[:require]) ||
|
221
221
|
(File.directory?(options[:require]) && !File.exist?("#{options[:require]}/config/application.rb"))
|
222
222
|
logger.info "=================================================================="
|
223
|
-
logger.info " Please point sidekiq to a Rails 3 application or a Ruby file
|
223
|
+
logger.info " Please point sidekiq to a Rails 3/4 application or a Ruby file "
|
224
224
|
logger.info " to load your worker classes with -r [DIR|FILE]."
|
225
225
|
logger.info "=================================================================="
|
226
226
|
logger.info @parser
|
data/lib/sidekiq/client.rb
CHANGED
@@ -70,7 +70,8 @@ module Sidekiq
|
|
70
70
|
pushed ? payloads.size : nil
|
71
71
|
end
|
72
72
|
|
73
|
-
# Resque compatibility helpers.
|
73
|
+
# Resque compatibility helpers. Note all helpers
|
74
|
+
# should go through Worker#client_push.
|
74
75
|
#
|
75
76
|
# Example usage:
|
76
77
|
# Sidekiq::Client.enqueue(MyWorker, 'foo', 1, :bat => 'bar')
|
@@ -88,6 +89,27 @@ module Sidekiq
|
|
88
89
|
klass.client_push('queue' => queue, 'class' => klass, 'args' => args)
|
89
90
|
end
|
90
91
|
|
92
|
+
# Example usage:
|
93
|
+
# Sidekiq::Client.enqueue_to_in(:queue_name, 3.minutes, MyWorker, 'foo', 1, :bat => 'bar')
|
94
|
+
#
|
95
|
+
def enqueue_to_in(queue, interval, klass, *args)
|
96
|
+
int = interval.to_f
|
97
|
+
now = Time.now.to_f
|
98
|
+
ts = (int < 1_000_000_000 ? now + int : int)
|
99
|
+
|
100
|
+
item = { 'class' => klass, 'args' => args, 'at' => ts, 'queue' => queue }
|
101
|
+
item.delete('at') if ts <= now
|
102
|
+
|
103
|
+
klass.client_push(item)
|
104
|
+
end
|
105
|
+
|
106
|
+
# Example usage:
|
107
|
+
# Sidekiq::Client.enqueue_in(3.minutes, MyWorker, 'foo', 1, :bat => 'bar')
|
108
|
+
#
|
109
|
+
def enqueue_in(interval, klass, *args)
|
110
|
+
klass.perform_in(interval, *args)
|
111
|
+
end
|
112
|
+
|
91
113
|
private
|
92
114
|
|
93
115
|
def raw_push(payloads)
|
@@ -129,7 +151,7 @@ module Sidekiq
|
|
129
151
|
normalized_item = item['class'].get_sidekiq_options.merge(item)
|
130
152
|
normalized_item['class'] = normalized_item['class'].to_s
|
131
153
|
else
|
132
|
-
normalized_item = Sidekiq
|
154
|
+
normalized_item = Sidekiq.default_worker_options.merge(item)
|
133
155
|
end
|
134
156
|
|
135
157
|
normalized_item['jid'] ||= SecureRandom.hex(12)
|
@@ -1,8 +1,8 @@
|
|
1
1
|
module Sidekiq
|
2
2
|
module ExceptionHandler
|
3
3
|
|
4
|
-
def handle_exception(ex, ctxHash)
|
5
|
-
Sidekiq.logger.warn ctxHash
|
4
|
+
def handle_exception(ex, ctxHash={})
|
5
|
+
Sidekiq.logger.warn(ctxHash) if !ctxHash.empty?
|
6
6
|
Sidekiq.logger.warn ex
|
7
7
|
Sidekiq.logger.warn ex.backtrace.join("\n")
|
8
8
|
# This list of services is getting a bit ridiculous.
|
data/lib/sidekiq/launcher.rb
CHANGED
@@ -1,25 +1,54 @@
|
|
1
|
-
require 'sidekiq/
|
1
|
+
require 'sidekiq/actor'
|
2
2
|
require 'sidekiq/manager'
|
3
|
+
require 'sidekiq/fetch'
|
3
4
|
require 'sidekiq/scheduled'
|
4
5
|
|
5
6
|
module Sidekiq
|
7
|
+
# The Launcher is a very simple Actor whose job is to
|
8
|
+
# start, monitor and stop the core Actors in Sidekiq.
|
9
|
+
# If any of these actors die, the Sidekiq process exits
|
10
|
+
# immediately.
|
6
11
|
class Launcher
|
7
|
-
|
12
|
+
include Actor
|
13
|
+
include Util
|
14
|
+
|
15
|
+
trap_exit :actor_died
|
16
|
+
|
17
|
+
attr_reader :manager, :poller, :fetcher
|
18
|
+
|
8
19
|
def initialize(options)
|
20
|
+
@manager = Sidekiq::Manager.new_link options
|
21
|
+
@poller = Sidekiq::Scheduled::Poller.new_link
|
22
|
+
@fetcher = Sidekiq::Fetcher.new_link @manager, options
|
23
|
+
@manager.fetcher = @fetcher
|
24
|
+
@done = false
|
9
25
|
@options = options
|
10
|
-
|
11
|
-
|
26
|
+
end
|
27
|
+
|
28
|
+
def actor_died(actor, reason)
|
29
|
+
return if @done
|
30
|
+
Sidekiq.logger.warn("Sidekiq died due to the following error, cannot recover, process exiting")
|
31
|
+
handle_exception(reason)
|
32
|
+
exit(1)
|
12
33
|
end
|
13
34
|
|
14
35
|
def run
|
15
|
-
|
16
|
-
|
36
|
+
watchdog('Launcher#run') do
|
37
|
+
manager.async.start
|
38
|
+
poller.async.poll(true)
|
39
|
+
end
|
17
40
|
end
|
18
41
|
|
19
42
|
def stop
|
20
|
-
|
21
|
-
|
22
|
-
|
43
|
+
watchdog('Launcher#stop') do
|
44
|
+
@done = true
|
45
|
+
Sidekiq::Fetcher.done!
|
46
|
+
fetcher.async.terminate if fetcher.alive?
|
47
|
+
poller.async.terminate if poller.alive?
|
48
|
+
|
49
|
+
manager.async.stop(:shutdown => true, :timeout => @options[:timeout])
|
50
|
+
manager.wait(:shutdown)
|
51
|
+
end
|
23
52
|
end
|
24
53
|
|
25
54
|
def procline(tag)
|
data/lib/sidekiq/manager.rb
CHANGED
@@ -28,7 +28,6 @@ module Sidekiq
|
|
28
28
|
@threads = {}
|
29
29
|
@done = false
|
30
30
|
@busy = []
|
31
|
-
@fetcher = Fetcher.new(current_actor, options)
|
32
31
|
@ready = @count.times.map do
|
33
32
|
p = Processor.new_link(current_actor)
|
34
33
|
p.proxy_id = p.object_id
|
@@ -42,8 +41,6 @@ module Sidekiq
|
|
42
41
|
timeout = options[:timeout]
|
43
42
|
|
44
43
|
@done = true
|
45
|
-
Sidekiq::Fetcher.done!
|
46
|
-
@fetcher.async.terminate if @fetcher.alive?
|
47
44
|
|
48
45
|
logger.info { "Shutting down #{@ready.size} quiet workers" }
|
49
46
|
@ready.each { |x| x.terminate if x.alive? }
|
data/lib/sidekiq/testing.rb
CHANGED
@@ -1,16 +1,77 @@
|
|
1
1
|
module Sidekiq
|
2
2
|
|
3
|
+
class Testing
|
4
|
+
class << self
|
5
|
+
attr_accessor :__test_mode
|
6
|
+
|
7
|
+
def __set_test_mode(mode, &block)
|
8
|
+
if block
|
9
|
+
current_mode = self.__test_mode
|
10
|
+
begin
|
11
|
+
self.__test_mode = mode
|
12
|
+
block.call
|
13
|
+
ensure
|
14
|
+
self.__test_mode = current_mode
|
15
|
+
end
|
16
|
+
else
|
17
|
+
self.__test_mode = mode
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def disable!(&block)
|
22
|
+
__set_test_mode(:disable, &block)
|
23
|
+
end
|
24
|
+
|
25
|
+
def fake!(&block)
|
26
|
+
__set_test_mode(:fake, &block)
|
27
|
+
end
|
28
|
+
|
29
|
+
def inline!(&block)
|
30
|
+
__set_test_mode(:inline, &block)
|
31
|
+
end
|
32
|
+
|
33
|
+
def enabled?
|
34
|
+
self.__test_mode != :disable
|
35
|
+
end
|
36
|
+
|
37
|
+
def disabled?
|
38
|
+
self.__test_mode == :disable
|
39
|
+
end
|
40
|
+
|
41
|
+
def fake?
|
42
|
+
self.__test_mode == :fake
|
43
|
+
end
|
44
|
+
|
45
|
+
def inline?
|
46
|
+
self.__test_mode == :inline
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Default to fake testing to keep old behavior
|
52
|
+
Sidekiq::Testing.fake!
|
53
|
+
|
3
54
|
class EmptyQueueError < RuntimeError; end
|
4
55
|
|
5
56
|
class Client
|
6
57
|
class << self
|
7
|
-
alias_method :
|
58
|
+
alias_method :raw_push_real, :raw_push
|
8
59
|
|
9
60
|
def raw_push(payloads)
|
10
|
-
|
11
|
-
|
61
|
+
if Sidekiq::Testing.fake?
|
62
|
+
payloads.each do |job|
|
63
|
+
job['class'].constantize.jobs << Sidekiq.load_json(Sidekiq.dump_json(job))
|
64
|
+
end
|
65
|
+
true
|
66
|
+
elsif Sidekiq::Testing.inline?
|
67
|
+
payloads.each do |item|
|
68
|
+
marshalled = Sidekiq.load_json(Sidekiq.dump_json(item))
|
69
|
+
marshalled['class'].constantize.new.perform(*marshalled['args'])
|
70
|
+
end
|
71
|
+
true
|
72
|
+
else
|
73
|
+
raw_push_real(payloads)
|
12
74
|
end
|
13
|
-
true
|
14
75
|
end
|
15
76
|
end
|
16
77
|
end
|
@@ -1,41 +1,28 @@
|
|
1
|
-
|
2
|
-
class Client
|
1
|
+
require 'sidekiq/testing'
|
3
2
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
alias_method :raw_push_old, :raw_push
|
31
|
-
def raw_push(payload)
|
32
|
-
[payload].flatten.each do |item|
|
33
|
-
marshalled = Sidekiq.load_json(Sidekiq.dump_json(item))
|
34
|
-
marshalled['class'].constantize.new.perform(*marshalled['args'])
|
35
|
-
end
|
36
|
-
|
37
|
-
true
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
3
|
+
##
|
4
|
+
# The Sidekiq inline infrastructure overrides perform_async so that it
|
5
|
+
# actually calls perform instead. This allows workers to be run inline in a
|
6
|
+
# testing environment.
|
7
|
+
#
|
8
|
+
# This is similar to `Resque.inline = true` functionality.
|
9
|
+
#
|
10
|
+
# Example:
|
11
|
+
#
|
12
|
+
# require 'sidekiq/testing/inline'
|
13
|
+
#
|
14
|
+
# $external_variable = 0
|
15
|
+
#
|
16
|
+
# class ExternalWorker
|
17
|
+
# include Sidekiq::Worker
|
18
|
+
#
|
19
|
+
# def perform
|
20
|
+
# $external_variable = 1
|
21
|
+
# end
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# assert_equal 0, $external_variable
|
25
|
+
# ExternalWorker.perform_async
|
26
|
+
# assert_equal 1, $external_variable
|
27
|
+
#
|
28
|
+
Sidekiq::Testing.inline!
|
data/lib/sidekiq/util.rb
CHANGED
data/lib/sidekiq/version.rb
CHANGED
data/lib/sidekiq/web.rb
CHANGED
@@ -53,12 +53,21 @@ module Sidekiq
|
|
53
53
|
|
54
54
|
def workers
|
55
55
|
@workers ||= begin
|
56
|
-
|
56
|
+
to_rem = []
|
57
|
+
workers = Sidekiq.redis do |conn|
|
57
58
|
conn.smembers('workers').map do |w|
|
58
59
|
msg = conn.get("worker:#{w}")
|
59
|
-
msg ? [w, Sidekiq.load_json(msg)] : nil
|
60
|
+
msg ? [w, Sidekiq.load_json(msg)] : (to_rem << w; nil)
|
60
61
|
end.compact.sort { |x| x[1] ? -1 : 1 }
|
61
62
|
end
|
63
|
+
|
64
|
+
# Detect and clear out any orphaned worker records.
|
65
|
+
# These can be left in Redis if Sidekiq crashes hard
|
66
|
+
# while processing jobs.
|
67
|
+
if to_rem.size > 0
|
68
|
+
Sidekiq.redis { |conn| conn.srem('workers', to_rem) }
|
69
|
+
end
|
70
|
+
workers
|
62
71
|
end
|
63
72
|
end
|
64
73
|
|
@@ -107,10 +116,14 @@ module Sidekiq
|
|
107
116
|
[score.to_f, jid]
|
108
117
|
end
|
109
118
|
|
119
|
+
def truncate(text, truncate_after_chars = 2000)
|
120
|
+
truncate_after_chars && text.size > truncate_after_chars ? "#{text[0..truncate_after_chars]}..." : text
|
121
|
+
end
|
122
|
+
|
110
123
|
def display_args(args, truncate_after_chars = 2000)
|
111
124
|
args.map do |arg|
|
112
125
|
a = arg.inspect
|
113
|
-
|
126
|
+
truncate(a)
|
114
127
|
end.join(", ")
|
115
128
|
end
|
116
129
|
|
data/lib/sidekiq/worker.rb
CHANGED
@@ -45,12 +45,12 @@ module Sidekiq
|
|
45
45
|
now = Time.now.to_f
|
46
46
|
ts = (int < 1_000_000_000 ? now + int : int)
|
47
47
|
|
48
|
+
item = { 'class' => self, 'args' => args, 'at' => ts }
|
49
|
+
|
48
50
|
# Optimization to enqueue something now that is scheduled to go out now or in the past
|
49
|
-
if ts <= now
|
50
|
-
|
51
|
-
|
52
|
-
client_push('class' => self, 'args' => args, 'at' => ts)
|
53
|
-
end
|
51
|
+
item.delete('at') if ts <= now
|
52
|
+
|
53
|
+
client_push(item)
|
54
54
|
end
|
55
55
|
alias_method :perform_at, :perform_in
|
56
56
|
|
@@ -75,10 +75,8 @@ module Sidekiq
|
|
75
75
|
self.sidekiq_retries_exhausted_block = block
|
76
76
|
end
|
77
77
|
|
78
|
-
DEFAULT_OPTIONS = { 'retry' => true, 'queue' => 'default' }
|
79
|
-
|
80
78
|
def get_sidekiq_options # :nodoc:
|
81
|
-
self.sidekiq_options_hash ||=
|
79
|
+
self.sidekiq_options_hash ||= Sidekiq.default_worker_options
|
82
80
|
end
|
83
81
|
|
84
82
|
def client_push(item) # :nodoc:
|