Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clear and Retry jobs by job class and exception #144

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
25 changes: 22 additions & 3 deletions app/controllers/resque_web/failures_controller.rb
Expand Up @@ -18,7 +18,11 @@ def destroy
# destroy all jobs from the failure queue
def destroy_all
queue = params[:queue] || 'failed'
Resque::Failure.clear(queue)

(Resque::Failure.count - 1).downto(0).each do |i|
Resque::Failure.remove(i) if must_execute_action_for(i)
end

redirect_to failures_path(redirect_params)
end

Expand All @@ -30,11 +34,14 @@ def retry

# retry all jobs from the failure queue
def retry_all
if params[:queue].present? && params[:queue]!="failed"
if params[:queue].present? && params[:queue] != 'failed'
Resque::Failure.requeue_queue(params[:queue])
else
(Resque::Failure.count-1).downto(0).each { |id| reque_single_job(id) }
(Resque::Failure.count - 1).downto(0).each do |i|
reque_single_job(i) if must_execute_action_for(i)
end
end

redirect_to failures_path(redirect_params)
end

Expand All @@ -58,5 +65,17 @@ def redirect_params
end
end

def must_execute_action_for(i)
(params[:job_class].blank? || params[:job_class] == job_class_for(i)) &&
(params[:job_exception].blank? || params[:job_exception] == job_exception_for(i))
end

def job_class_for(i)
Resque::Failure.all(i)['payload']['args'].first['job_class']
end

def job_exception_for(i)
Resque::Failure.all(i)['exception']
end
end
end
26 changes: 20 additions & 6 deletions app/views/resque_web/failures/index.html.erb
Expand Up @@ -5,15 +5,29 @@
<% end %>

<% unless failure_size.zero? %>
<%= form_tag(destroy_all_failures_path(queue: params[:queue]), method: :delete) do %>
<%= submit_tag "Clear #{failure_queue_name} Jobs", class: 'btn btn-danger', data: { confirm: "Are you sure you want to clear ALL #{failure_queue_name.downcase} jobs?" } %>
<div class='row'>
<%= form_tag(destroy_all_failures_path(queue: params[:queue]), class: 'form-inline', method: :delete) do |form| %>
<%= text_field form, :job_class, class: 'form-control', placeholder: 'Arguments Job Class' %>
<%= text_field form, :job_exception, class: 'form-control', placeholder: 'Job Exception' %>
<%= submit_tag "Clear #{failure_queue_name} Jobs", class: 'btn btn-danger', data: { confirm: "Are you sure you want to clear the #{failure_queue_name.downcase} jobs?" } %>
<% end %>
</div>

<br>

<div class='row'>
<%= form_tag(retry_all_failures_path(queue: params[:queue]), class: 'form-inline', method: :put) do |form| %>
<%= text_field form, :job_class, class: 'form-control', placeholder: 'Arguments Job Class' %>
<%= text_field form, :job_exception, class: 'form-control', placeholder: 'Job Exception' %>
<%= submit_tag "Retry #{failure_queue_name} Jobs", class: 'btn btn-primary', data: { confirm: "Are you sure you want to retry the #{failure_queue_name.downcase} jobs?" } %>
<% end %>
</div>

<div class='row pull-right'>
<% if failure_size > failure_per_page %>
<%= link_to "Last page &raquo;".html_safe, { start: (failure_size - failure_per_page) }, class: 'btn' %>
<% end %>
<% end %>
<%= form_tag(retry_all_failures_path(queue: params[:queue]), method: :put) do %>
<%= submit_tag "Retry #{failure_queue_name} Jobs", class: 'btn', data: { confirm: "Are you sure you want to retry ALL #{failure_queue_name.downcase} jobs?" } %>
<% end %>
</div>
<% end %>

<% if multiple_failure_queues? && !params[:queue] %>
Expand Down
2 changes: 1 addition & 1 deletion app/views/resque_web/failures/show.html.erb
@@ -1,4 +1,4 @@
<h1>Failed Jobs <%= "on '#{params[:id]}'" if params[:id] %> <%= "with class '#{params[:class]}'" if params[:class] %></h1>
<h1>Failed jobs <%= "on '#{params[:id]}'" if params[:id] %> <%= "with class '#{params[:class]}'" if params[:class] %></h1>

