quebert 2.0.3 → 2.0.4
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.
- checksums.yaml +4 -4
- data/.gitignore +4 -1
- data/.rbenv-version +1 -1
- data/README.md +20 -0
- data/lib/quebert/configuration.rb +49 -2
- data/lib/quebert/controller/beanstalk.rb +62 -69
- data/lib/quebert/job.rb +9 -1
- data/lib/quebert/version.rb +1 -1
- data/spec/job_spec.rb +31 -0
- metadata +14 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 13a07e8336155e0c777ed1295201b2d7d23e9bee
|
4
|
+
data.tar.gz: 6e3929be1a44ee006809f3b24a1ef605aed7605d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f56a3426063aa8353e6e52f8c61bb7a76e73bdbced8df6a919872979fb33102ce0e9cbd536046ccc30fa21d1e57b38b994d275abcfd560366d6b451cf7507257
|
7
|
+
data.tar.gz: 2f464763bb13a6eefd45ebc46f8a2dbc3a326dff7e5968960319f37a52bcf10c3d5d5c3abf64a55d6ca009a30b12d705b65367f47d0e0ab9572969aeb5651427
|
data/.gitignore
CHANGED
data/.rbenv-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
2.1.2
|
data/README.md
CHANGED
@@ -18,6 +18,7 @@ Quebert is a serious project. Its used in a production environment at Poll Every
|
|
18
18
|
* Rails/ActiveRecord integration similar to async_observer
|
19
19
|
* Pluggable exception handling (for Hoptoad integration)
|
20
20
|
* Run workers with pid, log, and config files. These do not daemonize (do it yourself punk!)
|
21
|
+
* Provide custom hooks to be called before, after & around jobs are run
|
21
22
|
|
22
23
|
Some features that are currently missing that I will soon add include:
|
23
24
|
|
@@ -53,6 +54,25 @@ Then perform the jobs!
|
|
53
54
|
Quebert.backend.reserve.perform # => 6
|
54
55
|
Quebert.backend.reserve.perform # => 15
|
55
56
|
|
57
|
+
## Before/After/Around Hooks
|
58
|
+
|
59
|
+
Quebert has support for providing custom hooks to be called before, after & around your jobs are being run.
|
60
|
+
A common example is making sure that any active ActiveRecord database connections are put back on the connection pool after a job is done:
|
61
|
+
|
62
|
+
Quebert.config.after_job do
|
63
|
+
ActiveRecord::Base.clear_active_connections!
|
64
|
+
end
|
65
|
+
|
66
|
+
Quebert.config.before_job do |job|
|
67
|
+
# all hooks take an optional job argument
|
68
|
+
# in case you want to do something with that job
|
69
|
+
end
|
70
|
+
|
71
|
+
Quebert.config.around_job do |job|
|
72
|
+
# this hook gets called twice
|
73
|
+
# once before & once after a job is performed
|
74
|
+
end
|
75
|
+
|
56
76
|
## Async Sender
|
57
77
|
|
58
78
|
Take any ol' class and include the Quebert::AsyncSender.
|
@@ -29,13 +29,60 @@ module Quebert
|
|
29
29
|
end
|
30
30
|
self
|
31
31
|
end
|
32
|
-
|
32
|
+
|
33
33
|
def worker
|
34
34
|
@worker ||= Struct.new(:exception_handler).new
|
35
35
|
end
|
36
|
-
|
36
|
+
|
37
37
|
def self.from_hash(hash)
|
38
38
|
new.from_hash(hash) # Config this puppy up from a config hash
|
39
39
|
end
|
40
|
+
|
41
|
+
def before_job(job = nil, &block)
|
42
|
+
if job
|
43
|
+
before_hooks.each do |h|
|
44
|
+
h.call(job)
|
45
|
+
end
|
46
|
+
else
|
47
|
+
before_hooks << block if block
|
48
|
+
end
|
49
|
+
self
|
50
|
+
end
|
51
|
+
|
52
|
+
def after_job(job = nil, &block)
|
53
|
+
if job
|
54
|
+
after_hooks.each do |h|
|
55
|
+
h.call(job)
|
56
|
+
end
|
57
|
+
else
|
58
|
+
after_hooks << block if block
|
59
|
+
end
|
60
|
+
self
|
61
|
+
end
|
62
|
+
|
63
|
+
def around_job(job = nil, &block)
|
64
|
+
if job
|
65
|
+
around_hooks.each do |h|
|
66
|
+
h.call(job)
|
67
|
+
end
|
68
|
+
else
|
69
|
+
around_hooks << block if block
|
70
|
+
end
|
71
|
+
self
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def before_hooks
|
77
|
+
@before_hooks ||= []
|
78
|
+
end
|
79
|
+
|
80
|
+
def after_hooks
|
81
|
+
@after_hooks ||= []
|
82
|
+
end
|
83
|
+
|
84
|
+
def around_hooks
|
85
|
+
@around_hooks ||= []
|
86
|
+
end
|
40
87
|
end
|
41
88
|
end
|
@@ -14,86 +14,79 @@ module Quebert
|
|
14
14
|
|
15
15
|
def initialize(beanstalk_job, queue)
|
16
16
|
@beanstalk_job, @queue = beanstalk_job, queue
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
log "Exception caught on initialization. #{e.inspect}", :error
|
32
|
-
raise e
|
33
|
-
end
|
17
|
+
@job = Job.from_json(beanstalk_job.body)
|
18
|
+
rescue Job::Delete
|
19
|
+
beanstalk_job.delete
|
20
|
+
log "Deleted on initialization", :error
|
21
|
+
rescue Job::Release
|
22
|
+
beanstalk_job.release @job.priority, @job.delay
|
23
|
+
log "Released on initialization with priority: #{@job.priority} and delay: #{@job.delay}", :error
|
24
|
+
rescue Job::Bury
|
25
|
+
beanstalk_job.bury
|
26
|
+
log "Buried on initialization", :error
|
27
|
+
rescue Exception => e
|
28
|
+
beanstalk_job.bury
|
29
|
+
log "Exception caught on initialization. #{e.inspect}", :error
|
30
|
+
raise
|
34
31
|
end
|
35
|
-
|
36
|
-
def perform
|
37
|
-
begin
|
38
|
-
log "Performing with args #{job.args.inspect}"
|
39
|
-
log "Beanstalk Job Stats: #{beanstalk_job.stats.inspect}"
|
40
32
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
beanstalk_job.delete
|
45
|
-
end
|
33
|
+
def perform
|
34
|
+
log "Performing with args #{job.args.inspect}"
|
35
|
+
log "Beanstalk Job Stats: #{beanstalk_job.stats.inspect}"
|
46
36
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
log "Deleting job", :error
|
37
|
+
result = false
|
38
|
+
time = Benchmark.realtime do
|
39
|
+
result = job.perform!
|
51
40
|
beanstalk_job.delete
|
52
|
-
log "Job deleted", :error
|
53
|
-
rescue Job::Release
|
54
|
-
log "Releasing with priority: #{@job.priority} and delay: #{@job.delay}", :error
|
55
|
-
beanstalk_job.release :pri => @job.priority, :delay => @job.delay
|
56
|
-
log "Job released", :error
|
57
|
-
rescue Job::Bury
|
58
|
-
log "Burrying job", :error
|
59
|
-
beanstalk_job.bury
|
60
|
-
log "Job burried", :error
|
61
|
-
rescue Job::Timeout => e
|
62
|
-
log "Job timed out!", :error
|
63
|
-
retry_with_delay
|
64
|
-
raise e
|
65
|
-
rescue Job::Retry => e
|
66
|
-
# The difference between the Retry and Timeout class is that
|
67
|
-
# Retry does not log an exception where as Timeout does
|
68
|
-
log "Manually retrying with delay"
|
69
|
-
retry_with_delay
|
70
|
-
rescue Exception => e
|
71
|
-
log "Exception caught on perform. Burying job. #{e.inspect} #{e.backtrace.join("\n")}", :error
|
72
|
-
beanstalk_job.bury
|
73
|
-
log "Job buried", :error
|
74
|
-
raise e
|
75
41
|
end
|
42
|
+
|
43
|
+
log "Completed in #{(time*1000*1000).to_i/1000.to_f} ms\n"
|
44
|
+
result
|
45
|
+
rescue Job::Delete
|
46
|
+
log "Deleting job", :error
|
47
|
+
beanstalk_job.delete
|
48
|
+
log "Job deleted", :error
|
49
|
+
rescue Job::Release
|
50
|
+
log "Releasing with priority: #{@job.priority} and delay: #{@job.delay}", :error
|
51
|
+
beanstalk_job.release :pri => @job.priority, :delay => @job.delay
|
52
|
+
log "Job released", :error
|
53
|
+
rescue Job::Bury
|
54
|
+
log "Burrying job", :error
|
55
|
+
beanstalk_job.bury
|
56
|
+
log "Job burried", :error
|
57
|
+
rescue Job::Timeout => e
|
58
|
+
log "Job timed out. Retrying with delay. #{e.inspect} #{e.backtrace.join("\n")}", :error
|
59
|
+
retry_with_delay
|
60
|
+
raise
|
61
|
+
rescue Job::Retry
|
62
|
+
# The difference between the Retry and Timeout class is that
|
63
|
+
# Retry does not log an exception where as Timeout does
|
64
|
+
log "Manually retrying with delay"
|
65
|
+
retry_with_delay
|
66
|
+
rescue Exception => e
|
67
|
+
log "Exception caught on perform. Burying job. #{e.inspect} #{e.backtrace.join("\n")}", :error
|
68
|
+
beanstalk_job.bury
|
69
|
+
log "Job buried", :error
|
70
|
+
raise
|
76
71
|
end
|
77
72
|
|
78
73
|
protected
|
79
74
|
def retry_with_delay
|
80
|
-
|
81
|
-
delay = TIMEOUT_RETRY_DELAY_SEED + TIMEOUT_RETRY_GROWTH_RATE**beanstalk_job.stats["releases"].to_i
|
75
|
+
delay = TIMEOUT_RETRY_DELAY_SEED + TIMEOUT_RETRY_GROWTH_RATE**beanstalk_job.stats["releases"].to_i
|
82
76
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
end
|
92
|
-
rescue ::Beaneater::NotFoundError
|
93
|
-
log "Job ran longer than allowed. Beanstalk already deleted it!!!!", :error
|
94
|
-
# Sometimes the timer doesn't behave correctly and this job actually runs longer than
|
95
|
-
# allowed. At that point the beanstalk job no longer exists anymore. Lets let it go and don't blow up.
|
77
|
+
if delay > MAX_TIMEOUT_RETRY_DELAY
|
78
|
+
log "Max retry delay exceeded. Burrying job"
|
79
|
+
beanstalk_job.bury
|
80
|
+
log "Job burried"
|
81
|
+
else
|
82
|
+
log "TTR exceeded. Releasing with priority: #{@job.priority} and delay: #{delay}"
|
83
|
+
beanstalk_job.release :pri => @job.priority, :delay => delay
|
84
|
+
log "Job released"
|
96
85
|
end
|
86
|
+
rescue ::Beaneater::NotFoundError
|
87
|
+
log "Job ran longer than allowed. Beanstalk already deleted it!!!!", :error
|
88
|
+
# Sometimes the timer doesn't behave correctly and this job actually runs longer than
|
89
|
+
# allowed. At that point the beanstalk job no longer exists anymore. Lets let it go and don't blow up.
|
97
90
|
end
|
98
91
|
|
99
92
|
def log(message, level=:info)
|
data/lib/quebert/job.rb
CHANGED
@@ -51,10 +51,18 @@ module Quebert
|
|
51
51
|
|
52
52
|
# Runs the perform method that somebody else should be implementing
|
53
53
|
def perform!
|
54
|
+
Quebert.config.before_job(self)
|
55
|
+
Quebert.config.around_job(self)
|
56
|
+
|
54
57
|
# Honor the timeout and kill the job in ruby-space. Beanstalk
|
55
58
|
# should be cleaning up this job and returning it to the queue
|
56
59
|
# as well.
|
57
|
-
::Timeout.timeout(@ttr, Job::Timeout){ perform(*args) }
|
60
|
+
val = ::Timeout.timeout(@ttr, Job::Timeout){ perform(*args) }
|
61
|
+
|
62
|
+
Quebert.config.around_job(self)
|
63
|
+
Quebert.config.after_job(self)
|
64
|
+
|
65
|
+
val
|
58
66
|
end
|
59
67
|
|
60
68
|
# Accepts arguments that override the job options and enqueu this stuff.
|
data/lib/quebert/version.rb
CHANGED
data/spec/job_spec.rb
CHANGED
@@ -153,4 +153,35 @@ describe Quebert::Job do
|
|
153
153
|
}.should raise_exception(Quebert::Job::Timeout)
|
154
154
|
end
|
155
155
|
end
|
156
|
+
|
157
|
+
context "before, after & around hooks" do
|
158
|
+
it "should call each type of hook as expected" do
|
159
|
+
before_jobs = []
|
160
|
+
after_jobs = []
|
161
|
+
around_jobs = []
|
162
|
+
|
163
|
+
jobs = (1..10).map do |i|
|
164
|
+
Adder.new(i, i)
|
165
|
+
end
|
166
|
+
|
167
|
+
Quebert.config.before_job do |job|
|
168
|
+
before_jobs << job
|
169
|
+
end
|
170
|
+
|
171
|
+
Quebert.config.after_job do |job|
|
172
|
+
after_jobs << job
|
173
|
+
end
|
174
|
+
|
175
|
+
Quebert.config.around_job do |job|
|
176
|
+
around_jobs << job
|
177
|
+
end
|
178
|
+
|
179
|
+
jobs.each(&:perform!)
|
180
|
+
|
181
|
+
before_jobs.should eql jobs
|
182
|
+
after_jobs.should eql jobs
|
183
|
+
# around_job hooks are called twice per job (before & after its performed)
|
184
|
+
around_jobs.should eql jobs.zip(jobs).flatten
|
185
|
+
end
|
186
|
+
end
|
156
187
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: quebert
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brad Gessler
|
@@ -10,34 +10,34 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2014-
|
13
|
+
date: 2014-10-28 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: json
|
17
17
|
requirement: !ruby/object:Gem::Requirement
|
18
18
|
requirements:
|
19
|
-
- -
|
19
|
+
- - ">="
|
20
20
|
- !ruby/object:Gem::Version
|
21
21
|
version: '0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
25
25
|
requirements:
|
26
|
-
- -
|
26
|
+
- - ">="
|
27
27
|
- !ruby/object:Gem::Version
|
28
28
|
version: '0'
|
29
29
|
- !ruby/object:Gem::Dependency
|
30
30
|
name: beaneater
|
31
31
|
requirement: !ruby/object:Gem::Requirement
|
32
32
|
requirements:
|
33
|
-
- -
|
33
|
+
- - ">="
|
34
34
|
- !ruby/object:Gem::Version
|
35
35
|
version: '0'
|
36
36
|
type: :runtime
|
37
37
|
prerelease: false
|
38
38
|
version_requirements: !ruby/object:Gem::Requirement
|
39
39
|
requirements:
|
40
|
-
- -
|
40
|
+
- - ">="
|
41
41
|
- !ruby/object:Gem::Version
|
42
42
|
version: '0'
|
43
43
|
- !ruby/object:Gem::Dependency
|
@@ -62,11 +62,11 @@ executables:
|
|
62
62
|
extensions: []
|
63
63
|
extra_rdoc_files: []
|
64
64
|
files:
|
65
|
-
- .document
|
66
|
-
- .gitignore
|
67
|
-
- .rbenv-version
|
68
|
-
- .rspec
|
69
|
-
- .travis.yml
|
65
|
+
- ".document"
|
66
|
+
- ".gitignore"
|
67
|
+
- ".rbenv-version"
|
68
|
+
- ".rspec"
|
69
|
+
- ".travis.yml"
|
70
70
|
- Gemfile
|
71
71
|
- Guardfile
|
72
72
|
- LICENSE.md
|
@@ -121,17 +121,17 @@ require_paths:
|
|
121
121
|
- lib
|
122
122
|
required_ruby_version: !ruby/object:Gem::Requirement
|
123
123
|
requirements:
|
124
|
-
- -
|
124
|
+
- - ">="
|
125
125
|
- !ruby/object:Gem::Version
|
126
126
|
version: '0'
|
127
127
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
128
128
|
requirements:
|
129
|
-
- -
|
129
|
+
- - ">="
|
130
130
|
- !ruby/object:Gem::Version
|
131
131
|
version: '0'
|
132
132
|
requirements: []
|
133
133
|
rubyforge_project: quebert
|
134
|
-
rubygems_version: 2.2.
|
134
|
+
rubygems_version: 2.2.2
|
135
135
|
signing_key:
|
136
136
|
specification_version: 4
|
137
137
|
summary: A worker queue framework built around beanstalkd
|