sidekiq 2.12.1 → 2.12.3
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 +16 -0
- data/Gemfile +2 -1
- data/lib/sidekiq/actor.rb +2 -62
- data/lib/sidekiq/api.rb +7 -27
- data/lib/sidekiq/capistrano.rb +8 -4
- data/lib/sidekiq/cli.rb +0 -2
- data/lib/sidekiq/extensions/action_mailer.rb +1 -1
- data/lib/sidekiq/extensions/active_record.rb +1 -1
- data/lib/sidekiq/extensions/class_methods.rb +1 -1
- data/lib/sidekiq/manager.rb +19 -5
- data/lib/sidekiq/middleware/server/retry_jobs.rb +2 -2
- data/lib/sidekiq/processor.rb +49 -30
- data/lib/sidekiq/version.rb +1 -1
- data/lib/sidekiq/web.rb +8 -7
- data/sidekiq.gemspec +1 -1
- data/test/helper.rb +1 -0
- data/test/test_api.rb +0 -26
- data/test/test_middleware.rb +2 -0
- data/test/test_processor.rb +43 -11
- data/test/test_testing.rb +1 -0
- data/web/views/queues.slim +5 -5
- data/web/views/retry.slim +6 -2
- metadata +4 -4
data/Changes.md
CHANGED
@@ -1,3 +1,19 @@
|
|
1
|
+
2.12.3
|
2
|
+
-----------
|
3
|
+
|
4
|
+
- Revert back to Celluloid's TaskFiber for job processing which has proven to be more
|
5
|
+
stable than TaskThread. [#985]
|
6
|
+
- Avoid possible lockup during hard shutdown [#997]
|
7
|
+
|
8
|
+
At this point, if you are experiencing stability issues with Sidekiq in
|
9
|
+
Ruby 1.9, please try Ruby 2.0. It seems to be more stable.
|
10
|
+
|
11
|
+
2.12.2
|
12
|
+
-----------
|
13
|
+
|
14
|
+
- Relax slim version requirement to >= 1.1.0
|
15
|
+
- Refactor historical stats to use TTL, not explicit cleanup. [grosser, #971]
|
16
|
+
|
1
17
|
2.12.1
|
2
18
|
-----------
|
3
19
|
|
data/Gemfile
CHANGED
data/lib/sidekiq/actor.rb
CHANGED
@@ -1,67 +1,7 @@
|
|
1
1
|
module Sidekiq
|
2
|
-
#
|
3
|
-
# Celluloid has the nasty side effect of making objects
|
4
|
-
# very hard to test because they are immediately async
|
5
|
-
# upon creation. In testing we want to just treat
|
6
|
-
# the actors as POROs.
|
7
|
-
#
|
8
|
-
# Instead of including Celluloid, we'll just stub
|
9
|
-
# out the key methods we use so that everything works
|
10
|
-
# synchronously. The alternative is no test coverage.
|
11
|
-
#
|
12
2
|
module Actor
|
13
|
-
|
14
|
-
|
15
|
-
TaskThread = 0
|
16
|
-
|
17
|
-
def sleep(amount=0)
|
18
|
-
end
|
19
|
-
|
20
|
-
def after(amount=0)
|
21
|
-
end
|
22
|
-
|
23
|
-
def defer
|
24
|
-
yield
|
25
|
-
end
|
26
|
-
|
27
|
-
def current_actor
|
28
|
-
self
|
29
|
-
end
|
30
|
-
|
31
|
-
def alive?
|
32
|
-
!@dead
|
33
|
-
end
|
34
|
-
|
35
|
-
def terminate
|
36
|
-
@dead = true
|
37
|
-
end
|
38
|
-
|
39
|
-
def async
|
40
|
-
self
|
41
|
-
end
|
42
|
-
|
43
|
-
def signal(sym)
|
44
|
-
end
|
45
|
-
|
46
|
-
# we don't want to hide or catch failures in testing
|
47
|
-
def watchdog(msg)
|
48
|
-
yield
|
49
|
-
end
|
50
|
-
|
51
|
-
def self.included(klass)
|
52
|
-
class << klass
|
53
|
-
alias_method :new_link, :new
|
54
|
-
def trap_exit(meth)
|
55
|
-
end
|
56
|
-
def task_class(konstant)
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
else
|
62
|
-
def self.included(klass)
|
63
|
-
klass.send(:include, Celluloid)
|
64
|
-
end
|
3
|
+
def self.included(klass)
|
4
|
+
klass.send(:include, Celluloid)
|
65
5
|
end
|
66
6
|
end
|
67
7
|
end
|
data/lib/sidekiq/api.rb
CHANGED
@@ -3,17 +3,11 @@ require 'sidekiq'
|
|
3
3
|
module Sidekiq
|
4
4
|
class Stats
|
5
5
|
def processed
|
6
|
-
|
7
|
-
conn.get("stat:processed")
|
8
|
-
end
|
9
|
-
count.nil? ? 0 : count.to_i
|
6
|
+
Sidekiq.redis { |conn| conn.get("stat:processed") }.to_i
|
10
7
|
end
|
11
8
|
|
12
9
|
def failed
|
13
|
-
|
14
|
-
conn.get("stat:failed")
|
15
|
-
end
|
16
|
-
count.nil? ? 0 : count.to_i
|
10
|
+
Sidekiq.redis { |conn| conn.get("stat:failed") }.to_i
|
17
11
|
end
|
18
12
|
|
19
13
|
def reset
|
@@ -62,24 +56,6 @@ module Sidekiq
|
|
62
56
|
date_stat_hash("failed")
|
63
57
|
end
|
64
58
|
|
65
|
-
def self.cleanup
|
66
|
-
days_of_stats_to_keep = 180
|
67
|
-
today = Time.now.utc.to_date
|
68
|
-
delete_before_date = Time.now.utc.to_date - days_of_stats_to_keep
|
69
|
-
|
70
|
-
Sidekiq.redis do |conn|
|
71
|
-
processed_keys = conn.keys("stat:processed:*")
|
72
|
-
earliest = "stat:processed:#{delete_before_date.to_s}"
|
73
|
-
pkeys = processed_keys.select { |key| key < earliest }
|
74
|
-
conn.del(pkeys) if pkeys.size > 0
|
75
|
-
|
76
|
-
failed_keys = conn.keys("stat:failed:*")
|
77
|
-
earliest = "stat:failed:#{delete_before_date.to_s}"
|
78
|
-
fkeys = failed_keys.select { |key| key < earliest }
|
79
|
-
conn.del(fkeys) if fkeys.size > 0
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
59
|
private
|
84
60
|
|
85
61
|
def date_stat_hash(stat)
|
@@ -117,6 +93,10 @@ module Sidekiq
|
|
117
93
|
class Queue
|
118
94
|
include Enumerable
|
119
95
|
|
96
|
+
def self.all
|
97
|
+
Sidekiq.redis {|c| c.smembers('queues') }.map {|q| Sidekiq::Queue.new(q) }
|
98
|
+
end
|
99
|
+
|
120
100
|
attr_reader :name
|
121
101
|
|
122
102
|
def initialize(name="default")
|
@@ -195,7 +175,7 @@ module Sidekiq
|
|
195
175
|
end
|
196
176
|
|
197
177
|
def enqueued_at
|
198
|
-
Time.at(@item['enqueued_at'])
|
178
|
+
Time.at(@item['enqueued_at'] || 0)
|
199
179
|
end
|
200
180
|
|
201
181
|
def queue
|
data/lib/sidekiq/capistrano.rb
CHANGED
@@ -1,9 +1,6 @@
|
|
1
1
|
Capistrano::Configuration.instance.load do
|
2
|
-
before "deploy:update_code", "sidekiq:quiet"
|
3
|
-
after "deploy:stop", "sidekiq:stop"
|
4
|
-
after "deploy:start", "sidekiq:start"
|
5
|
-
before "deploy:restart", "sidekiq:restart"
|
6
2
|
|
3
|
+
_cset(:sidekiq_default_hooks) { true }
|
7
4
|
_cset(:sidekiq_cmd) { "#{fetch(:bundle_cmd, "bundle")} exec sidekiq" }
|
8
5
|
_cset(:sidekiqctl_cmd) { "#{fetch(:bundle_cmd, "bundle")} exec sidekiqctl" }
|
9
6
|
_cset(:sidekiq_timeout) { 10 }
|
@@ -11,6 +8,13 @@ Capistrano::Configuration.instance.load do
|
|
11
8
|
_cset(:sidekiq_pid) { "#{current_path}/tmp/pids/sidekiq.pid" }
|
12
9
|
_cset(:sidekiq_processes) { 1 }
|
13
10
|
|
11
|
+
if fetch(:sidekiq_default_hooks)
|
12
|
+
before "deploy:update_code", "sidekiq:quiet"
|
13
|
+
after "deploy:stop", "sidekiq:stop"
|
14
|
+
after "deploy:start", "sidekiq:start"
|
15
|
+
before "deploy:restart", "sidekiq:restart"
|
16
|
+
end
|
17
|
+
|
14
18
|
namespace :sidekiq do
|
15
19
|
def for_each_process(&block)
|
16
20
|
fetch(:sidekiq_processes).times do |idx|
|
data/lib/sidekiq/cli.rb
CHANGED
@@ -3,7 +3,7 @@ require 'sidekiq/extensions/generic_proxy'
|
|
3
3
|
module Sidekiq
|
4
4
|
module Extensions
|
5
5
|
##
|
6
|
-
# Adds 'delay'
|
6
|
+
# Adds 'delay', 'delay_for' and `delay_until` methods to ActionMailer to offload arbitrary email
|
7
7
|
# delivery to Sidekiq. Example:
|
8
8
|
#
|
9
9
|
# UserMailer.delay.send_welcome_email(new_user)
|
@@ -3,7 +3,7 @@ require 'sidekiq/extensions/generic_proxy'
|
|
3
3
|
module Sidekiq
|
4
4
|
module Extensions
|
5
5
|
##
|
6
|
-
# Adds
|
6
|
+
# Adds 'delay', 'delay_for' and `delay_until` methods to ActiveRecord to offload instance method
|
7
7
|
# execution to Sidekiq. Examples:
|
8
8
|
#
|
9
9
|
# User.recent_signups.each { |user| user.delay.mark_as_awesome }
|
@@ -3,7 +3,7 @@ require 'sidekiq/extensions/generic_proxy'
|
|
3
3
|
module Sidekiq
|
4
4
|
module Extensions
|
5
5
|
##
|
6
|
-
# Adds
|
6
|
+
# Adds 'delay', 'delay_for' and `delay_until` methods to all Classes to offload class method
|
7
7
|
# execution to Sidekiq. Examples:
|
8
8
|
#
|
9
9
|
# User.delay.delete_inactive
|
data/lib/sidekiq/manager.rb
CHANGED
@@ -25,10 +25,11 @@ module Sidekiq
|
|
25
25
|
@done_callback = nil
|
26
26
|
|
27
27
|
@in_progress = {}
|
28
|
+
@threads = {}
|
28
29
|
@done = false
|
29
30
|
@busy = []
|
30
31
|
@fetcher = Fetcher.new(current_actor, options)
|
31
|
-
@ready = @count.times.map { Processor.new_link(current_actor) }
|
32
|
+
@ready = @count.times.map { Processor.new_link(current_actor).tap {|p| p.proxy_id = p.object_id} }
|
32
33
|
end
|
33
34
|
|
34
35
|
def stop(options={})
|
@@ -63,6 +64,7 @@ module Sidekiq
|
|
63
64
|
watchdog('Manager#processor_done died') do
|
64
65
|
@done_callback.call(processor) if @done_callback
|
65
66
|
@in_progress.delete(processor.object_id)
|
67
|
+
@threads.delete(processor.object_id)
|
66
68
|
@busy.delete(processor)
|
67
69
|
if stopped?
|
68
70
|
processor.terminate if processor.alive?
|
@@ -77,10 +79,13 @@ module Sidekiq
|
|
77
79
|
def processor_died(processor, reason)
|
78
80
|
watchdog("Manager#processor_died died") do
|
79
81
|
@in_progress.delete(processor.object_id)
|
82
|
+
@threads.delete(processor.object_id)
|
80
83
|
@busy.delete(processor)
|
81
84
|
|
82
85
|
unless stopped?
|
83
|
-
@ready << Processor.new_link(current_actor)
|
86
|
+
@ready << Processor.new_link(current_actor).tap do |p|
|
87
|
+
p.proxy_id = p.object_id
|
88
|
+
end
|
84
89
|
dispatch
|
85
90
|
else
|
86
91
|
signal(:shutdown) if @busy.empty?
|
@@ -105,6 +110,14 @@ module Sidekiq
|
|
105
110
|
end
|
106
111
|
end
|
107
112
|
|
113
|
+
# A hack worthy of Rube Goldberg. We need to be able
|
114
|
+
# to hard stop a working thread. But there's no way for us to
|
115
|
+
# get handle to the underlying thread performing work for a processor
|
116
|
+
# so we have it call us and tell us.
|
117
|
+
def real_thread(proxy_id, thr)
|
118
|
+
@threads[proxy_id] = thr
|
119
|
+
end
|
120
|
+
|
108
121
|
def procline(tag)
|
109
122
|
"sidekiq #{Sidekiq::VERSION} #{tag}[#{@busy.size} of #{@count} busy]#{stopped? ? ' stopping' : ''}"
|
110
123
|
end
|
@@ -145,10 +158,11 @@ module Sidekiq
|
|
145
158
|
# it is worse to lose a job than to run it twice.
|
146
159
|
Sidekiq::Fetcher.strategy.bulk_requeue(@in_progress.values)
|
147
160
|
|
148
|
-
logger.debug { "Terminating worker threads" }
|
161
|
+
logger.debug { "Terminating #{@busy.size} busy worker threads" }
|
149
162
|
@busy.each do |processor|
|
150
|
-
t = processor.
|
151
|
-
|
163
|
+
if processor.alive? && t = @threads.delete(processor.object_id)
|
164
|
+
t.raise Shutdown
|
165
|
+
end
|
152
166
|
end
|
153
167
|
|
154
168
|
after(0) { signal(:shutdown) }
|
@@ -43,13 +43,12 @@ module Sidekiq
|
|
43
43
|
class RetryJobs
|
44
44
|
include Sidekiq::Util
|
45
45
|
|
46
|
-
# delayed_job uses the same basic formula
|
47
46
|
DEFAULT_MAX_RETRY_ATTEMPTS = 25
|
48
47
|
|
49
48
|
def call(worker, msg, queue)
|
50
49
|
yield
|
51
50
|
rescue Sidekiq::Shutdown
|
52
|
-
# ignore, will be pushed back onto queue
|
51
|
+
# ignore, will be pushed back onto queue during hard_shutdown
|
53
52
|
raise
|
54
53
|
rescue Exception => e
|
55
54
|
raise e unless msg['retry']
|
@@ -110,6 +109,7 @@ module Sidekiq
|
|
110
109
|
end
|
111
110
|
end
|
112
111
|
|
112
|
+
# delayed_job uses the same basic formula
|
113
113
|
def seconds_to_delay(count)
|
114
114
|
(count ** 4) + 15 + (rand(30)*(count+1))
|
115
115
|
end
|
data/lib/sidekiq/processor.rb
CHANGED
@@ -11,11 +11,11 @@ module Sidekiq
|
|
11
11
|
# processes it. It instantiates the worker, runs the middleware
|
12
12
|
# chain and then calls Sidekiq::Worker#perform.
|
13
13
|
class Processor
|
14
|
+
STATS_TIMEOUT = 180 * 24 * 60 * 60
|
15
|
+
|
14
16
|
include Util
|
15
17
|
include Actor
|
16
18
|
|
17
|
-
task_class TaskThread
|
18
|
-
|
19
19
|
def self.default_middleware
|
20
20
|
Middleware::Chain.new do |m|
|
21
21
|
m.add Middleware::Server::Logging
|
@@ -24,10 +24,7 @@ module Sidekiq
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
-
|
28
|
-
# can later kill if it necessary during
|
29
|
-
# hard shutdown.
|
30
|
-
attr_accessor :actual_work_thread
|
27
|
+
attr_accessor :proxy_id
|
31
28
|
|
32
29
|
def initialize(boss)
|
33
30
|
@boss = boss
|
@@ -37,38 +34,56 @@ module Sidekiq
|
|
37
34
|
msgstr = work.message
|
38
35
|
queue = work.queue_name
|
39
36
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
37
|
+
do_defer do
|
38
|
+
@boss.async.real_thread(proxy_id, Thread.current)
|
39
|
+
|
40
|
+
begin
|
41
|
+
msg = Sidekiq.load_json(msgstr)
|
42
|
+
klass = msg['class'].constantize
|
43
|
+
worker = klass.new
|
44
|
+
worker.jid = msg['jid']
|
45
|
+
|
46
|
+
stats(worker, msg, queue) do
|
47
|
+
Sidekiq.server_middleware.invoke(worker, msg, queue) do
|
48
|
+
worker.perform(*cloned(msg['args']))
|
49
|
+
end
|
50
50
|
end
|
51
|
+
rescue Sidekiq::Shutdown
|
52
|
+
# Had to force kill this job because it didn't finish
|
53
|
+
# within the timeout.
|
54
|
+
rescue Exception => ex
|
55
|
+
handle_exception(ex, msg || { :message => msgstr })
|
56
|
+
raise
|
57
|
+
ensure
|
58
|
+
work.acknowledge
|
51
59
|
end
|
52
|
-
rescue Sidekiq::Shutdown
|
53
|
-
# Had to force kill this job because it didn't finish
|
54
|
-
# within the timeout.
|
55
|
-
rescue Exception => ex
|
56
|
-
handle_exception(ex, msg || { :message => msgstr })
|
57
|
-
raise
|
58
|
-
ensure
|
59
|
-
work.acknowledge
|
60
60
|
end
|
61
61
|
|
62
62
|
@boss.async.processor_done(current_actor)
|
63
63
|
end
|
64
64
|
|
65
|
-
# See http://github.com/tarcieri/celluloid/issues/22
|
66
65
|
def inspect
|
67
|
-
"
|
66
|
+
"<Processor##{object_id.to_s(16)}>"
|
68
67
|
end
|
69
68
|
|
70
69
|
private
|
71
70
|
|
71
|
+
# We use Celluloid's defer to workaround tiny little
|
72
|
+
# Fiber stacks (4kb!) in MRI 1.9.
|
73
|
+
#
|
74
|
+
# For some reason, Celluloid's thread dispatch, TaskThread,
|
75
|
+
# is unstable under heavy concurrency but TaskFiber has proven
|
76
|
+
# itself stable.
|
77
|
+
NEED_DEFER = (RUBY_ENGINE == 'ruby' && RUBY_VERSION < '2.0.0')
|
78
|
+
|
79
|
+
def do_defer(&block)
|
80
|
+
if NEED_DEFER
|
81
|
+
defer(&block)
|
82
|
+
else
|
83
|
+
yield
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
72
87
|
def identity
|
73
88
|
@str ||= "#{hostname}:#{process_id}-#{Thread.current.object_id}:default"
|
74
89
|
end
|
@@ -87,21 +102,25 @@ module Sidekiq
|
|
87
102
|
yield
|
88
103
|
rescue Exception
|
89
104
|
redis do |conn|
|
90
|
-
|
105
|
+
failed = "stat:failed:#{Time.now.utc.to_date}"
|
106
|
+
result = conn.multi do
|
91
107
|
conn.incrby("stat:failed", 1)
|
92
|
-
conn.incrby(
|
108
|
+
conn.incrby(failed, 1)
|
93
109
|
end
|
110
|
+
conn.expire(failed, STATS_TIMEOUT) if result.last == 1
|
94
111
|
end
|
95
112
|
raise
|
96
113
|
ensure
|
97
114
|
redis do |conn|
|
98
|
-
|
115
|
+
processed = "stat:processed:#{Time.now.utc.to_date}"
|
116
|
+
result = conn.multi do
|
99
117
|
conn.srem("workers", identity)
|
100
118
|
conn.del("worker:#{identity}")
|
101
119
|
conn.del("worker:#{identity}:started")
|
102
120
|
conn.incrby("stat:processed", 1)
|
103
|
-
conn.incrby(
|
121
|
+
conn.incrby(processed, 1)
|
104
122
|
end
|
123
|
+
conn.expire(processed, STATS_TIMEOUT) if result.last == 1
|
105
124
|
end
|
106
125
|
end
|
107
126
|
end
|
data/lib/sidekiq/version.rb
CHANGED
data/lib/sidekiq/web.rb
CHANGED
@@ -2,7 +2,7 @@ require 'yaml'
|
|
2
2
|
require 'sinatra/base'
|
3
3
|
require 'slim'
|
4
4
|
|
5
|
-
raise "The Sidekiq Web UI requires slim 1.
|
5
|
+
raise "The Sidekiq Web UI requires slim 1.1.0 or greater. You have slim v#{Slim::VERSION}" if Gem::Version.new(Slim::VERSION) < Gem::Version.new('1.1.0')
|
6
6
|
|
7
7
|
require 'sidekiq'
|
8
8
|
require 'sidekiq/api'
|
@@ -41,9 +41,7 @@ module Sidekiq
|
|
41
41
|
def reset_worker_list
|
42
42
|
Sidekiq.redis do |conn|
|
43
43
|
workers = conn.smembers('workers')
|
44
|
-
workers
|
45
|
-
conn.srem('workers', name)
|
46
|
-
end
|
44
|
+
conn.srem('workers', *workers)
|
47
45
|
end
|
48
46
|
end
|
49
47
|
|
@@ -104,13 +102,16 @@ module Sidekiq
|
|
104
102
|
end
|
105
103
|
|
106
104
|
def display_args(args, count=100)
|
107
|
-
args.map
|
105
|
+
args.map do |arg|
|
106
|
+
a = arg.inspect
|
107
|
+
a.size > count ? "#{a[0..count]}..." : a
|
108
|
+
end.join(", ")
|
108
109
|
end
|
109
110
|
|
110
111
|
RETRY_JOB_KEYS = Set.new(%w(
|
111
112
|
queue class args retry_count retried_at failed_at
|
112
113
|
jid error_message error_class backtrace
|
113
|
-
error_backtrace enqueued_at
|
114
|
+
error_backtrace enqueued_at retry
|
114
115
|
))
|
115
116
|
|
116
117
|
def retry_extra_items(retry_job)
|
@@ -158,7 +159,7 @@ module Sidekiq
|
|
158
159
|
end
|
159
160
|
|
160
161
|
get "/queues" do
|
161
|
-
@queues = Sidekiq::
|
162
|
+
@queues = Sidekiq::Queue.all
|
162
163
|
slim :queues
|
163
164
|
end
|
164
165
|
|
data/sidekiq.gemspec
CHANGED
@@ -20,7 +20,7 @@ Gem::Specification.new do |gem|
|
|
20
20
|
gem.add_dependency 'celluloid', '>= 0.14.1'
|
21
21
|
gem.add_dependency 'json'
|
22
22
|
gem.add_development_dependency 'sinatra'
|
23
|
-
gem.add_development_dependency 'slim'
|
23
|
+
gem.add_development_dependency 'slim', '>= 1.1.0'
|
24
24
|
gem.add_development_dependency 'minitest', '~> 5'
|
25
25
|
gem.add_development_dependency 'rake'
|
26
26
|
gem.add_development_dependency 'actionmailer'
|
data/test/helper.rb
CHANGED
data/test/test_api.rb
CHANGED
@@ -127,32 +127,6 @@ class TestApi < Minitest::Test
|
|
127
127
|
end
|
128
128
|
end
|
129
129
|
end
|
130
|
-
|
131
|
-
describe "cleanup" do
|
132
|
-
it 'removes processed stats outside of keep window' do
|
133
|
-
Sidekiq.redis do |c|
|
134
|
-
c.incrby("stat:processed:2012-05-03", 4)
|
135
|
-
c.incrby("stat:processed:2012-06-03", 4)
|
136
|
-
c.incrby("stat:processed:2012-07-03", 1)
|
137
|
-
end
|
138
|
-
Time.stub(:now, Time.parse("2012-12-01 1:00:00 -0500")) do
|
139
|
-
Sidekiq::Stats::History.cleanup
|
140
|
-
assert_equal false, Sidekiq.redis { |c| c.exists("stat:processed:2012-05-03") }
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
it 'removes failed stats outside of keep window' do
|
145
|
-
Sidekiq.redis do |c|
|
146
|
-
c.incrby("stat:failed:2012-05-03", 4)
|
147
|
-
c.incrby("stat:failed:2012-06-03", 4)
|
148
|
-
c.incrby("stat:failed:2012-07-03", 1)
|
149
|
-
end
|
150
|
-
Time.stub(:now, Time.parse("2012-12-01 1:00:00 -0500")) do
|
151
|
-
Sidekiq::Stats::History.cleanup
|
152
|
-
assert_equal false, Sidekiq.redis { |c| c.exists("stat:failed:2012-05-03") }
|
153
|
-
end
|
154
|
-
end
|
155
|
-
end
|
156
130
|
end
|
157
131
|
end
|
158
132
|
|
data/test/test_middleware.rb
CHANGED
@@ -82,6 +82,8 @@ class TestMiddleware < Minitest::Test
|
|
82
82
|
processor = Sidekiq::Processor.new(boss)
|
83
83
|
actor = Minitest::Mock.new
|
84
84
|
actor.expect(:processor_done, nil, [processor])
|
85
|
+
actor.expect(:real_thread, nil, [nil, Celluloid::Thread])
|
86
|
+
boss.expect(:async, actor, [])
|
85
87
|
boss.expect(:async, actor, [])
|
86
88
|
processor.process(Sidekiq::BasicFetch::UnitOfWork.new('queue:default', msg))
|
87
89
|
assert_equal %w(2 before 3 before 0 before work_performed 0 after 3 after 2 after), $recorder.flatten
|
data/test/test_processor.rb
CHANGED
@@ -31,6 +31,8 @@ class TestProcessor < Minitest::Test
|
|
31
31
|
msg = Sidekiq.dump_json({ 'class' => MockWorker.to_s, 'args' => ['myarg'] })
|
32
32
|
actor = Minitest::Mock.new
|
33
33
|
actor.expect(:processor_done, nil, [@processor])
|
34
|
+
actor.expect(:real_thread, nil, [nil, Celluloid::Thread])
|
35
|
+
@boss.expect(:async, actor, [])
|
34
36
|
@boss.expect(:async, actor, [])
|
35
37
|
@processor.process(work(msg))
|
36
38
|
@boss.verify
|
@@ -38,6 +40,9 @@ class TestProcessor < Minitest::Test
|
|
38
40
|
end
|
39
41
|
|
40
42
|
it 'passes exceptions to ExceptionHandler' do
|
43
|
+
actor = Minitest::Mock.new
|
44
|
+
actor.expect(:real_thread, nil, [nil, Celluloid::Thread])
|
45
|
+
@boss.expect(:async, actor, [])
|
41
46
|
msg = Sidekiq.dump_json({ 'class' => MockWorker.to_s, 'args' => ['boom'] })
|
42
47
|
begin
|
43
48
|
@processor.process(work(msg))
|
@@ -51,6 +56,9 @@ class TestProcessor < Minitest::Test
|
|
51
56
|
it 're-raises exceptions after handling' do
|
52
57
|
msg = Sidekiq.dump_json({ 'class' => MockWorker.to_s, 'args' => ['boom'] })
|
53
58
|
re_raise = false
|
59
|
+
actor = Minitest::Mock.new
|
60
|
+
actor.expect(:real_thread, nil, [nil, Celluloid::Thread])
|
61
|
+
@boss.expect(:async, actor, [])
|
54
62
|
|
55
63
|
begin
|
56
64
|
@processor.process(work(msg))
|
@@ -67,6 +75,8 @@ class TestProcessor < Minitest::Test
|
|
67
75
|
processor = ::Sidekiq::Processor.new(@boss)
|
68
76
|
actor = Minitest::Mock.new
|
69
77
|
actor.expect(:processor_done, nil, [processor])
|
78
|
+
actor.expect(:real_thread, nil, [nil, Celluloid::Thread])
|
79
|
+
@boss.expect(:async, actor, [])
|
70
80
|
@boss.expect(:async, actor, [])
|
71
81
|
processor.process(work(msgstr))
|
72
82
|
assert_equal [['myarg']], msg['args']
|
@@ -77,12 +87,26 @@ class TestProcessor < Minitest::Test
|
|
77
87
|
Sidekiq.redis {|c| c.flushdb }
|
78
88
|
end
|
79
89
|
|
90
|
+
def with_expire(time)
|
91
|
+
begin
|
92
|
+
old = Sidekiq::Processor::STATS_TIMEOUT
|
93
|
+
silence_warnings { Sidekiq::Processor.const_set(:STATS_TIMEOUT, time) }
|
94
|
+
yield
|
95
|
+
ensure
|
96
|
+
silence_warnings { Sidekiq::Processor.const_set(:STATS_TIMEOUT, old) }
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
80
100
|
describe 'when successful' do
|
101
|
+
let(:processed_today_key) { "stat:processed:#{Time.now.utc.to_date}" }
|
102
|
+
|
81
103
|
def successful_job
|
82
104
|
msg = Sidekiq.dump_json({ 'class' => MockWorker.to_s, 'args' => ['myarg'] })
|
83
105
|
actor = Minitest::Mock.new
|
106
|
+
actor.expect(:real_thread, nil, [nil, Celluloid::Thread])
|
84
107
|
actor.expect(:processor_done, nil, [@processor])
|
85
108
|
@boss.expect(:async, actor, [])
|
109
|
+
@boss.expect(:async, actor, [])
|
86
110
|
@processor.process(work(msg))
|
87
111
|
end
|
88
112
|
|
@@ -91,17 +115,24 @@ class TestProcessor < Minitest::Test
|
|
91
115
|
assert_equal 1, Sidekiq::Stats.new.processed
|
92
116
|
end
|
93
117
|
|
118
|
+
it 'expires processed stat' do
|
119
|
+
successful_job
|
120
|
+
assert_equal Sidekiq::Processor::STATS_TIMEOUT, Sidekiq.redis { |conn| conn.ttl(processed_today_key) }
|
121
|
+
end
|
122
|
+
|
94
123
|
it 'increments date processed stat' do
|
95
|
-
|
96
|
-
|
97
|
-
date_processed = Sidekiq.redis { |conn| conn.get("stat:processed:2012-12-25") }.to_i
|
98
|
-
assert_equal 1, date_processed
|
99
|
-
end
|
124
|
+
successful_job
|
125
|
+
assert_equal 1, Sidekiq.redis { |conn| conn.get(processed_today_key) }.to_i
|
100
126
|
end
|
101
127
|
end
|
102
128
|
|
103
129
|
describe 'when failed' do
|
130
|
+
let(:failed_today_key) { "stat:failed:#{Time.now.utc.to_date}" }
|
131
|
+
|
104
132
|
def failed_job
|
133
|
+
actor = Minitest::Mock.new
|
134
|
+
actor.expect(:real_thread, nil, [nil, Celluloid::Thread])
|
135
|
+
@boss.expect(:async, actor, [])
|
105
136
|
msg = Sidekiq.dump_json({ 'class' => MockWorker.to_s, 'args' => ['boom'] })
|
106
137
|
begin
|
107
138
|
@processor.process(work(msg))
|
@@ -115,14 +146,15 @@ class TestProcessor < Minitest::Test
|
|
115
146
|
end
|
116
147
|
|
117
148
|
it 'increments date failed stat' do
|
118
|
-
|
119
|
-
|
120
|
-
date_failed = Sidekiq.redis { |conn| conn.get("stat:failed:2012-12-25") }.to_i
|
121
|
-
assert_equal 1, date_failed
|
122
|
-
end
|
149
|
+
failed_job
|
150
|
+
assert_equal 1, Sidekiq.redis { |conn| conn.get(failed_today_key) }.to_i
|
123
151
|
end
|
124
|
-
end
|
125
152
|
|
153
|
+
it 'expires failed stat' do
|
154
|
+
failed_job
|
155
|
+
assert_equal Sidekiq::Processor::STATS_TIMEOUT, Sidekiq.redis { |conn| conn.ttl(failed_today_key) }
|
156
|
+
end
|
157
|
+
end
|
126
158
|
end
|
127
159
|
end
|
128
160
|
end
|
data/test/test_testing.rb
CHANGED
data/web/views/queues.slim
CHANGED
@@ -5,11 +5,11 @@ table class="queues table table-hover table-bordered table-striped table-white"
|
|
5
5
|
th = t('Queue')
|
6
6
|
th = t('Size')
|
7
7
|
th = t('Actions')
|
8
|
-
- @queues.each do |queue
|
8
|
+
- @queues.each do |queue|
|
9
9
|
tr
|
10
10
|
td
|
11
|
-
a href="#{root_path}queues/#{queue}" #{queue}
|
12
|
-
td= number_with_delimiter(size)
|
11
|
+
a href="#{root_path}queues/#{queue.name}" #{queue.name}
|
12
|
+
td= number_with_delimiter(queue.size)
|
13
13
|
td width="20%"
|
14
|
-
form action="#{root_path}queues/#{queue}" method="post"
|
15
|
-
input.btn.btn-danger.btn-small type="submit" name="delete" value="#{t('Delete')}" data-confirm="#{t('AreYouSureDeleteQueue', :queue => queue)}"
|
14
|
+
form action="#{root_path}queues/#{queue.name}" method="post"
|
15
|
+
input.btn.btn-danger.btn-small type="submit" name="delete" value="#{t('Delete')}" data-confirm="#{t('AreYouSureDeleteQueue', :queue => queue.name)}"
|
data/web/views/retry.slim
CHANGED
@@ -19,11 +19,15 @@ table class="retry table table-bordered table-striped"
|
|
19
19
|
th JID
|
20
20
|
td
|
21
21
|
code= @retry.jid
|
22
|
+
tr
|
23
|
+
th = t('Enqueued')
|
24
|
+
td== relative_time(@retry.enqueued_at)
|
22
25
|
- unless retry_extra_items(@retry).empty?
|
23
26
|
tr
|
24
27
|
th = t('Extras')
|
25
28
|
td
|
26
|
-
code
|
29
|
+
code
|
30
|
+
= retry_extra_items(@retry).inspect
|
27
31
|
- if @retry['retry_count'] > 0
|
28
32
|
tr
|
29
33
|
th = t('RetryCount')
|
@@ -37,7 +41,7 @@ table class="retry table table-bordered table-striped"
|
|
37
41
|
td== relative_time(@retry['failed_at'].is_a?(Numeric) ? Time.at(@retry['failed_at']) : Time.parse(@retry['failed_at']))
|
38
42
|
tr
|
39
43
|
th = t('NextRetry')
|
40
|
-
td== relative_time(
|
44
|
+
td== relative_time(@retry.at)
|
41
45
|
|
42
46
|
h3 = t('Error')
|
43
47
|
table class="error table table-bordered table-striped"
|
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.12.
|
4
|
+
version: 2.12.3
|
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-
|
12
|
+
date: 2013-06-12 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: redis
|
@@ -114,7 +114,7 @@ dependencies:
|
|
114
114
|
requirements:
|
115
115
|
- - ! '>='
|
116
116
|
- !ruby/object:Gem::Version
|
117
|
-
version:
|
117
|
+
version: 1.1.0
|
118
118
|
type: :development
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -122,7 +122,7 @@ dependencies:
|
|
122
122
|
requirements:
|
123
123
|
- - ! '>='
|
124
124
|
- !ruby/object:Gem::Version
|
125
|
-
version:
|
125
|
+
version: 1.1.0
|
126
126
|
- !ruby/object:Gem::Dependency
|
127
127
|
name: minitest
|
128
128
|
requirement: !ruby/object:Gem::Requirement
|