mirror of
https://github.com/mperham/sidekiq.git
synced 2022-11-09 13:52:34 -05:00
Manage the retrying/deleting of jobs in the Web UI through the 'jid'
rather than 'score' so as to avoid accidentally performing actions on multiple jobs when only one was intended.
This commit is contained in:
parent
166edb8f68
commit
cbfa33cedc
8 changed files with 149 additions and 138 deletions
|
@ -101,11 +101,24 @@ module Sidekiq
|
|||
end
|
||||
|
||||
def at
|
||||
Time.at(@score)
|
||||
Time.at(score)
|
||||
end
|
||||
|
||||
def delete
|
||||
@parent.delete(@score)
|
||||
@parent.delete(score)
|
||||
end
|
||||
|
||||
def retry
|
||||
raise "Retry not available on jobs not in the Retry queue." unless item["failed_at"]
|
||||
Sidekiq.redis do |conn|
|
||||
results = conn.zrangebyscore('retry', score, score)
|
||||
conn.zremrangebyscore('retry', score, score)
|
||||
results.map do |message|
|
||||
msg = Sidekiq.load_json(message)
|
||||
msg['retry_count'] = msg['retry_count'] - 1
|
||||
conn.rpush("queue:#{msg['queue']}", Sidekiq.dump_json(msg))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -150,14 +150,6 @@ module Sidekiq
|
|||
redirect "#{root_path}queues/#{params[:name]}"
|
||||
end
|
||||
|
||||
get "/retries/:score" do
|
||||
halt 404 unless params[:score]
|
||||
@score = params[:score].to_f
|
||||
@retries = retries_with_score(@score)
|
||||
redirect "#{root_path}retries" if @retries.empty?
|
||||
slim :retry
|
||||
end
|
||||
|
||||
get '/retries' do
|
||||
@count = (params[:count] || 25).to_i
|
||||
(@current_page, @total_size, @retries) = page("retry", params[:page], @count)
|
||||
|
@ -165,6 +157,43 @@ module Sidekiq
|
|||
slim :retries
|
||||
end
|
||||
|
||||
get "/retries/:jid" do
|
||||
halt 404 unless params[:jid]
|
||||
@retry = Sidekiq::RetrySet.new.select do |retri|
|
||||
retri.jid == params[:jid]
|
||||
end.first
|
||||
redirect "#{root_path}retries" if @retry.nil?
|
||||
slim :retry
|
||||
end
|
||||
|
||||
post '/retries' do
|
||||
halt 404 unless params['jid']
|
||||
if params['delete']
|
||||
Sidekiq::RetrySet.new.select do |job|
|
||||
job.jid.in?(params['jid'])
|
||||
end.map(&:delete)
|
||||
elsif params['retry']
|
||||
Sidekiq::RetrySet.new.select do |job|
|
||||
job.jid.in?(params['jid'])
|
||||
end.map(&:retry)
|
||||
end
|
||||
redirect "#{root_path}retries"
|
||||
end
|
||||
|
||||
post "/retries/:jid" do
|
||||
halt 404 unless params['jid']
|
||||
if params['retry']
|
||||
Sidekiq::RetrySet.new.select do |job|
|
||||
job.jid == params['jid']
|
||||
end.first.retry
|
||||
elsif params['delete']
|
||||
Sidekiq::RetrySet.new.select do |job|
|
||||
job.jid == params['jid']
|
||||
end.first.delete
|
||||
end
|
||||
redirect "#{root_path}retries"
|
||||
end
|
||||
|
||||
get '/scheduled' do
|
||||
@count = (params[:count] || 25).to_i
|
||||
(@current_page, @total_size, @scheduled) = page("schedule", params[:page], @count)
|
||||
|
@ -173,58 +202,14 @@ module Sidekiq
|
|||
end
|
||||
|
||||
post '/scheduled' do
|
||||
halt 404 unless params[:score]
|
||||
halt 404 unless params['jid']
|
||||
halt 404 unless params['delete']
|
||||
params[:score].each do |score|
|
||||
s = score.to_f
|
||||
process_score('schedule', s, :delete)
|
||||
end
|
||||
Sidekiq::ScheduledSet.new.select do |job|
|
||||
job.jid.in?(params['jid'])
|
||||
end.map(&:delete)
|
||||
redirect "#{root_path}scheduled"
|
||||
end
|
||||
|
||||
post '/retries' do
|
||||
halt 404 unless params[:score]
|
||||
params[:score].each do |score|
|
||||
s = score.to_f
|
||||
if params['retry']
|
||||
process_score('retry', s, :retry)
|
||||
elsif params['delete']
|
||||
process_score('retry', s, :delete)
|
||||
end
|
||||
end
|
||||
redirect "#{root_path}retries"
|
||||
end
|
||||
|
||||
post "/retries/:score" do
|
||||
halt 404 unless params[:score]
|
||||
score = params[:score].to_f
|
||||
if params['retry']
|
||||
process_score('retry', score, :retry)
|
||||
elsif params['delete']
|
||||
process_score('retry', score, :delete)
|
||||
end
|
||||
redirect "#{root_path}retries"
|
||||
end
|
||||
|
||||
def process_score(set, score, operation)
|
||||
case operation
|
||||
when :retry
|
||||
Sidekiq.redis do |conn|
|
||||
results = conn.zrangebyscore(set, score, score)
|
||||
conn.zremrangebyscore(set, score, score)
|
||||
results.map do |message|
|
||||
msg = Sidekiq.load_json(message)
|
||||
msg['retry_count'] = msg['retry_count'] - 1
|
||||
conn.rpush("queue:#{msg['queue']}", Sidekiq.dump_json(msg))
|
||||
end
|
||||
end
|
||||
when :delete
|
||||
Sidekiq.redis do |conn|
|
||||
conn.zremrangebyscore(set, score, score)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.tabs
|
||||
@tabs ||= {
|
||||
"Workers" =>'',
|
||||
|
|
|
@ -9,7 +9,7 @@ class WorkController < ApplicationController
|
|||
|
||||
def email
|
||||
UserMailer.delay_for(30.seconds).greetings(Time.now)
|
||||
render :nothing => true
|
||||
render :text => 'enqueued'
|
||||
end
|
||||
|
||||
def long
|
||||
|
@ -33,6 +33,6 @@ class WorkController < ApplicationController
|
|||
p2 = Post.second
|
||||
end
|
||||
p.delay.long_method(p2)
|
||||
render :nothing => true
|
||||
render :text => 'enqueued'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -64,9 +64,21 @@ class TestApi < MiniTest::Unit::TestCase
|
|||
assert_equal 0, r.size
|
||||
end
|
||||
|
||||
it 'can retry a retry' do
|
||||
add_retry
|
||||
r = Sidekiq::RetrySet.new
|
||||
assert_equal 1, r.size
|
||||
r.first.retry
|
||||
assert_equal 0, r.size
|
||||
assert_equal 1, Sidekiq::Queue.new('default').size
|
||||
job = Sidekiq::Queue.new('default').first
|
||||
assert_equal 'bob', job.jid
|
||||
assert_equal 1, job['retry_count']
|
||||
end
|
||||
|
||||
def add_retry
|
||||
at = Time.now.to_f
|
||||
payload = Sidekiq.dump_json('class' => 'ApiWorker', 'args' => [1, 'mike'], 'queue' => 'default', 'jid' => 'bob')
|
||||
payload = Sidekiq.dump_json('class' => 'ApiWorker', 'args' => [1, 'mike'], 'queue' => 'default', 'jid' => 'bob', 'retry_count' => 2, 'failed_at' => Time.now.utc)
|
||||
Sidekiq.redis do |conn|
|
||||
conn.zadd('retry', at.to_s, payload)
|
||||
end
|
||||
|
|
|
@ -49,7 +49,7 @@ class TestWeb < MiniTest::Unit::TestCase
|
|||
end
|
||||
|
||||
it 'handles missing retry' do
|
||||
get '/retries/12391982.123'
|
||||
get '/retries/2c4c17969825a384a92f023b'
|
||||
assert_equal 302, last_response.status
|
||||
end
|
||||
|
||||
|
@ -107,17 +107,6 @@ class TestWeb < MiniTest::Unit::TestCase
|
|||
assert_match /HardWorker/, last_response.body
|
||||
end
|
||||
|
||||
it 'can delete scheduled' do
|
||||
msg,score = add_scheduled
|
||||
Sidekiq.redis do |conn|
|
||||
assert_equal 1, conn.zcard('schedule')
|
||||
post '/scheduled', 'score' => [score], 'delete' => 'Delete'
|
||||
assert_equal 302, last_response.status
|
||||
assert_equal 'http://example.org/scheduled', last_response.header['Location']
|
||||
assert_equal 0, conn.zcard('schedule')
|
||||
end
|
||||
end
|
||||
|
||||
it 'can display retries' do
|
||||
get '/retries'
|
||||
assert_equal 200, last_response.status
|
||||
|
@ -133,31 +122,28 @@ class TestWeb < MiniTest::Unit::TestCase
|
|||
end
|
||||
|
||||
it 'can display a single retry' do
|
||||
get '/retries/12938712.123333'
|
||||
get '/retries/2c4c17969825a384a92f023b'
|
||||
assert_equal 302, last_response.status
|
||||
_, score = add_retry
|
||||
|
||||
get "/retries/#{score}"
|
||||
msg = add_retry
|
||||
get "/retries/#{msg['jid']}"
|
||||
assert_equal 200, last_response.status
|
||||
assert_match /HardWorker/, last_response.body
|
||||
end
|
||||
|
||||
it 'can delete a single retry' do
|
||||
_, score = add_retry
|
||||
|
||||
post "/retries/#{score}", 'delete' => 'Delete'
|
||||
msg = add_retry
|
||||
post "/retries/#{msg['jid']}", 'delete' => 'Delete'
|
||||
assert_equal 302, last_response.status
|
||||
assert_equal 'http://example.org/retries', last_response.header['Location']
|
||||
|
||||
get "/retries"
|
||||
assert_equal 200, last_response.status
|
||||
refute_match /#{score}/, last_response.body
|
||||
refute_match /#{msg['args'][2]}/, last_response.body
|
||||
end
|
||||
|
||||
it 'can retry a single retry now' do
|
||||
msg, score = add_retry
|
||||
|
||||
post "/retries/#{score}", 'retry' => 'Retry'
|
||||
msg = add_retry
|
||||
post "/retries/#{msg['jid']}", 'retry' => 'Retry'
|
||||
assert_equal 302, last_response.status
|
||||
assert_equal 'http://example.org/retries', last_response.header['Location']
|
||||
|
||||
|
@ -166,6 +152,17 @@ class TestWeb < MiniTest::Unit::TestCase
|
|||
assert_match /#{msg['args'][2]}/, last_response.body
|
||||
end
|
||||
|
||||
it 'can delete scheduled' do
|
||||
msg = add_scheduled
|
||||
Sidekiq.redis do |conn|
|
||||
assert_equal 1, conn.zcard('schedule')
|
||||
post '/scheduled', 'jid' => [msg['jid']], 'delete' => 'Delete'
|
||||
assert_equal 302, last_response.status
|
||||
assert_equal 'http://example.org/scheduled', last_response.header['Location']
|
||||
assert_equal 0, conn.zcard('schedule')
|
||||
end
|
||||
end
|
||||
|
||||
it 'can show user defined tab' do
|
||||
begin
|
||||
Sidekiq::Web.tabs['Custom Tab'] = '/custom'
|
||||
|
@ -179,14 +176,14 @@ class TestWeb < MiniTest::Unit::TestCase
|
|||
end
|
||||
|
||||
def add_scheduled
|
||||
score = Time.now.to_f
|
||||
msg = { 'class' => 'HardWorker',
|
||||
'args' => ['bob', 1, Time.now.to_f],
|
||||
'at' => Time.now.to_f }
|
||||
score = Time.now.to_f
|
||||
'at' => score }
|
||||
Sidekiq.redis do |conn|
|
||||
conn.zadd('schedule', score, Sidekiq.dump_json(msg))
|
||||
end
|
||||
[msg, score]
|
||||
msg
|
||||
end
|
||||
|
||||
def add_retry
|
||||
|
@ -196,12 +193,13 @@ class TestWeb < MiniTest::Unit::TestCase
|
|||
'error_message' => 'Some fake message',
|
||||
'error_class' => 'RuntimeError',
|
||||
'retry_count' => 0,
|
||||
'failed_at' => Time.now.utc, }
|
||||
'failed_at' => Time.now.utc,
|
||||
'jid' => "f39af2a05e8f4b24dbc0f1e4"}
|
||||
score = Time.now.to_f
|
||||
Sidekiq.redis do |conn|
|
||||
conn.zadd('retry', score, Sidekiq.dump_json(msg))
|
||||
end
|
||||
[msg, score]
|
||||
msg
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -17,18 +17,18 @@ header.row
|
|||
th Queue
|
||||
th Worker
|
||||
th Args
|
||||
- @retries.each do |(msg, score)|
|
||||
- @retries.each do |msg, score|
|
||||
tr
|
||||
td
|
||||
input type='checkbox' name='score[]' value='#{score}'
|
||||
input type='checkbox' name='jid[]' value='#{msg['jid']}'
|
||||
td
|
||||
a href="#{root_path}retries/#{score}"== relative_time(Time.at(score))
|
||||
a href="#{root_path}retries/#{msg['jid']}"== relative_time(Time.at(score))
|
||||
td= msg['retry_count']
|
||||
td
|
||||
a href="#{root_path}queues/#{msg['queue']}" #{msg['queue']}
|
||||
td= msg['class']
|
||||
td= display_args(msg['args'])
|
||||
input.btn.btn-danger.btn-small.pull-right type="submit" name="delete" value="Delete"
|
||||
input.btn.btn-primary.btn-small.pull-right type="submit" name="retry" value="Retry Now"
|
||||
input.btn.btn-primary.btn-small.pull-right type="submit" name="retry" value="Retry Now"
|
||||
- else
|
||||
.alert.alert-success No retries were found
|
||||
|
|
|
@ -1,52 +1,55 @@
|
|||
header
|
||||
h3 Job
|
||||
|
||||
- @retries.each do |msg|
|
||||
table class="retry table table-bordered table-striped"
|
||||
tbody
|
||||
table class="retry table table-bordered table-striped"
|
||||
tbody
|
||||
tr
|
||||
th Queue
|
||||
td
|
||||
a href="#{root_path}queues/#{@retry['queue']}" #{@retry['queue']}
|
||||
tr
|
||||
th Job Class
|
||||
td
|
||||
code= @retry['class']
|
||||
tr
|
||||
th Job Arguments
|
||||
td
|
||||
code= display_args(@retry['args'], 1000)
|
||||
tr
|
||||
th Job ID
|
||||
td
|
||||
code= @retry.jid
|
||||
- if @retry['retry_count'] > 0
|
||||
tr
|
||||
th Queue
|
||||
td
|
||||
a href="#{root_path}queues/#{msg['queue']}" #{msg['queue']}
|
||||
th Retry Count
|
||||
td= @retry['retry_count']
|
||||
tr
|
||||
th Job Class
|
||||
td
|
||||
code= msg['class']
|
||||
th Last Retry
|
||||
td== relative_time(@retry['retried_at'].is_a?(Numeric) ? Time.at(@retry['retried_at']) : Time.parse(@retry['retried_at']))
|
||||
- else
|
||||
tr
|
||||
th Job Arguments
|
||||
td
|
||||
code= display_args(msg['args'], 1000)
|
||||
- if msg['retry_count'] > 0
|
||||
tr
|
||||
th Retry Count
|
||||
td= msg['retry_count']
|
||||
tr
|
||||
th Last Retry
|
||||
td== relative_time(msg['retried_at'].is_a?(Numeric) ? Time.at(msg['retried_at']) : Time.parse(msg['retried_at']))
|
||||
- else
|
||||
tr
|
||||
th Originally Failed
|
||||
td== relative_time(msg['failed_at'].is_a?(Numeric) ? Time.at(msg['failed_at']) : Time.parse(msg['failed_at']))
|
||||
tr
|
||||
th Next Retry
|
||||
td== relative_time(Time.at(@score))
|
||||
th Originally Failed
|
||||
td== relative_time(@retry['failed_at'].is_a?(Numeric) ? Time.at(@retry['failed_at']) : Time.parse(@retry['failed_at']))
|
||||
tr
|
||||
th Next Retry
|
||||
td== relative_time(Time.at(@retry.score))
|
||||
|
||||
h3 Error
|
||||
table class="error table table-bordered table-striped"
|
||||
tbody
|
||||
h3 Error
|
||||
table class="error table table-bordered table-striped"
|
||||
tbody
|
||||
tr
|
||||
th Error Class
|
||||
td
|
||||
code= @retry['error_class']
|
||||
tr
|
||||
th Error Message
|
||||
td= @retry['error_message']
|
||||
- if !@retry['error_backtrace'].nil?
|
||||
tr
|
||||
th Error Class
|
||||
th Error Backtrace
|
||||
td
|
||||
code= msg['error_class']
|
||||
tr
|
||||
th Error Message
|
||||
td= msg['error_message']
|
||||
- if !msg['error_backtrace'].nil?
|
||||
tr
|
||||
th Error Backtrace
|
||||
td
|
||||
code== msg['error_backtrace'].join("<br/>")
|
||||
form.form-horizontal action="#{root_path}retries/#{@score}" method="post"
|
||||
a.btn href="#{root_path}retries" ← Back
|
||||
input.btn.btn-primary type="submit" name="retry" value="Retry Now"
|
||||
input.btn.btn-danger type="submit" name="delete" value="Delete"
|
||||
code== @retry['error_backtrace'].join("<br/>")
|
||||
form.form-horizontal action="#{root_path}retries/#{@retry.jid}" method="post"
|
||||
a.btn href="#{root_path}retries" ← Back
|
||||
input.btn.btn-primary type="submit" name="retry" value="Retry Now"
|
||||
input.btn.btn-danger type="submit" name="delete" value="Delete"
|
||||
|
|
|
@ -16,10 +16,10 @@ header.row
|
|||
th width="10%" Queue
|
||||
th Worker
|
||||
th Args
|
||||
- @scheduled.each do |(msg, score)|
|
||||
- @scheduled.each do |msg, score|
|
||||
tr
|
||||
td
|
||||
input type='checkbox' name='score[]' value='#{score}'
|
||||
input type='checkbox' name='jid[]' value='#{msg['jid']}'
|
||||
td== relative_time(Time.at(score))
|
||||
td
|
||||
a href="#{root_path}queues/#{msg['queue']}" #{msg['queue']}
|
||||
|
|
Loading…
Reference in a new issue