sidekiq 2.8.0 → 2.9.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 +14 -0
- data/README.md +9 -3
- data/lib/sidekiq.rb +5 -18
- data/lib/sidekiq/cli.rb +49 -51
- data/lib/sidekiq/client.rb +111 -110
- data/lib/sidekiq/manager.rb +3 -2
- data/lib/sidekiq/middleware/server/retry_jobs.rb +18 -4
- data/lib/sidekiq/processor.rb +18 -11
- data/lib/sidekiq/redis_connection.rb +3 -4
- data/lib/sidekiq/testing.rb +14 -6
- data/lib/sidekiq/testing/inline.rb +4 -3
- data/lib/sidekiq/version.rb +1 -1
- data/lib/sidekiq/web.rb +1 -1
- data/sidekiq.gemspec +1 -0
- data/test/helper.rb +5 -0
- data/test/test_api.rb +1 -1
- data/test/test_client.rb +9 -11
- data/test/test_retry.rb +30 -0
- data/test/test_scheduling.rb +3 -3
- data/test/test_sidekiq.rb +10 -0
- data/test/test_testing.rb +12 -4
- data/test/test_testing_inline.rb +2 -2
- data/web/views/_paging.slim +1 -1
- metadata +18 -5
- data/bin/client +0 -7
- data/test/test_stats.rb +0 -47
data/Changes.md
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
2.9.0
|
2
|
+
-----------
|
3
|
+
|
4
|
+
- Update 'sidekiq/testing' to work with any Sidekiq::Client call. It
|
5
|
+
also serializes the arguments as using Redis would. [#713]
|
6
|
+
- Raise a Sidekiq::Shutdown error within workers which don't finish within the hard
|
7
|
+
timeout. This is to prevent unwanted database transaction commits. [#377]
|
8
|
+
- Lazy load Redis connection pool, you no longer need to specify
|
9
|
+
anything in Passenger or Unicorn's after_fork callback [#794]
|
10
|
+
- Add optional Worker#retries_exhausted hook after max retries failed. [jkassemi, #780]
|
11
|
+
- Fix bug in pagination link to last page [pitr, #774]
|
12
|
+
- Upstart scripts for multiple Sidekiq instances [dariocravero, #763]
|
13
|
+
- Use select via pipes instead of poll to catch signals [mrnugget, #761]
|
14
|
+
|
1
15
|
2.8.0
|
2
16
|
-----------
|
3
17
|
|
data/README.md
CHANGED
@@ -1,8 +1,11 @@
|
|
1
1
|
Sidekiq
|
2
2
|
==============
|
3
3
|
|
4
|
-
[![
|
5
|
-
[![
|
4
|
+
- [![Gem Version](https://badge.fury.io/rb/sidekiq.png)](https://rubygems.org/gems/sidekiq)
|
5
|
+
- [![Code Climate](https://codeclimate.com/github/mperham/sidekiq.png)](https://codeclimate.com/github/mperham/sidekiq)
|
6
|
+
- [![Build Status](https://travis-ci.org/mperham/sidekiq.png)](https://travis-ci.org/mperham/sidekiq)
|
7
|
+
- [![Coverage Status](https://coveralls.io/repos/mperham/sidekiq/badge.png?branch=master)](https://coveralls.io/r/mperham/sidekiq)
|
8
|
+
|
6
9
|
|
7
10
|
Simple, efficient message processing for Ruby.
|
8
11
|
|
@@ -43,7 +46,10 @@ Getting Started
|
|
43
46
|
-----------------
|
44
47
|
|
45
48
|
See the [sidekiq home page](http://mperham.github.com/sidekiq) for the simple 4-step process.
|
46
|
-
You can watch [Railscast #366](http://railscasts.com/episodes/366-sidekiq) to see Sidekiq in action.
|
49
|
+
You can watch [Railscast #366](http://railscasts.com/episodes/366-sidekiq) to see Sidekiq in action. If you do everything right, you should see this:
|
50
|
+
|
51
|
+
![Web UI](https://github.com/mperham/sidekiq/raw/master/examples/web-ui.png)
|
52
|
+
|
47
53
|
|
48
54
|
|
49
55
|
More Information
|
data/lib/sidekiq.rb
CHANGED
@@ -27,7 +27,7 @@ module Sidekiq
|
|
27
27
|
:profile => false,
|
28
28
|
}
|
29
29
|
|
30
|
-
def self
|
30
|
+
def self.❨╯°□°❩╯︵┻━┻
|
31
31
|
puts "Calm down, bro"
|
32
32
|
end
|
33
33
|
|
@@ -68,16 +68,15 @@ module Sidekiq
|
|
68
68
|
|
69
69
|
def self.redis(&block)
|
70
70
|
raise ArgumentError, "requires a block" if !block
|
71
|
-
@redis ||= Sidekiq::RedisConnection.create
|
71
|
+
@redis ||= Sidekiq::RedisConnection.create(@hash || {})
|
72
72
|
@redis.with(&block)
|
73
73
|
end
|
74
74
|
|
75
75
|
def self.redis=(hash)
|
76
|
+
return @redis = hash if hash.is_a?(ConnectionPool)
|
77
|
+
|
76
78
|
if hash.is_a?(Hash)
|
77
|
-
@
|
78
|
-
options[:namespace] ||= hash[:namespace]
|
79
|
-
elsif hash.is_a?(ConnectionPool)
|
80
|
-
@redis = hash
|
79
|
+
@hash = hash
|
81
80
|
else
|
82
81
|
raise ArgumentError, "redis= requires a Hash or ConnectionPool"
|
83
82
|
end
|
@@ -115,16 +114,4 @@ module Sidekiq
|
|
115
114
|
self.options[:poll_interval] = interval
|
116
115
|
end
|
117
116
|
|
118
|
-
##
|
119
|
-
# deprecated
|
120
|
-
def self.size(*queues)
|
121
|
-
return Sidekiq::Stats.new.enqueued if queues.empty?
|
122
|
-
|
123
|
-
Sidekiq.redis { |conn|
|
124
|
-
conn.multi {
|
125
|
-
queues.map { |q| conn.llen("queue:#{q}") }
|
126
|
-
}
|
127
|
-
}.inject(0) { |memo, count| memo += count }
|
128
|
-
end
|
129
|
-
|
130
117
|
end
|
data/lib/sidekiq/cli.rb
CHANGED
@@ -1,14 +1,3 @@
|
|
1
|
-
$sidekiq_signals = []
|
2
|
-
|
3
|
-
# Signal handlers should do as little as humanly possible
|
4
|
-
# and defer all work to a non-trap context. We'll have
|
5
|
-
# the main thread poll for signals and handle them there.
|
6
|
-
%w(INT TERM USR1 USR2 TTIN).each do |sig|
|
7
|
-
trap sig do
|
8
|
-
$sidekiq_signals << sig
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
1
|
$stdout.sync = true
|
13
2
|
|
14
3
|
require 'yaml'
|
@@ -20,6 +9,11 @@ require 'sidekiq'
|
|
20
9
|
require 'sidekiq/util'
|
21
10
|
|
22
11
|
module Sidekiq
|
12
|
+
# Used to raise in workers that have not finished within the
|
13
|
+
# hard timeout limit. This is needed to rollback db transactions,
|
14
|
+
# otherwise Ruby's Thread#kill will commit. See #377.
|
15
|
+
class Shutdown < RuntimeError; end
|
16
|
+
|
23
17
|
class CLI
|
24
18
|
include Util
|
25
19
|
include Singleton
|
@@ -31,8 +25,6 @@ module Sidekiq
|
|
31
25
|
|
32
26
|
def initialize
|
33
27
|
@code = nil
|
34
|
-
@interrupt_mutex = Mutex.new
|
35
|
-
@interrupted = false
|
36
28
|
end
|
37
29
|
|
38
30
|
def parse(args=ARGV)
|
@@ -48,6 +40,14 @@ module Sidekiq
|
|
48
40
|
end
|
49
41
|
|
50
42
|
def run
|
43
|
+
self_read, self_write = IO.pipe
|
44
|
+
|
45
|
+
%w(INT TERM USR1 USR2 TTIN).each do |sig|
|
46
|
+
trap sig do
|
47
|
+
self_write.puts(sig)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
51
|
logger.info "Booting Sidekiq #{Sidekiq::VERSION} with Redis at #{redis {|x| x.client.id}}"
|
52
52
|
logger.info "Running in #{RUBY_DESCRIPTION}"
|
53
53
|
logger.info Sidekiq::LICENSE
|
@@ -69,9 +69,9 @@ module Sidekiq
|
|
69
69
|
end
|
70
70
|
launcher.run
|
71
71
|
|
72
|
-
while
|
73
|
-
|
74
|
-
|
72
|
+
while readable_io = IO.select([self_read])
|
73
|
+
signal = readable_io.first[0].gets.strip
|
74
|
+
handle_signal(signal)
|
75
75
|
end
|
76
76
|
rescue Interrupt
|
77
77
|
logger.info 'Shutting down'
|
@@ -82,47 +82,45 @@ module Sidekiq
|
|
82
82
|
end
|
83
83
|
end
|
84
84
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
end
|
97
|
-
# Handle Ctrl-C in JRuby like MRI
|
98
|
-
# http://jira.codehaus.org/browse/JRUBY-4637
|
99
|
-
raise Interrupt
|
100
|
-
when 'TERM'
|
101
|
-
# Heroku sends TERM and then waits 10 seconds for process to exit.
|
102
|
-
raise Interrupt
|
103
|
-
when 'USR1'
|
104
|
-
Sidekiq.logger.info "Received USR1, no longer accepting new work"
|
105
|
-
launcher.manager.async.stop
|
106
|
-
when 'USR2'
|
107
|
-
if Sidekiq.options[:logfile]
|
108
|
-
Sidekiq.logger.info "Received USR2, reopening log file"
|
109
|
-
Sidekiq::Logging.initialize_logger(Sidekiq.options[:logfile])
|
85
|
+
private
|
86
|
+
|
87
|
+
def handle_signal(sig)
|
88
|
+
Sidekiq.logger.debug "Got #{sig} signal"
|
89
|
+
case sig
|
90
|
+
when 'INT'
|
91
|
+
if Sidekiq.options[:profile]
|
92
|
+
result = RubyProf.stop
|
93
|
+
printer = RubyProf::GraphHtmlPrinter.new(result)
|
94
|
+
File.open("profile.html", 'w') do |f|
|
95
|
+
printer.print(f, :min_percent => 1)
|
110
96
|
end
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
97
|
+
end
|
98
|
+
# Handle Ctrl-C in JRuby like MRI
|
99
|
+
# http://jira.codehaus.org/browse/JRUBY-4637
|
100
|
+
raise Interrupt
|
101
|
+
when 'TERM'
|
102
|
+
# Heroku sends TERM and then waits 10 seconds for process to exit.
|
103
|
+
raise Interrupt
|
104
|
+
when 'USR1'
|
105
|
+
Sidekiq.logger.info "Received USR1, no longer accepting new work"
|
106
|
+
launcher.manager.async.stop
|
107
|
+
when 'USR2'
|
108
|
+
if Sidekiq.options[:logfile]
|
109
|
+
Sidekiq.logger.info "Received USR2, reopening log file"
|
110
|
+
Sidekiq::Logging.initialize_logger(Sidekiq.options[:logfile])
|
111
|
+
end
|
112
|
+
when 'TTIN'
|
113
|
+
Thread.list.each do |thread|
|
114
|
+
Sidekiq.logger.info "Thread TID-#{thread.object_id.to_s(36)} #{thread['label']}"
|
115
|
+
if thread.backtrace
|
116
|
+
Sidekiq.logger.info thread.backtrace.join("\n")
|
117
|
+
else
|
118
|
+
Sidekiq.logger.info "<no backtrace available>"
|
119
119
|
end
|
120
120
|
end
|
121
121
|
end
|
122
122
|
end
|
123
123
|
|
124
|
-
private
|
125
|
-
|
126
124
|
def load_celluloid
|
127
125
|
raise "Celluloid cannot be required until here, or it will break Sidekiq's daemonization" if defined?(::Celluloid) && options[:daemon]
|
128
126
|
|
data/lib/sidekiq/client.rb
CHANGED
@@ -4,134 +4,135 @@ require 'sidekiq/middleware/chain'
|
|
4
4
|
|
5
5
|
module Sidekiq
|
6
6
|
class Client
|
7
|
+
class << self
|
7
8
|
|
8
|
-
|
9
|
-
|
9
|
+
def default_middleware
|
10
|
+
Middleware::Chain.new do
|
11
|
+
end
|
10
12
|
end
|
11
|
-
end
|
12
|
-
|
13
|
-
def self.registered_workers
|
14
|
-
Sidekiq.redis { |x| x.smembers('workers') }
|
15
|
-
end
|
16
13
|
|
17
|
-
|
18
|
-
|
19
|
-
|
14
|
+
def registered_workers
|
15
|
+
Sidekiq.redis { |x| x.smembers('workers') }
|
16
|
+
end
|
20
17
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
# queue - the named queue to use, default 'default'
|
25
|
-
# class - the worker class to call, required
|
26
|
-
# args - an array of simple arguments to the perform method, must be JSON-serializable
|
27
|
-
# retry - whether to retry this job if it fails, true or false, default true
|
28
|
-
# backtrace - whether to save any error backtrace, default false
|
29
|
-
#
|
30
|
-
# All options must be strings, not symbols. NB: because we are serializing to JSON, all
|
31
|
-
# symbols in 'args' will be converted to strings.
|
32
|
-
#
|
33
|
-
# Returns nil if not pushed to Redis or a unique Job ID if pushed.
|
34
|
-
#
|
35
|
-
# Example:
|
36
|
-
# Sidekiq::Client.push('queue' => 'my_queue', 'class' => MyWorker, 'args' => ['foo', 1, :bat => 'bar'])
|
37
|
-
#
|
38
|
-
def self.push(item)
|
39
|
-
normed = normalize_item(item)
|
40
|
-
normed, payload = process_single(item['class'], normed)
|
41
|
-
|
42
|
-
pushed = false
|
43
|
-
pushed = raw_push(normed, payload) if normed
|
44
|
-
pushed ? normed['jid'] : nil
|
45
|
-
end
|
18
|
+
def registered_queues
|
19
|
+
Sidekiq.redis { |x| x.smembers('queues') }
|
20
|
+
end
|
46
21
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
22
|
+
##
|
23
|
+
# The main method used to push a job to Redis. Accepts a number of options:
|
24
|
+
#
|
25
|
+
# queue - the named queue to use, default 'default'
|
26
|
+
# class - the worker class to call, required
|
27
|
+
# args - an array of simple arguments to the perform method, must be JSON-serializable
|
28
|
+
# retry - whether to retry this job if it fails, true or false, default true
|
29
|
+
# backtrace - whether to save any error backtrace, default false
|
30
|
+
#
|
31
|
+
# All options must be strings, not symbols. NB: because we are serializing to JSON, all
|
32
|
+
# symbols in 'args' will be converted to strings.
|
33
|
+
#
|
34
|
+
# Returns nil if not pushed to Redis or a unique Job ID if pushed.
|
35
|
+
#
|
36
|
+
# Example:
|
37
|
+
# Sidekiq::Client.push('queue' => 'my_queue', 'class' => MyWorker, 'args' => ['foo', 1, :bat => 'bar'])
|
38
|
+
#
|
39
|
+
def push(item)
|
40
|
+
normed = normalize_item(item)
|
41
|
+
payload = process_single(item['class'], normed)
|
42
|
+
|
43
|
+
pushed = false
|
44
|
+
pushed = raw_push([payload]) if payload
|
45
|
+
pushed ? payload['jid'] : nil
|
46
|
+
end
|
71
47
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
48
|
+
##
|
49
|
+
# Push a large number of jobs to Redis. In practice this method is only
|
50
|
+
# useful if you are pushing tens of thousands of jobs or more. This method
|
51
|
+
# basically cuts down on the redis round trip latency.
|
52
|
+
#
|
53
|
+
# Takes the same arguments as Client.push except that args is expected to be
|
54
|
+
# an Array of Arrays. All other keys are duplicated for each job. Each job
|
55
|
+
# is run through the client middleware pipeline and each job gets its own Job ID
|
56
|
+
# as normal.
|
57
|
+
#
|
58
|
+
# Returns the number of jobs pushed or nil if the pushed failed. The number of jobs
|
59
|
+
# pushed can be less than the number given if the middleware stopped processing for one
|
60
|
+
# or more jobs.
|
61
|
+
def push_bulk(items)
|
62
|
+
normed = normalize_item(items)
|
63
|
+
payloads = items['args'].map do |args|
|
64
|
+
raise ArgumentError, "Bulk arguments must be an Array of Arrays: [[1], [2]]" if !args.is_a?(Array)
|
65
|
+
process_single(items['class'], normed.merge('args' => args, 'jid' => SecureRandom.hex(12)))
|
66
|
+
end.compact
|
67
|
+
|
68
|
+
pushed = false
|
69
|
+
pushed = raw_push(payloads) if !payloads.empty?
|
70
|
+
pushed ? payloads.size : nil
|
71
|
+
end
|
82
72
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
73
|
+
# Resque compatibility helpers.
|
74
|
+
#
|
75
|
+
# Example usage:
|
76
|
+
# Sidekiq::Client.enqueue(MyWorker, 'foo', 1, :bat => 'bar')
|
77
|
+
#
|
78
|
+
# Messages are enqueued to the 'default' queue.
|
79
|
+
#
|
80
|
+
def enqueue(klass, *args)
|
81
|
+
klass.client_push('class' => klass, 'args' => args)
|
82
|
+
end
|
89
83
|
|
90
|
-
|
84
|
+
# Example usage:
|
85
|
+
# Sidekiq::Client.enqueue_to(:queue_name, MyWorker, 'foo', 1, :bat => 'bar')
|
86
|
+
#
|
87
|
+
def enqueue_to(queue, klass, *args)
|
88
|
+
klass.client_push('queue' => queue, 'class' => klass, 'args' => args)
|
89
|
+
end
|
91
90
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
conn.
|
91
|
+
private
|
92
|
+
|
93
|
+
def raw_push(payloads)
|
94
|
+
pushed = false
|
95
|
+
Sidekiq.redis do |conn|
|
96
|
+
if payloads.first['at']
|
97
|
+
pushed = conn.zadd('schedule', payloads.map {|hash| [hash['at'].to_s, Sidekiq.dump_json(hash)]})
|
98
|
+
else
|
99
|
+
q = payloads.first['queue']
|
100
|
+
to_push = payloads.map { |entry| Sidekiq.dump_json(entry) }
|
101
|
+
_, pushed = conn.multi do
|
102
|
+
conn.sadd('queues', q)
|
103
|
+
conn.lpush("queue:#{q}", to_push)
|
104
|
+
end
|
103
105
|
end
|
104
106
|
end
|
107
|
+
pushed
|
105
108
|
end
|
106
|
-
pushed
|
107
|
-
end
|
108
109
|
|
109
|
-
|
110
|
-
|
110
|
+
def process_single(worker_class, item)
|
111
|
+
queue = item['queue']
|
111
112
|
|
112
|
-
|
113
|
-
|
114
|
-
|
113
|
+
Sidekiq.client_middleware.invoke(worker_class, item, queue) do
|
114
|
+
item
|
115
|
+
end
|
115
116
|
end
|
116
|
-
end
|
117
117
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
118
|
+
def normalize_item(item)
|
119
|
+
raise(ArgumentError, "Message must be a Hash of the form: { 'class' => SomeWorker, 'args' => ['bob', 1, :foo => 'bar'] }") unless item.is_a?(Hash)
|
120
|
+
raise(ArgumentError, "Message must include a class and set of arguments: #{item.inspect}") if !item['class'] || !item['args']
|
121
|
+
raise(ArgumentError, "Message args must be an Array") unless item['args'].is_a?(Array)
|
122
|
+
raise(ArgumentError, "Message class must be either a Class or String representation of the class name") unless item['class'].is_a?(Class) || item['class'].is_a?(String)
|
123
|
+
|
124
|
+
if item['class'].is_a?(Class)
|
125
|
+
raise(ArgumentError, "Message must include a Sidekiq::Worker class, not class name: #{item['class'].ancestors.inspect}") if !item['class'].respond_to?('get_sidekiq_options')
|
126
|
+
normalized_item = item['class'].get_sidekiq_options.merge(item)
|
127
|
+
normalized_item['class'] = normalized_item['class'].to_s
|
128
|
+
else
|
129
|
+
normalized_item = Sidekiq::Worker::ClassMethods::DEFAULT_OPTIONS.merge(item)
|
130
|
+
end
|
131
|
+
|
132
|
+
normalized_item['jid'] = SecureRandom.hex(12)
|
133
|
+
normalized_item
|
130
134
|
end
|
131
135
|
|
132
|
-
normalized_item['jid'] = SecureRandom.hex(12)
|
133
|
-
normalized_item
|
134
136
|
end
|
135
|
-
|
136
137
|
end
|
137
138
|
end
|
data/lib/sidekiq/manager.rb
CHANGED
@@ -110,7 +110,7 @@ module Sidekiq
|
|
110
110
|
|
111
111
|
def hard_shutdown_in(delay)
|
112
112
|
after(delay) do
|
113
|
-
watchdog("Manager#
|
113
|
+
watchdog("Manager#hard_shutdown_in died") do
|
114
114
|
# We've reached the timeout and we still have busy workers.
|
115
115
|
# They must die but their messages shall live on.
|
116
116
|
logger.info("Still waiting for #{@busy.size} busy workers")
|
@@ -138,7 +138,8 @@ module Sidekiq
|
|
138
138
|
|
139
139
|
logger.debug { "Terminating worker threads" }
|
140
140
|
@busy.each do |processor|
|
141
|
-
|
141
|
+
t = processor.bare_object.actual_work_thread
|
142
|
+
t.raise Shutdown if processor.alive?
|
142
143
|
end
|
143
144
|
|
144
145
|
after(0) { signal(:shutdown) }
|
@@ -14,7 +14,9 @@ module Sidekiq
|
|
14
14
|
# 3. after a few days, a developer deploys a fix. the message is
|
15
15
|
# reprocessed successfully.
|
16
16
|
# 4. if 3 never happens, sidekiq will eventually give up and throw the
|
17
|
-
# message away.
|
17
|
+
# message away. If the worker defines a method called 'retries_exhausted',
|
18
|
+
# this will be called before throwing the message away. If the
|
19
|
+
# 'retries_exhausted' method throws an exception, it's dropped and logged.
|
18
20
|
#
|
19
21
|
# A message looks like:
|
20
22
|
#
|
@@ -43,7 +45,6 @@ module Sidekiq
|
|
43
45
|
|
44
46
|
# delayed_job uses the same basic formula
|
45
47
|
DEFAULT_MAX_RETRY_ATTEMPTS = 25
|
46
|
-
DELAY = proc { |count| (count ** 4) + 15 + (rand(30)*(count+1)) }
|
47
48
|
|
48
49
|
def call(worker, msg, queue)
|
49
50
|
yield
|
@@ -73,7 +74,7 @@ module Sidekiq
|
|
73
74
|
end
|
74
75
|
|
75
76
|
if count < max_retry_attempts
|
76
|
-
delay =
|
77
|
+
delay = seconds_to_delay(count)
|
77
78
|
logger.debug { "Failure! Retry #{count} in #{delay} seconds" }
|
78
79
|
retry_at = Time.now.to_f + delay
|
79
80
|
payload = Sidekiq.dump_json(msg)
|
@@ -82,11 +83,20 @@ module Sidekiq
|
|
82
83
|
end
|
83
84
|
else
|
84
85
|
# Goodbye dear message, you (re)tried your best I'm sure.
|
85
|
-
|
86
|
+
retries_exhausted(worker, msg)
|
86
87
|
end
|
88
|
+
|
87
89
|
raise e
|
88
90
|
end
|
89
91
|
|
92
|
+
def retries_exhausted(worker, msg)
|
93
|
+
logger.debug { "Dropping message after hitting the retry maximum: #{msg}" }
|
94
|
+
worker.retries_exhausted(*msg['args']) if worker.respond_to?(:retries_exhausted)
|
95
|
+
|
96
|
+
rescue Exception => e
|
97
|
+
handle_exception(e, "Error calling retries_exhausted")
|
98
|
+
end
|
99
|
+
|
90
100
|
def retry_attempts_from(msg_retry, default)
|
91
101
|
if msg_retry.is_a?(Fixnum)
|
92
102
|
msg_retry
|
@@ -95,6 +105,10 @@ module Sidekiq
|
|
95
105
|
end
|
96
106
|
end
|
97
107
|
|
108
|
+
def seconds_to_delay(count)
|
109
|
+
(count ** 4) + 15 + (rand(30)*(count+1))
|
110
|
+
end
|
111
|
+
|
98
112
|
end
|
99
113
|
end
|
100
114
|
end
|
data/lib/sidekiq/processor.rb
CHANGED
@@ -15,8 +15,6 @@ module Sidekiq
|
|
15
15
|
include Util
|
16
16
|
include Celluloid
|
17
17
|
|
18
|
-
# exclusive :process
|
19
|
-
|
20
18
|
def self.default_middleware
|
21
19
|
Middleware::Chain.new do |m|
|
22
20
|
m.add Middleware::Server::Logging
|
@@ -26,6 +24,11 @@ module Sidekiq
|
|
26
24
|
end
|
27
25
|
end
|
28
26
|
|
27
|
+
# store the actual working thread so we
|
28
|
+
# can later kill if it necessary during
|
29
|
+
# hard shutdown.
|
30
|
+
attr_accessor :actual_work_thread
|
31
|
+
|
29
32
|
def initialize(boss)
|
30
33
|
@boss = boss
|
31
34
|
end
|
@@ -34,6 +37,7 @@ module Sidekiq
|
|
34
37
|
msgstr = work.message
|
35
38
|
queue = work.queue_name
|
36
39
|
defer do
|
40
|
+
@actual_work_thread = Thread.current
|
37
41
|
begin
|
38
42
|
msg = Sidekiq.load_json(msgstr)
|
39
43
|
klass = msg['class'].constantize
|
@@ -45,6 +49,9 @@ module Sidekiq
|
|
45
49
|
worker.perform(*cloned(msg['args']))
|
46
50
|
end
|
47
51
|
end
|
52
|
+
rescue Sidekiq::Shutdown
|
53
|
+
# Had to force kill this job because it didn't finish
|
54
|
+
# within the timeout.
|
48
55
|
rescue Exception => ex
|
49
56
|
handle_exception(ex, msg || { :message => msgstr })
|
50
57
|
raise
|
@@ -60,19 +67,19 @@ module Sidekiq
|
|
60
67
|
"#<Processor #{to_s}>"
|
61
68
|
end
|
62
69
|
|
63
|
-
|
70
|
+
private
|
71
|
+
|
72
|
+
def identity
|
64
73
|
@str ||= "#{hostname}:#{process_id}-#{Thread.current.object_id}:default"
|
65
74
|
end
|
66
75
|
|
67
|
-
private
|
68
|
-
|
69
76
|
def stats(worker, msg, queue)
|
70
77
|
redis do |conn|
|
71
78
|
conn.multi do
|
72
|
-
conn.sadd('workers',
|
73
|
-
conn.setex("worker:#{
|
79
|
+
conn.sadd('workers', identity)
|
80
|
+
conn.setex("worker:#{identity}:started", EXPIRY, Time.now.to_s)
|
74
81
|
hash = {:queue => queue, :payload => msg, :run_at => Time.now.to_i }
|
75
|
-
conn.setex("worker:#{
|
82
|
+
conn.setex("worker:#{identity}", EXPIRY, Sidekiq.dump_json(hash))
|
76
83
|
end
|
77
84
|
end
|
78
85
|
|
@@ -89,9 +96,9 @@ module Sidekiq
|
|
89
96
|
ensure
|
90
97
|
redis do |conn|
|
91
98
|
conn.multi do
|
92
|
-
conn.srem("workers",
|
93
|
-
conn.del("worker:#{
|
94
|
-
conn.del("worker:#{
|
99
|
+
conn.srem("workers", identity)
|
100
|
+
conn.del("worker:#{identity}")
|
101
|
+
conn.del("worker:#{identity}:started")
|
95
102
|
conn.incrby("stat:processed", 1)
|
96
103
|
conn.incrby("stat:processed:#{Time.now.utc.to_date}", 1)
|
97
104
|
end
|
@@ -1,24 +1,22 @@
|
|
1
1
|
require 'connection_pool'
|
2
2
|
require 'redis'
|
3
|
-
require 'redis/namespace'
|
4
3
|
|
5
4
|
module Sidekiq
|
6
5
|
class RedisConnection
|
7
6
|
def self.create(options={})
|
8
7
|
url = options[:url] || determine_redis_provider || 'redis://localhost:6379/0'
|
9
|
-
driver = options[:driver] || 'ruby'
|
10
8
|
# need a connection for Fetcher and Retry
|
11
9
|
size = options[:size] || (Sidekiq.server? ? (Sidekiq.options[:concurrency] + 2) : 5)
|
12
|
-
namespace = options[:namespace]
|
13
10
|
|
14
11
|
ConnectionPool.new(:timeout => 1, :size => size) do
|
15
|
-
build_client(url, namespace, driver)
|
12
|
+
build_client(url, options[:namespace], options[:driver] || 'ruby')
|
16
13
|
end
|
17
14
|
end
|
18
15
|
|
19
16
|
def self.build_client(url, namespace, driver)
|
20
17
|
client = Redis.connect(:url => url, :driver => driver)
|
21
18
|
if namespace
|
19
|
+
require 'redis/namespace'
|
22
20
|
Redis::Namespace.new(namespace, :redis => client)
|
23
21
|
else
|
24
22
|
client
|
@@ -28,6 +26,7 @@ module Sidekiq
|
|
28
26
|
|
29
27
|
# Not public
|
30
28
|
def self.determine_redis_provider
|
29
|
+
# REDISTOGO_URL is only support for legacy reasons
|
31
30
|
return ENV['REDISTOGO_URL'] if ENV['REDISTOGO_URL']
|
32
31
|
provider = ENV['REDIS_PROVIDER'] || 'REDIS_URL'
|
33
32
|
ENV[provider]
|
data/lib/sidekiq/testing.rb
CHANGED
@@ -1,4 +1,18 @@
|
|
1
1
|
module Sidekiq
|
2
|
+
|
3
|
+
class Client
|
4
|
+
class << self
|
5
|
+
alias_method :raw_push_old, :raw_push
|
6
|
+
|
7
|
+
def raw_push(payloads)
|
8
|
+
payloads.each do |job|
|
9
|
+
job['class'].constantize.jobs << Sidekiq.load_json(Sidekiq.dump_json(job))
|
10
|
+
end
|
11
|
+
true
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
2
16
|
module Worker
|
3
17
|
##
|
4
18
|
# The Sidekiq testing infrastructure overrides perform_async
|
@@ -56,12 +70,6 @@ module Sidekiq
|
|
56
70
|
# Then I should receive a welcome email to "foo@example.com"
|
57
71
|
#
|
58
72
|
module ClassMethods
|
59
|
-
alias_method :client_push_old, :client_push
|
60
|
-
|
61
|
-
def client_push(opts)
|
62
|
-
jobs << opts
|
63
|
-
opts.object_id
|
64
|
-
end
|
65
73
|
|
66
74
|
# Jobs queued for this worker
|
67
75
|
def jobs
|
@@ -28,9 +28,10 @@ module Sidekiq
|
|
28
28
|
#
|
29
29
|
singleton_class.class_eval do
|
30
30
|
alias_method :raw_push_old, :raw_push
|
31
|
-
def raw_push(
|
32
|
-
|
33
|
-
|
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'])
|
34
35
|
end
|
35
36
|
|
36
37
|
true
|
data/lib/sidekiq/version.rb
CHANGED
data/lib/sidekiq/web.rb
CHANGED
data/sidekiq.gemspec
CHANGED
data/test/helper.rb
CHANGED
data/test/test_api.rb
CHANGED
data/test/test_client.rb
CHANGED
@@ -46,7 +46,7 @@ class TestClient < MiniTest::Unit::TestCase
|
|
46
46
|
end
|
47
47
|
|
48
48
|
it 'pushes messages to redis' do
|
49
|
-
@redis.expect :lpush, 1, ['queue:foo',
|
49
|
+
@redis.expect :lpush, 1, ['queue:foo', Array]
|
50
50
|
pushed = Sidekiq::Client.push('queue' => 'foo', 'class' => MyWorker, 'args' => [1, 2])
|
51
51
|
assert pushed
|
52
52
|
assert_equal 24, pushed.size
|
@@ -54,7 +54,7 @@ class TestClient < MiniTest::Unit::TestCase
|
|
54
54
|
end
|
55
55
|
|
56
56
|
it 'pushes messages to redis using a String class' do
|
57
|
-
@redis.expect :lpush, 1, ['queue:foo',
|
57
|
+
@redis.expect :lpush, 1, ['queue:foo', Array]
|
58
58
|
pushed = Sidekiq::Client.push('queue' => 'foo', 'class' => 'MyWorker', 'args' => [1, 2])
|
59
59
|
assert pushed
|
60
60
|
assert_equal 24, pushed.size
|
@@ -70,28 +70,28 @@ class TestClient < MiniTest::Unit::TestCase
|
|
70
70
|
end
|
71
71
|
|
72
72
|
it 'handles perform_async' do
|
73
|
-
@redis.expect :lpush, 1, ['queue:default',
|
73
|
+
@redis.expect :lpush, 1, ['queue:default', Array]
|
74
74
|
pushed = MyWorker.perform_async(1, 2)
|
75
75
|
assert pushed
|
76
76
|
@redis.verify
|
77
77
|
end
|
78
78
|
|
79
79
|
it 'handles perform_async on failure' do
|
80
|
-
@redis.expect :lpush, nil, ['queue:default',
|
80
|
+
@redis.expect :lpush, nil, ['queue:default', Array]
|
81
81
|
pushed = MyWorker.perform_async(1, 2)
|
82
82
|
refute pushed
|
83
83
|
@redis.verify
|
84
84
|
end
|
85
85
|
|
86
86
|
it 'enqueues messages to redis' do
|
87
|
-
@redis.expect :lpush, 1, ['queue:default',
|
87
|
+
@redis.expect :lpush, 1, ['queue:default', Array]
|
88
88
|
pushed = Sidekiq::Client.enqueue(MyWorker, 1, 2)
|
89
89
|
assert pushed
|
90
90
|
@redis.verify
|
91
91
|
end
|
92
92
|
|
93
93
|
it 'enqueues messages to redis' do
|
94
|
-
@redis.expect :lpush, 1, ['queue:custom_queue',
|
94
|
+
@redis.expect :lpush, 1, ['queue:custom_queue', Array]
|
95
95
|
pushed = Sidekiq::Client.enqueue_to(:custom_queue, MyWorker, 1, 2)
|
96
96
|
assert pushed
|
97
97
|
@redis.verify
|
@@ -103,7 +103,7 @@ class TestClient < MiniTest::Unit::TestCase
|
|
103
103
|
end
|
104
104
|
|
105
105
|
it 'enqueues to the named queue' do
|
106
|
-
@redis.expect :lpush, 1, ['queue:flimflam',
|
106
|
+
@redis.expect :lpush, 1, ['queue:flimflam', Array]
|
107
107
|
pushed = QueuedWorker.perform_async(1, 2)
|
108
108
|
assert pushed
|
109
109
|
@redis.verify
|
@@ -125,12 +125,10 @@ class TestClient < MiniTest::Unit::TestCase
|
|
125
125
|
Sidekiq::Queue.new.clear
|
126
126
|
end
|
127
127
|
it 'can push a large set of jobs at once' do
|
128
|
-
a = Time.now
|
129
128
|
count = Sidekiq::Client.push_bulk('class' => QueuedWorker, 'args' => (1..1_000).to_a.map { |x| Array(x) })
|
130
129
|
assert_equal 1_000, count
|
131
130
|
end
|
132
131
|
it 'can push a large set of jobs at once using a String class' do
|
133
|
-
a = Time.now
|
134
132
|
count = Sidekiq::Client.push_bulk('class' => 'QueuedWorker', 'args' => (1..1_000).to_a.map { |x| Array(x) })
|
135
133
|
assert_equal 1_000, count
|
136
134
|
end
|
@@ -178,11 +176,11 @@ class TestClient < MiniTest::Unit::TestCase
|
|
178
176
|
|
179
177
|
describe 'item normalization' do
|
180
178
|
it 'defaults retry to true' do
|
181
|
-
assert_equal true, Sidekiq::Client.normalize_item
|
179
|
+
assert_equal true, Sidekiq::Client.send(:normalize_item, 'class' => QueuedWorker, 'args' => [])['retry']
|
182
180
|
end
|
183
181
|
|
184
182
|
it "does not normalize numeric retry's" do
|
185
|
-
assert_equal 2, Sidekiq::Client.normalize_item
|
183
|
+
assert_equal 2, Sidekiq::Client.send(:normalize_item, 'class' => CWorker, 'args' => [])['retry']
|
186
184
|
end
|
187
185
|
end
|
188
186
|
end
|
data/test/test_retry.rb
CHANGED
@@ -167,6 +167,36 @@ class TestRetry < MiniTest::Unit::TestCase
|
|
167
167
|
# MiniTest can't assert that a method call did NOT happen!?
|
168
168
|
assert_raises(MockExpectationError) { @redis.verify }
|
169
169
|
end
|
170
|
+
|
171
|
+
describe "retry exhaustion" do
|
172
|
+
let(:worker){ MiniTest::Mock.new }
|
173
|
+
let(:handler){ Sidekiq::Middleware::Server::RetryJobs.new }
|
174
|
+
let(:msg){ {"class"=>"Bob", "args"=>[1, 2, "foo"], "queue"=>"default", "error_message"=>"kerblammo!", "error_class"=>"RuntimeError", "failed_at"=>Time.now.utc, "retry"=>3, "retry_count"=>3} }
|
175
|
+
|
176
|
+
it 'calls worker retries_exhausted after too many retries' do
|
177
|
+
worker.expect(:retries_exhausted, true, [1,2,"foo"])
|
178
|
+
task_misbehaving_worker
|
179
|
+
worker.verify
|
180
|
+
end
|
181
|
+
|
182
|
+
it 'handles and logs retries_exhausted failures gracefully (drops them)' do
|
183
|
+
def worker.retries_exhausted(*args)
|
184
|
+
raise 'bam!'
|
185
|
+
end
|
186
|
+
|
187
|
+
e = task_misbehaving_worker
|
188
|
+
assert_equal e.message, "kerblammo!"
|
189
|
+
worker.verify
|
190
|
+
end
|
191
|
+
|
192
|
+
def task_misbehaving_worker
|
193
|
+
assert_raises RuntimeError do
|
194
|
+
handler.call(worker, msg, 'default') do
|
195
|
+
raise 'kerblammo!'
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
170
200
|
end
|
171
201
|
|
172
202
|
describe 'poller' do
|
data/test/test_scheduling.rb
CHANGED
@@ -18,20 +18,20 @@ class TestScheduling < MiniTest::Unit::TestCase
|
|
18
18
|
end
|
19
19
|
|
20
20
|
it 'schedules a job via interval' do
|
21
|
-
@redis.expect :zadd, true, ['schedule',
|
21
|
+
@redis.expect :zadd, true, ['schedule', Array]
|
22
22
|
assert ScheduledWorker.perform_in(600, 'mike')
|
23
23
|
@redis.verify
|
24
24
|
end
|
25
25
|
|
26
26
|
it 'schedules a job via timestamp' do
|
27
|
-
@redis.expect :zadd, true, ['schedule',
|
27
|
+
@redis.expect :zadd, true, ['schedule', Array]
|
28
28
|
assert ScheduledWorker.perform_in(5.days.from_now, 'mike')
|
29
29
|
@redis.verify
|
30
30
|
end
|
31
31
|
|
32
32
|
it 'schedules multiple jobs at once' do
|
33
33
|
@redis.expect :zadd, true, ['schedule', Array]
|
34
|
-
assert Sidekiq::Client.push_bulk('class' => ScheduledWorker, 'args' => ['mike', 'mike'], 'at' => 600)
|
34
|
+
assert Sidekiq::Client.push_bulk('class' => ScheduledWorker, 'args' => [['mike'], ['mike']], 'at' => 600)
|
35
35
|
@redis.verify
|
36
36
|
end
|
37
37
|
end
|
data/test/test_sidekiq.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# encoding: utf-8
|
1
2
|
require 'helper'
|
2
3
|
|
3
4
|
class TestSidekiq < MiniTest::Unit::TestCase
|
@@ -24,4 +25,13 @@ class TestSidekiq < MiniTest::Unit::TestCase
|
|
24
25
|
end
|
25
26
|
end
|
26
27
|
|
28
|
+
describe "❨╯°□°❩╯︵┻━┻" do
|
29
|
+
before { $stdout = StringIO.new }
|
30
|
+
after { $stdout = STDOUT }
|
31
|
+
|
32
|
+
it "allows angry developers to express their emotional constitution and remedies it" do
|
33
|
+
Sidekiq.❨╯°□°❩╯︵┻━┻
|
34
|
+
assert_equal "Calm down, bro\n", $stdout.string
|
35
|
+
end
|
36
|
+
end
|
27
37
|
end
|
data/test/test_testing.rb
CHANGED
@@ -53,10 +53,10 @@ class TestTesting < MiniTest::Unit::TestCase
|
|
53
53
|
|
54
54
|
after do
|
55
55
|
# Undo override
|
56
|
-
Sidekiq::
|
57
|
-
remove_method :
|
58
|
-
alias_method :
|
59
|
-
remove_method :
|
56
|
+
(class << Sidekiq::Client; self; end).class_eval do
|
57
|
+
remove_method :raw_push
|
58
|
+
alias_method :raw_push, :raw_push_old
|
59
|
+
remove_method :raw_push_old
|
60
60
|
end
|
61
61
|
end
|
62
62
|
|
@@ -109,6 +109,14 @@ class TestTesting < MiniTest::Unit::TestCase
|
|
109
109
|
StoredWorker.drain
|
110
110
|
end
|
111
111
|
assert_equal 0, StoredWorker.jobs.size
|
112
|
+
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'round trip serializes the job arguments' do
|
116
|
+
assert StoredWorker.perform_async(:mike)
|
117
|
+
job = StoredWorker.jobs.first
|
118
|
+
assert_equal "mike", job['args'].first
|
119
|
+
StoredWorker.clear
|
112
120
|
end
|
113
121
|
|
114
122
|
class FirstWorker
|
data/test/test_testing_inline.rb
CHANGED
@@ -81,10 +81,10 @@ class TestInline < MiniTest::Unit::TestCase
|
|
81
81
|
end
|
82
82
|
|
83
83
|
it 'stubs the push_bulk call when in testing mode' do
|
84
|
-
assert Sidekiq::Client.push_bulk({'class' => InlineWorker, 'args' => [true, true]})
|
84
|
+
assert Sidekiq::Client.push_bulk({'class' => InlineWorker, 'args' => [[true], [true]]})
|
85
85
|
|
86
86
|
assert_raises InlineError do
|
87
|
-
Sidekiq::Client.push_bulk({'class' => InlineWorker, 'args' => [true, false]})
|
87
|
+
Sidekiq::Client.push_bulk({'class' => InlineWorker, 'args' => [[true], [false]]})
|
88
88
|
end
|
89
89
|
end
|
90
90
|
|
data/web/views/_paging.slim
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sidekiq
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.9.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-03-
|
12
|
+
date: 2013-03-28 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: redis
|
@@ -187,6 +187,22 @@ dependencies:
|
|
187
187
|
- - ~>
|
188
188
|
- !ruby/object:Gem::Version
|
189
189
|
version: '3'
|
190
|
+
- !ruby/object:Gem::Dependency
|
191
|
+
name: coveralls
|
192
|
+
requirement: !ruby/object:Gem::Requirement
|
193
|
+
none: false
|
194
|
+
requirements:
|
195
|
+
- - ! '>='
|
196
|
+
- !ruby/object:Gem::Version
|
197
|
+
version: '0'
|
198
|
+
type: :development
|
199
|
+
prerelease: false
|
200
|
+
version_requirements: !ruby/object:Gem::Requirement
|
201
|
+
none: false
|
202
|
+
requirements:
|
203
|
+
- - ! '>='
|
204
|
+
- !ruby/object:Gem::Version
|
205
|
+
version: '0'
|
190
206
|
description: Simple, efficient message processing for Ruby
|
191
207
|
email:
|
192
208
|
- mperham@gmail.com
|
@@ -204,7 +220,6 @@ files:
|
|
204
220
|
- LICENSE
|
205
221
|
- README.md
|
206
222
|
- Rakefile
|
207
|
-
- bin/client
|
208
223
|
- bin/sidekiq
|
209
224
|
- bin/sidekiqctl
|
210
225
|
- config.ru
|
@@ -258,7 +273,6 @@ files:
|
|
258
273
|
- test/test_retry.rb
|
259
274
|
- test/test_scheduling.rb
|
260
275
|
- test/test_sidekiq.rb
|
261
|
-
- test/test_stats.rb
|
262
276
|
- test/test_testing.rb
|
263
277
|
- test/test_testing_inline.rb
|
264
278
|
- test/test_web.rb
|
@@ -327,7 +341,6 @@ test_files:
|
|
327
341
|
- test/test_retry.rb
|
328
342
|
- test/test_scheduling.rb
|
329
343
|
- test/test_sidekiq.rb
|
330
|
-
- test/test_stats.rb
|
331
344
|
- test/test_testing.rb
|
332
345
|
- test/test_testing_inline.rb
|
333
346
|
- test/test_web.rb
|
data/bin/client
DELETED
data/test/test_stats.rb
DELETED
@@ -1,47 +0,0 @@
|
|
1
|
-
require 'helper'
|
2
|
-
require 'sidekiq'
|
3
|
-
require 'sidekiq/processor'
|
4
|
-
|
5
|
-
class TestStats < MiniTest::Unit::TestCase
|
6
|
-
describe 'with redis' do
|
7
|
-
before do
|
8
|
-
@redis = Sidekiq.redis = REDIS
|
9
|
-
Sidekiq.redis {|c| c.flushdb }
|
10
|
-
end
|
11
|
-
|
12
|
-
class DumbWorker
|
13
|
-
include Sidekiq::Worker
|
14
|
-
sidekiq_options :queue => 'dumbq'
|
15
|
-
|
16
|
-
def perform(arg)
|
17
|
-
raise 'bang' if arg == nil
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
describe "info counts" do
|
22
|
-
before do
|
23
|
-
@redis.with do |conn|
|
24
|
-
conn.rpush 'queue:foo', '{}'
|
25
|
-
conn.sadd 'queues', 'foo'
|
26
|
-
|
27
|
-
3.times { conn.rpush 'queue:bar', '{}' }
|
28
|
-
conn.sadd 'queues', 'bar'
|
29
|
-
|
30
|
-
2.times { conn.rpush 'queue:baz', '{}' }
|
31
|
-
conn.sadd 'queues', 'baz'
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
describe "size" do
|
36
|
-
it "returns size of queues" do
|
37
|
-
assert_equal 0, Sidekiq.size("foox")
|
38
|
-
assert_equal 1, Sidekiq.size(:foo)
|
39
|
-
assert_equal 1, Sidekiq.size("foo")
|
40
|
-
assert_equal 4, Sidekiq.size("foo", "bar")
|
41
|
-
assert_equal 6, Sidekiq.size
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
end
|
47
|
-
end
|