resque-insist 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +4 -0
- data/Gemfile.lock +42 -0
- data/README.md +48 -0
- data/Rakefile +2 -0
- data/lib/resque/plugins/insist.rb +77 -0
- data/resque-insist.gemspec +21 -0
- data/spec/redis-test.conf +30 -0
- data/spec/resque_insist_spec.rb +79 -0
- data/spec/spec_helper.rb +25 -0
- metadata +109 -0
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
resque-insist (0.1.0)
|
5
|
+
resque (~> 1.10.0)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: http://rubygems.org/
|
9
|
+
specs:
|
10
|
+
diff-lcs (1.1.2)
|
11
|
+
json (1.4.6)
|
12
|
+
rack (1.2.1)
|
13
|
+
redis (2.1.1)
|
14
|
+
redis-namespace (0.8.0)
|
15
|
+
redis (< 3.0.0)
|
16
|
+
resque (1.10.0)
|
17
|
+
json (~> 1.4.6)
|
18
|
+
redis-namespace (~> 0.8.0)
|
19
|
+
sinatra (>= 0.9.2)
|
20
|
+
vegas (~> 0.1.2)
|
21
|
+
rspec (2.2.0)
|
22
|
+
rspec-core (~> 2.2)
|
23
|
+
rspec-expectations (~> 2.2)
|
24
|
+
rspec-mocks (~> 2.2)
|
25
|
+
rspec-core (2.2.1)
|
26
|
+
rspec-expectations (2.2.0)
|
27
|
+
diff-lcs (~> 1.1.2)
|
28
|
+
rspec-mocks (2.2.0)
|
29
|
+
sinatra (1.1.0)
|
30
|
+
rack (~> 1.1)
|
31
|
+
tilt (~> 1.1)
|
32
|
+
tilt (1.1)
|
33
|
+
vegas (0.1.8)
|
34
|
+
rack (>= 1.0.0)
|
35
|
+
|
36
|
+
PLATFORMS
|
37
|
+
ruby
|
38
|
+
|
39
|
+
DEPENDENCIES
|
40
|
+
resque (~> 1.10.0)
|
41
|
+
resque-insist!
|
42
|
+
rspec (~> 2.2.0)
|
data/README.md
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# Resque::Plugins::Insist
|
2
|
+
## Give your Resque jobs a second chance
|
3
|
+
|
4
|
+
<http://github.com/jrom/resque-insist> by Jordi Romero
|
5
|
+
|
6
|
+
If you want to give your jobs a second chance (or more)
|
7
|
+
extend them with this plugin and let the job fail
|
8
|
+
some times before considering it failed.
|
9
|
+
|
10
|
+
By default a job will be run 3 times before marking it
|
11
|
+
as failed, but you can configure that with @insist = N
|
12
|
+
in your job.
|
13
|
+
|
14
|
+
## Usage
|
15
|
+
|
16
|
+
require 'resque/plugins/insist'
|
17
|
+
class HardJob
|
18
|
+
extend Resque::Plugins::Insist
|
19
|
+
@queue = :hard_job
|
20
|
+
@insist = 5
|
21
|
+
def self.perform(something)
|
22
|
+
do_work
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
When your job fails for the first time, it will be queued
|
27
|
+
again, but will be locked for 8 seconds before the worker
|
28
|
+
performs it again. If it fails again, the wait time will be
|
29
|
+
16 seconds, then 32, 64, ... until your job reaches the
|
30
|
+
maximum number of attempts or just succeeds (not raising an
|
31
|
+
exception).
|
32
|
+
|
33
|
+
## Contributing
|
34
|
+
|
35
|
+
If you want to improve resque-insist
|
36
|
+
|
37
|
+
1. Fork the repo
|
38
|
+
2. Create a topic branch `git checkout -b my_feature`
|
39
|
+
3. Push it! `git push origin my_feature`
|
40
|
+
4. Open a pull request
|
41
|
+
|
42
|
+
Make sure you add specs for your changes and document them.
|
43
|
+
Any contribution will be appreciated, both fixing some typo or
|
44
|
+
adding the coolest feature ever.
|
45
|
+
|
46
|
+
## Issues
|
47
|
+
|
48
|
+
<http://github.com/jrom/resque-insist/issues>
|
data/Rakefile
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
module Resque # :nodoc:
|
2
|
+
module Plugins # :nodoc:
|
3
|
+
# If you want to give your jobs a second chance (or more)
|
4
|
+
# extend them with this plugin and let the job fail
|
5
|
+
# some times before considering it failed.
|
6
|
+
#
|
7
|
+
# By default a job will be run 3 times before marking it
|
8
|
+
# as failed, but you can configure that with @insist = N
|
9
|
+
# in your job.
|
10
|
+
#
|
11
|
+
# Example:
|
12
|
+
#
|
13
|
+
# require 'resque/plugins/insist'
|
14
|
+
# class HardJob
|
15
|
+
# extend Resque::Plugins::Insist
|
16
|
+
# @queue = :hard_job
|
17
|
+
# @insist = 5
|
18
|
+
# def self.perform(something)
|
19
|
+
# do_work
|
20
|
+
# end
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# When your job fails for the first time, it will be queued
|
24
|
+
# again, but will be locked for 8 seconds before the worker
|
25
|
+
# performs it again. If it fails again, the wait time will be
|
26
|
+
# 16 seconds, then 32, 64, ... until your job reaches the
|
27
|
+
# maximum number of attempts or just succeeds (not raising an
|
28
|
+
# exception).
|
29
|
+
module Insist
|
30
|
+
include Resque::Helpers
|
31
|
+
# Intercept the execution of a job to add the extra security
|
32
|
+
# layer.
|
33
|
+
def around_perform_insist(*args)
|
34
|
+
if redis.get "plugin:insist:wait:#{insist_key(args)}"
|
35
|
+
Resque.enqueue constantize(self.to_s), *args
|
36
|
+
else
|
37
|
+
begin
|
38
|
+
yield
|
39
|
+
redis.del "plugin:insist:attempts:#{insist_key(args)}"
|
40
|
+
rescue => e
|
41
|
+
attempts = redis.incr "plugin:insist:attempts:#{insist_key(args)}"
|
42
|
+
if attempts.to_i >= insist_times
|
43
|
+
redis.del "plugin:insist:wait:#{insist_key(args)}"
|
44
|
+
redis.del "plugin:insist:attempts:#{insist_key(args)}"
|
45
|
+
raise e
|
46
|
+
else
|
47
|
+
redis.set "plugin:insist:wait:#{insist_key(args)}", 1
|
48
|
+
redis.expire "plugin:insist:wait:#{insist_key(args)}", wait_time(attempts)
|
49
|
+
Resque.enqueue constantize(self.to_s), *args
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Number of times a job will be executed before considering
|
56
|
+
# it failed. By default it's 3. To specify it, set the ivar
|
57
|
+
# @insist to an integer. Any value under 1 will make the job
|
58
|
+
# perform just once as if the plugin was never there.
|
59
|
+
def insist_times
|
60
|
+
@insist || 3
|
61
|
+
end
|
62
|
+
|
63
|
+
# Calculates the amount of seconds a job is locked between
|
64
|
+
# the last failure and the next attempt.
|
65
|
+
def wait_time(attempts)
|
66
|
+
2 ** (attempts.to_i + 2)
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
# Calculates a key to identify the job according to
|
71
|
+
# its arguments.
|
72
|
+
def insist_key(*args)
|
73
|
+
self.to_s + ':' + Digest::SHA1.hexdigest(args.join(','))
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
$:.push File.expand_path("../lib", __FILE__)
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = "resque-insist"
|
5
|
+
s.version = "0.1.0"
|
6
|
+
s.platform = Gem::Platform::RUBY
|
7
|
+
s.authors = ["Jordi Romero"]
|
8
|
+
s.email = ["jordi@jrom.net"]
|
9
|
+
s.homepage = "http://github.com/jrom/resque-insist"
|
10
|
+
s.summary = %q{Give your Resque jobs a second chance}
|
11
|
+
s.description = %q{If you want to give your jobs a second change (or more) extend them with this plugin and let the job fail some times before considering it failed.}
|
12
|
+
|
13
|
+
s.files = `git ls-files`.split("\n") - %w(.gitignore .rspec) - ['autotest/discover.rb']
|
14
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
15
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
16
|
+
s.require_paths = ["lib"]
|
17
|
+
|
18
|
+
s.add_dependency "resque", "~> 1.10.0"
|
19
|
+
s.add_development_dependency "rspec", "~> 2.2.0"
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
daemonize yes
|
2
|
+
pidfile ./spec/redis.pid
|
3
|
+
port 6378
|
4
|
+
|
5
|
+
timeout 300
|
6
|
+
loglevel debug
|
7
|
+
logfile stdout
|
8
|
+
databases 16
|
9
|
+
|
10
|
+
save 900 1
|
11
|
+
save 300 10
|
12
|
+
save 60 10000
|
13
|
+
rdbcompression yes
|
14
|
+
|
15
|
+
dbfilename dump.rdb
|
16
|
+
dir ./spec/
|
17
|
+
|
18
|
+
appendonly no
|
19
|
+
appendfsync everysec
|
20
|
+
|
21
|
+
vm-enabled no
|
22
|
+
vm-swap-file /tmp/redis.swap
|
23
|
+
vm-max-memory 0
|
24
|
+
vm-page-size 32
|
25
|
+
vm-pages 134217728
|
26
|
+
vm-max-threads 4
|
27
|
+
glueoutputbuf yes
|
28
|
+
hash-max-zipmap-entries 64
|
29
|
+
hash-max-zipmap-value 512
|
30
|
+
activerehashing yes
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
class Job
|
4
|
+
extend Resque::Plugins::Insist
|
5
|
+
@queue = :job
|
6
|
+
@insist = 2
|
7
|
+
def self.perform(success)
|
8
|
+
raise 'Not gonna work' unless success
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe Resque::Plugins::Insist do
|
13
|
+
describe "compliance of Resque Plugins guidelines" do
|
14
|
+
it "should be valid" do
|
15
|
+
lambda{ Resque::Plugin.lint(Resque::Plugins::Insist) }.should_not raise_error
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "config" do
|
20
|
+
it "should take the insist_times value from the @insist ivar" do
|
21
|
+
Job.insist_times.should == 2
|
22
|
+
end
|
23
|
+
|
24
|
+
it "shoult default insist_times to 3" do
|
25
|
+
class JobWithoutCustomInsist
|
26
|
+
extend Resque::Plugins::Insist
|
27
|
+
@queue = :job
|
28
|
+
def self.perform; end
|
29
|
+
end
|
30
|
+
JobWithoutCustomInsist.insist_times.should == 3
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "a succeeding Job" do
|
35
|
+
it "should be executed on the first attempt and not be enqueued again" do
|
36
|
+
Resque.enqueue Job, true
|
37
|
+
pending.should == 1
|
38
|
+
processed.should == 0
|
39
|
+
failed.should == 0
|
40
|
+
work_jobs
|
41
|
+
pending.should == 0
|
42
|
+
processed.should == 1
|
43
|
+
failed.should == 0
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "a failing Job" do
|
48
|
+
it "should should quietly die and be enqueued back the first time it's executed" do
|
49
|
+
Resque.enqueue Job, false # enqueue a failing job
|
50
|
+
work_job # try to work it for the first time
|
51
|
+
pending.should == 1
|
52
|
+
failed.should == 0
|
53
|
+
time_before = Time.now
|
54
|
+
work_jobs # try to work it for the second and last time
|
55
|
+
elapsed_time = Time.now - time_before
|
56
|
+
elapsed_time.should > 7 # We wait aprox 8 seconds
|
57
|
+
elapsed_time.should < 9
|
58
|
+
pending.should == 0
|
59
|
+
failed.should == 1 # the job is marked as failed after 2 attempts
|
60
|
+
Resque::Failure.all['error'].should == 'Not gonna work' # we get the original error
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Defines pending, failed, processed, queues, environment, workers and servers
|
65
|
+
Resque.info.keys.each do |key|
|
66
|
+
define_method(key) { Resque.info[key] }
|
67
|
+
end
|
68
|
+
|
69
|
+
# Starts a worker to work off queue
|
70
|
+
def work_jobs(queue = :job)
|
71
|
+
Resque::Worker.new(queue).work(0)
|
72
|
+
end
|
73
|
+
|
74
|
+
# Performs the first job available from queue
|
75
|
+
def work_job(queue = :job)
|
76
|
+
worker = Resque::Worker.new(queue)
|
77
|
+
worker.perform(worker.reserve)
|
78
|
+
end
|
79
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
Bundler.require(:default, :development)
|
2
|
+
require 'lib/resque/plugins/insist'
|
3
|
+
|
4
|
+
RSpec.configure do |config|
|
5
|
+
config.before(:all) do
|
6
|
+
if !system('which redis-server')
|
7
|
+
puts "\ncan't find `redis-server` in your path"
|
8
|
+
abort
|
9
|
+
end
|
10
|
+
`redis-server #{File.dirname(File.expand_path(__FILE__))}/redis-test.conf` # run Redis with local config
|
11
|
+
puts "Starting test redis server: redis-server #{File.dirname(File.expand_path(__FILE__))}/redis-test.conf"
|
12
|
+
Resque.redis = 'localhost:6378' # port specified in redis-test.conf
|
13
|
+
end
|
14
|
+
|
15
|
+
config.before(:each) do
|
16
|
+
Resque.redis.flushall # Drop all keys between examples
|
17
|
+
end
|
18
|
+
|
19
|
+
config.after(:all) do
|
20
|
+
pid = `ps -e -o pid,command | grep [r]edis-test`.split(" ")[0]
|
21
|
+
puts "\nKilling test redis server PID #{pid}..."
|
22
|
+
`rm -f #{File.dirname(File.expand_path(__FILE__))}/dump.rdb` # file specified in redis-test.conf
|
23
|
+
Process.kill("KILL", pid.to_i)
|
24
|
+
end
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: resque-insist
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Jordi Romero
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-12-19 00:00:00 +01:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: resque
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 63
|
30
|
+
segments:
|
31
|
+
- 1
|
32
|
+
- 10
|
33
|
+
- 0
|
34
|
+
version: 1.10.0
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: rspec
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 7
|
46
|
+
segments:
|
47
|
+
- 2
|
48
|
+
- 2
|
49
|
+
- 0
|
50
|
+
version: 2.2.0
|
51
|
+
type: :development
|
52
|
+
version_requirements: *id002
|
53
|
+
description: If you want to give your jobs a second change (or more) extend them with this plugin and let the job fail some times before considering it failed.
|
54
|
+
email:
|
55
|
+
- jordi@jrom.net
|
56
|
+
executables: []
|
57
|
+
|
58
|
+
extensions: []
|
59
|
+
|
60
|
+
extra_rdoc_files: []
|
61
|
+
|
62
|
+
files:
|
63
|
+
- Gemfile
|
64
|
+
- Gemfile.lock
|
65
|
+
- README.md
|
66
|
+
- Rakefile
|
67
|
+
- lib/resque/plugins/insist.rb
|
68
|
+
- resque-insist.gemspec
|
69
|
+
- spec/redis-test.conf
|
70
|
+
- spec/resque_insist_spec.rb
|
71
|
+
- spec/spec_helper.rb
|
72
|
+
has_rdoc: true
|
73
|
+
homepage: http://github.com/jrom/resque-insist
|
74
|
+
licenses: []
|
75
|
+
|
76
|
+
post_install_message:
|
77
|
+
rdoc_options: []
|
78
|
+
|
79
|
+
require_paths:
|
80
|
+
- lib
|
81
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
82
|
+
none: false
|
83
|
+
requirements:
|
84
|
+
- - ">="
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
hash: 3
|
87
|
+
segments:
|
88
|
+
- 0
|
89
|
+
version: "0"
|
90
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
|
+
none: false
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
hash: 3
|
96
|
+
segments:
|
97
|
+
- 0
|
98
|
+
version: "0"
|
99
|
+
requirements: []
|
100
|
+
|
101
|
+
rubyforge_project:
|
102
|
+
rubygems_version: 1.3.7
|
103
|
+
signing_key:
|
104
|
+
specification_version: 3
|
105
|
+
summary: Give your Resque jobs a second chance
|
106
|
+
test_files:
|
107
|
+
- spec/redis-test.conf
|
108
|
+
- spec/resque_insist_spec.rb
|
109
|
+
- spec/spec_helper.rb
|