Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit bdc4c46
Showing
5 changed files
with
306 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
.DS_Store |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
Copyright (c) 2010 Tatsuya Ono | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining | ||
a copy of this software and associated documentation files (the | ||
"Software"), to deal in the Software without restriction, including | ||
without limitation the rights to use, copy, modify, merge, publish, | ||
distribute, sublicense, and/or sell copies of the Software, and to | ||
permit persons to whom the Software is furnished to do so, subject to | ||
the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be | ||
included in all copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | ||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
module Resque | ||
module Plugins | ||
# ResqueCleaner class provides useful functionalities to retry or clean | ||
# failed jobs. | ||
class ResqueCleaner | ||
# ResqueCleaner fetches all elements from Redis once and checks them | ||
# by linear when filtering. Since there is a performance concern, | ||
# ResqueCleaner only targets the latest x(default 1000) jobs. | ||
# You can change the value. e.g. cleaner.limiter.maximum = 2000 | ||
attr_reader :limiter | ||
|
||
# Set false if you don't show any message. | ||
attr_accessor :print_message | ||
|
||
# Initializes instance | ||
def initialize | ||
@failure = Resque::Failure.backend | ||
@print_message = true | ||
@limiter = Limiter.new | ||
end | ||
|
||
# Returns redis instance. | ||
def redis | ||
Resque.redis | ||
end | ||
|
||
# Returns failure backend. Only supports redis backend. | ||
def failure | ||
@failure | ||
end | ||
|
||
# Outputs summary of failure jobs by date. | ||
def summary_by_date | ||
jobs = @limiter.jobs | ||
summary = {} | ||
jobs.each do |job| | ||
date = job["failed_at"][0,10] | ||
summary[date] ||= 0 | ||
summary[date] += 1 | ||
end if jobs | ||
|
||
if print? | ||
log too_many_message if @limiter.on? | ||
summary.keys.sort.each do |k| | ||
log "%s: %4d" % [k,summary[k]] | ||
end | ||
log "%10s: %4d" % ["total", @limiter.count] | ||
end | ||
summary | ||
end | ||
|
||
def summary_by_class | ||
end | ||
|
||
# Returns every jobs for which block evaluates to true. | ||
def select(&block) | ||
jobs = @limiter.jobs | ||
@limiter.jobs.select &block if jobs | ||
end | ||
|
||
# Clears every jobs for which block evaluates to true. | ||
def clear(&block) | ||
|
||
end | ||
|
||
# Retries every jobs for which block evaluates to true. | ||
def retry(&block) | ||
end | ||
|
||
# Retries and clears every jobs for which block evaluates to true. | ||
def retry_and_clear(&block) | ||
end | ||
|
||
# Clears all jobs except the last x jobs | ||
def clear_stale | ||
return unless @limiter.on? | ||
redis.ltrim(:failed, -@limiter.maximum, -1) | ||
end | ||
|
||
# Returns | ||
def proc(&block) | ||
FilterProc.new(&block) | ||
end | ||
|
||
# Provides typical proc you can filter jobs. | ||
class FilterProc < Proc | ||
def retried | ||
FilterProc.new {|job| self.call(job) && job['retried_at'].blank?} | ||
end | ||
|
||
def before(time) | ||
time = Time.parse(time) if time.is_a?(String) | ||
FilterProc.new {|job| self.call(job) && Time.parse(job['failed_at']) <= time} | ||
end | ||
|
||
def after(time) | ||
time = Time.parse(time) if time.is_a?(String) | ||
FilterProc.new {|job| self.call(job) && Time.parse(job['failed_at']) >= time} | ||
end | ||
|
||
def klass(klass_or_name) | ||
FilterProc.new {|job| self.call(job) && job["payload"]["class"] == klass_or_name.to_s} | ||
end | ||
|
||
def queue(queue) | ||
FilterProc.new {|job| self.call(job) && job["queue"] == queue.to_s} | ||
end | ||
|
||
def self.new(&block) | ||
if block | ||
super | ||
else | ||
super {|job| true} | ||
end | ||
end | ||
end | ||
|
||
# Through the Limiter class, you accesses only the last x(default 1000) | ||
# jobs. | ||
class Limiter | ||
DEFAULT_MAX_JOBS = 1000 | ||
attr_accessor :maximum | ||
def initialize(cleaner) | ||
@cleaner = cleaner | ||
@maximum = DEFAULT_MAX_JOBS | ||
end | ||
|
||
# Returns true if limiter is ON: number of failed jobs is more than | ||
# maximum value. | ||
def on? | ||
@cleaner.failure.count > @maximum | ||
end | ||
|
||
def count | ||
on? ? @maximum : @cleaner.failure.count | ||
end | ||
|
||
def jobs | ||
jobs = @cleaner.failure.all( - count, count) | ||
jobs.is_a?(Array) ? jobs : [jobs] if jobs | ||
end | ||
end | ||
|
||
# Outputs message. Overrides this method when you want to change a output | ||
# stream. | ||
def log(msg) | ||
puts msg if print? | ||
end | ||
|
||
def print? | ||
@print_message | ||
end | ||
|
||
def too_many_message | ||
"There are too many failed jobs(count=#{@failure.count}). This only looks into last #{@limiter.maximum} jobs." | ||
end | ||
end | ||
end | ||
end | ||
|
||
require 'pp' | ||
|
||
h = ResqueFailureHelper.new | ||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
# Mostly copied from Resque in order to have similar test environment. | ||
# https://github.com/defunkt/resque/blob/master/test/test_helper.rb | ||
|
||
dir = File.dirname(File.expand_path(__FILE__)) | ||
$LOAD_PATH.unshift dir + '/../lib' | ||
$TESTING = true | ||
require 'test/unit' | ||
require 'rubygems' | ||
require 'resque' | ||
|
||
begin | ||
require 'leftright' | ||
rescue LoadError | ||
end | ||
|
||
|
||
# | ||
# make sure we can run redis | ||
# | ||
|
||
if !system("which redis-server") | ||
puts '', "** can't find `redis-server` in your path" | ||
puts "** try running `sudo rake install`" | ||
abort '' | ||
end | ||
|
||
|
||
# | ||
# start our own redis when the tests start, | ||
# kill it when they end | ||
# | ||
|
||
at_exit do | ||
next if $! | ||
|
||
if defined?(MiniTest) | ||
exit_code = MiniTest::Unit.new.run(ARGV) | ||
else | ||
exit_code = Test::Unit::AutoRunner.run | ||
end | ||
|
||
pid = `ps -A -o pid,command | grep [r]edis-test`.split(" ")[0] | ||
puts "Killing test redis server..." | ||
`rm -f #{dir}/dump.rdb` | ||
Process.kill("KILL", pid.to_i) | ||
exit exit_code | ||
end | ||
|
||
puts "Starting redis for testing at localhost:9736..." | ||
`redis-server #{dir}/redis-test.conf` | ||
Resque.redis = 'localhost:9736' | ||
|
||
|
||
## | ||
# test/spec/mini 3 | ||
# http://gist.github.com/25455 | ||
# chris@ozmm.org | ||
# | ||
def context(*args, &block) | ||
return super unless (name = args.first) && block | ||
require 'test/unit' | ||
klass = Class.new(defined?(ActiveSupport::TestCase) ? ActiveSupport::TestCase : Test::Unit::TestCase) do | ||
def self.test(name, &block) | ||
define_method("test_#{name.gsub(/\W/,'_')}", &block) if block | ||
end | ||
def self.xtest(*args) end | ||
def self.setup(&block) define_method(:setup, &block) end | ||
def self.teardown(&block) define_method(:teardown, &block) end | ||
end | ||
(class << klass; self end).send(:define_method, :name) { name.gsub(/\W/,'_') } | ||
klass.class_eval &block | ||
end | ||
|
||
## | ||
# Helper to perform job classes | ||
# | ||
module PerformJob | ||
def perform_job(klass, *args) | ||
resque_job = Resque::Job.new(:testqueue, 'class' => klass, 'args' => args) | ||
resque_job.perform | ||
end | ||
end | ||
|
||
# | ||
# fixture classes | ||
# | ||
|
||
class SomeJob | ||
def self.perform(repo_id, path) | ||
end | ||
end | ||
|
||
class SomeIvarJob < SomeJob | ||
@queue = :ivar | ||
end | ||
|
||
class SomeMethodJob < SomeJob | ||
def self.queue | ||
:method | ||
end | ||
end | ||
|
||
class BadJob | ||
def self.perform | ||
raise "Bad job!" | ||
end | ||
end | ||
|
||
class GoodJob | ||
def self.perform(name) | ||
"Good job, #{name}" | ||
end | ||
end | ||
|
||
class BadJobWithSyntaxError | ||
def self.perform | ||
raise SyntaxError, "Extra Bad job!" | ||
end | ||
end |