<% if @jobs.any? %>
<%= form_tag("/failures/#{params[:id] if params[:id]}", :method => :delete) do %>
Expand Down
88 changes: 87 additions & 1 deletion test/functional/failures_controller_test.rb
Expand Up @@ -25,10 +25,52 @@ class FailuresControllerTest < ActionController::TestCase

describe "DELETE /failures/destroy_all" do
it "deletes all failures" do
Resque::Failure.expects(:clear).with('failed')
Resque::Failure.stubs(:count).returns(2)
Resque::Failure.expects(:remove).with(0)
Resque::Failure.expects(:remove).with(1)
visit(:destroy_all, {}, :method => :delete)
assert_redirected_to failures_path
end

it 'deletes all failures with the job class specified' do
Resque::Failure.stubs(:count).returns(2)
Resque::Failure.stubs(:all).with(0).returns(
{
'payload' => { 'args' => ['job_class' => 'JobClass1'] },
'exception' => 'Exception'
}
)
Resque::Failure.stubs(:all).with(1).returns(
{
'payload' => { 'args' => ['job_class' => 'JobClass2'] },
'exception' => 'Exception'
}
)
Resque::Failure.expects(:remove).with(0)
Resque::Failure.expects(:remove).with(1).never
visit(:destroy_all, { job_class: 'JobClass1' }, :method => :delete)
assert_redirected_to failures_path
end

it 'deletes all failures with the job exception specified' do
Resque::Failure.stubs(:count).returns(2)
Resque::Failure.stubs(:all).with(0).returns(
{
'payload' => { 'args' => ['job_class' => 'JobClass1'] },
'exception' => 'Exception1'
}
)
Resque::Failure.stubs(:all).with(1).returns(
{
'payload' => { 'args' => ['job_class' => 'JobClass2'] },
'exception' => 'Exception2'
}
)
Resque::Failure.expects(:remove).with(0).never
Resque::Failure.expects(:remove).with(1)
visit(:destroy_all, { job_exception: 'Exception2' }, :method => :delete)
assert_redirected_to failures_path
end
end

describe "PUT /failures/:id/retry" do
Expand Down Expand Up @@ -69,6 +111,50 @@ class FailuresControllerTest < ActionController::TestCase
visit(:retry_all, {:queue=>"myqueue"}, :method => :put)
assert_redirected_to failures_path(:queue=>'myqueue')
end

it 'retries all failures with the job class specified' do
Resque::Failure.stubs(:count).returns(2)
Resque::Failure.stubs(:all).with(0).returns(
{
'payload' => { 'args' => ['job_class' => 'JobClass1'] },
'exception' => 'Exception'
}
)
Resque::Failure.stubs(:all).with(1).returns(
{
'payload' => { 'args' => ['job_class' => 'JobClass2'] },
'exception' => 'Exception'
}
)
Resque::Failure.expects(:requeue).with(0)
Resque::Failure.expects(:remove).with(0)
Resque::Failure.expects(:requeue).with(1).never
Resque::Failure.expects(:remove).with(1).never
visit(:retry_all, { job_class: 'JobClass1' }, :method => :put)
assert_redirected_to failures_path
end

it 'retries all failures with the job exception specified' do
Resque::Failure.stubs(:count).returns(2)
Resque::Failure.stubs(:all).with(0).returns(
{
'payload' => { 'args' => ['job_class' => 'JobClass1'] },
'exception' => 'Exception1'
}
)
Resque::Failure.stubs(:all).with(1).returns(
{
'payload' => { 'args' => ['job_class' => 'JobClass2'] },
'exception' => 'Exception2'
}
)
Resque::Failure.expects(:requeue).with(0).never
Resque::Failure.expects(:remove).with(0).never
Resque::Failure.expects(:requeue).with(1)
Resque::Failure.expects(:remove).with(1)
visit(:retry_all, { job_exception: 'Exception2' }, :method => :put)
assert_redirected_to failures_path
end
end
end
end