resque-insist 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|