ayl-beanstalk 0.2.0 → 0.3.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 +1 -1
- data/Gemfile.lock +2 -2
- data/VERSION +1 -1
- data/ayl-beanstalk.gemspec +8 -5
- data/lib/ayl-beanstalk/exceptions.rb +28 -0
- data/lib/ayl-beanstalk/job.rb +73 -0
- data/lib/ayl-beanstalk/worker.rb +81 -50
- data/lib/ayl-beanstalk.rb +2 -0
- data/spec/engine_spec.rb +1 -1
- data/spec/job_spec.rb +167 -0
- data/spec/worker_spec.rb +150 -145
- metadata +8 -5
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -30,7 +30,7 @@ GEM
|
|
30
30
|
activesupport (3.1.2)
|
31
31
|
multi_json (~> 1.0)
|
32
32
|
arel (2.2.1)
|
33
|
-
ayl (0.
|
33
|
+
ayl (0.3.0)
|
34
34
|
beanstalk-client (1.1.1)
|
35
35
|
builder (3.0.0)
|
36
36
|
coderay (0.9.8)
|
@@ -114,7 +114,7 @@ PLATFORMS
|
|
114
114
|
ruby
|
115
115
|
|
116
116
|
DEPENDENCIES
|
117
|
-
ayl (>= 0.
|
117
|
+
ayl (>= 0.3.0)
|
118
118
|
beanstalk-client (>= 1.1.0)
|
119
119
|
bundler (>= 1.0.0)
|
120
120
|
daemons (>= 1.1.0)
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.3.0
|
data/ayl-beanstalk.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "ayl-beanstalk"
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.3.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["j0hnds@gmail.com"]
|
12
|
-
s.date = "2013-07
|
12
|
+
s.date = "2013-08-07"
|
13
13
|
s.description = "Ayl extension to provide beanstalk support."
|
14
14
|
s.email = "j0hnds@gmail.com"
|
15
15
|
s.executables = ["ayl_beanstalk", "ayl_worker", "ayl_worker_control"]
|
@@ -33,12 +33,15 @@ Gem::Specification.new do |s|
|
|
33
33
|
"lib/ayl-beanstalk.rb",
|
34
34
|
"lib/ayl-beanstalk/command_line.rb",
|
35
35
|
"lib/ayl-beanstalk/engine.rb",
|
36
|
+
"lib/ayl-beanstalk/exceptions.rb",
|
37
|
+
"lib/ayl-beanstalk/job.rb",
|
36
38
|
"lib/ayl-beanstalk/pool.rb",
|
37
39
|
"lib/ayl-beanstalk/worker.rb",
|
38
40
|
"spec/ayl_worker_control_spec.rb",
|
39
41
|
"spec/ayl_worker_spec.rb",
|
40
42
|
"spec/command_line_spec.rb",
|
41
43
|
"spec/engine_spec.rb",
|
44
|
+
"spec/job_spec.rb",
|
42
45
|
"spec/spec_helper.rb",
|
43
46
|
"spec/support/config/environment.rb",
|
44
47
|
"spec/support/exit_code_matchers.rb",
|
@@ -54,7 +57,7 @@ Gem::Specification.new do |s|
|
|
54
57
|
s.specification_version = 3
|
55
58
|
|
56
59
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
57
|
-
s.add_runtime_dependency(%q<ayl>, [">= 0.
|
60
|
+
s.add_runtime_dependency(%q<ayl>, [">= 0.3.0"])
|
58
61
|
s.add_runtime_dependency(%q<beanstalk-client>, [">= 1.1.0"])
|
59
62
|
s.add_runtime_dependency(%q<daemons>, [">= 1.1.0"])
|
60
63
|
s.add_development_dependency(%q<rspec>, [">= 2.3.0"])
|
@@ -63,7 +66,7 @@ Gem::Specification.new do |s|
|
|
63
66
|
s.add_development_dependency(%q<rcov>, [">= 0"])
|
64
67
|
s.add_development_dependency(%q<pry>, [">= 0"])
|
65
68
|
else
|
66
|
-
s.add_dependency(%q<ayl>, [">= 0.
|
69
|
+
s.add_dependency(%q<ayl>, [">= 0.3.0"])
|
67
70
|
s.add_dependency(%q<beanstalk-client>, [">= 1.1.0"])
|
68
71
|
s.add_dependency(%q<daemons>, [">= 1.1.0"])
|
69
72
|
s.add_dependency(%q<rspec>, [">= 2.3.0"])
|
@@ -73,7 +76,7 @@ Gem::Specification.new do |s|
|
|
73
76
|
s.add_dependency(%q<pry>, [">= 0"])
|
74
77
|
end
|
75
78
|
else
|
76
|
-
s.add_dependency(%q<ayl>, [">= 0.
|
79
|
+
s.add_dependency(%q<ayl>, [">= 0.3.0"])
|
77
80
|
s.add_dependency(%q<beanstalk-client>, [">= 1.1.0"])
|
78
81
|
s.add_dependency(%q<daemons>, [">= 1.1.0"])
|
79
82
|
s.add_dependency(%q<rspec>, [">= 2.3.0"])
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Ayl
|
2
|
+
|
3
|
+
module Beanstalk
|
4
|
+
|
5
|
+
#
|
6
|
+
# Raise this exception to have ayl release the current job and place it in
|
7
|
+
# the delay slot for the tube.
|
8
|
+
#
|
9
|
+
class RequiresJobDecay < StandardError
|
10
|
+
|
11
|
+
attr_reader :delay
|
12
|
+
|
13
|
+
def initialize(delay=nil)
|
14
|
+
@delay = delay
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
#
|
19
|
+
# Raise this exception to have ayl release the current job and place it in
|
20
|
+
# the buried slot for the tube.
|
21
|
+
#
|
22
|
+
class RequiresJobBury < StandardError
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
#
|
2
|
+
# I don't like to call this a monkey-patch. I simply want to extend the
|
3
|
+
# Beanstalk::Job to take care of some things that are best left to it.
|
4
|
+
# So let's just call it 'ayl Duck-punching'.
|
5
|
+
#
|
6
|
+
# I promise that no methods overridden here; only new methods added.
|
7
|
+
#
|
8
|
+
class Beanstalk::Job
|
9
|
+
include Ayl::Logging
|
10
|
+
|
11
|
+
#
|
12
|
+
# Return the body of the job as an Ayl::Message. If the message is improperly
|
13
|
+
# formatted, then nil is returned.
|
14
|
+
#
|
15
|
+
def ayl_message
|
16
|
+
@msg ||= Ayl::Message.from_hash(ybody)
|
17
|
+
rescue Ayl::UnrecoverableMessageException => ex
|
18
|
+
logger.error "Error extracting message from beanstalk job: #{ex}"
|
19
|
+
Ayl::Mailer.instance.deliver_message "Error extracting message from beanstalk job", ex
|
20
|
+
@msg = nil
|
21
|
+
end
|
22
|
+
|
23
|
+
#
|
24
|
+
# Delete the job handling any exceptions that occur during the job deletion
|
25
|
+
# (like the job not existing).
|
26
|
+
#
|
27
|
+
def ayl_delete
|
28
|
+
delete
|
29
|
+
rescue Exception => ex
|
30
|
+
logger.error "Error deleting job: #{ex}\n#{ex.backtrace.join("\n")}"
|
31
|
+
Ayl::Mailer.instance.deliver_message("Error deleting job", ex)
|
32
|
+
end
|
33
|
+
|
34
|
+
#
|
35
|
+
# Decay the job handling any exceptions that occur during the job decay
|
36
|
+
# process.
|
37
|
+
#
|
38
|
+
def ayl_decay(delay=nil)
|
39
|
+
decay(*[ delay ].compact)
|
40
|
+
rescue Exception => ex
|
41
|
+
logger.error "Error decaying job: #{ex}\n#{ex.backtrace.join("\n")}"
|
42
|
+
Ayl::Mailer.instance.deliver_message("Error decaying job", ex)
|
43
|
+
end
|
44
|
+
|
45
|
+
#
|
46
|
+
# Bury the job handling any exceptions that occur during the burying
|
47
|
+
# process.
|
48
|
+
#
|
49
|
+
def ayl_bury
|
50
|
+
bury
|
51
|
+
rescue Exception => ex
|
52
|
+
logger.error "Error burying job: #{ex}\n#{ex.backtrace.join("\n")}"
|
53
|
+
Ayl::Mailer.instance.deliver_message("Error decaying job", ex)
|
54
|
+
end
|
55
|
+
|
56
|
+
#
|
57
|
+
# Handle the decay process by deleting the job if its age is more than
|
58
|
+
# 60 seconds, but delaying it if it is younger than 60 seconds. Obviously
|
59
|
+
# we want to handle any exceptions that occur here.
|
60
|
+
#
|
61
|
+
def handle_decay(ex)
|
62
|
+
logger.debug "Age of job: #{age}"
|
63
|
+
if age > 60
|
64
|
+
Ayl::Mailer.instance.deliver_message("Deleting decayed job; it just took too long.", ex)
|
65
|
+
logger.debug "Deleting job"
|
66
|
+
ayl_delete
|
67
|
+
else
|
68
|
+
logger.debug "Decaying job"
|
69
|
+
ayl_decay
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
data/lib/ayl-beanstalk/worker.rb
CHANGED
@@ -14,71 +14,102 @@ module Ayl
|
|
14
14
|
|
15
15
|
def process_messages
|
16
16
|
logger.debug "#{self.class.name} entering process_messages loop watching: #{Ayl::MessageOptions.default_queue_name}"
|
17
|
+
|
17
18
|
# Set the queue that we will be watching
|
18
19
|
pool.watch(Ayl::MessageOptions.default_queue_name)
|
19
|
-
|
20
|
-
|
21
|
-
job = nil
|
22
|
-
begin
|
23
|
-
job = pool.reserve
|
24
|
-
rescue Exception => ex
|
25
|
-
logger.error "#{self.class.name} Exception in process_messages: #{ex}\n#{ex.backtrace.join("\n")}"
|
26
|
-
Ayl::Mailer.instance.deliver_message("#{self.class.name} Exception in process_messages.", ex)
|
27
|
-
delete_job(job) unless job.nil?
|
28
|
-
next
|
29
|
-
end
|
30
|
-
break if job.nil?
|
31
|
-
msg = nil
|
20
|
+
|
21
|
+
reserve_job do | job |
|
32
22
|
begin
|
33
|
-
|
34
|
-
process_message(
|
35
|
-
|
36
|
-
|
37
|
-
logger.error "#{self.class.name} Unrecoverable exception in process_messages: #{ex}"
|
38
|
-
Ayl::Mailer.instance.deliver_message("#{self.class.name} Unrecoverable exception in process_messages", ex)
|
39
|
-
delete_job(job)
|
23
|
+
|
24
|
+
process_message(job.ayl_message) unless job.ayl_message.nil?
|
25
|
+
job.ayl_delete
|
26
|
+
|
40
27
|
rescue SystemExit
|
28
|
+
|
41
29
|
# This exception is raised when 'Kernel.exit' is called. In this case
|
42
30
|
# we want to make sure the job is deleted, then we simply re-raise
|
43
31
|
# the exception and we go bye-bye.
|
44
|
-
|
32
|
+
job.ayl_delete
|
45
33
|
raise
|
34
|
+
|
35
|
+
rescue Ayl::Beanstalk::RequiresJobDecay => ex
|
36
|
+
|
37
|
+
# The code in the message body has requested that we throw this job back
|
38
|
+
# in the queue with a delay.
|
39
|
+
job.ayl_decay(ex.delay)
|
40
|
+
|
41
|
+
rescue Ayl::Beanstalk::RequiresJobBury => ex
|
42
|
+
|
43
|
+
# The code in the message body has requested that we throw this job
|
44
|
+
# into the 'buried' state. This will allow a human to look the job
|
45
|
+
# over and determine if it can be processed
|
46
|
+
job.ayl_bury
|
47
|
+
|
46
48
|
rescue Exception => ex
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
else
|
51
|
-
delete_job(job)
|
52
|
-
Ayl::Mailer.instance.deliver_message("#{self.class.name} Exception in process_messages.", ex)
|
53
|
-
end
|
49
|
+
|
50
|
+
deal_with_unexpected_exception(job, ex)
|
51
|
+
|
54
52
|
end
|
55
|
-
end
|
56
|
-
end
|
57
53
|
|
58
|
-
|
59
|
-
|
60
|
-
if job.age > 60
|
61
|
-
Ayl::Mailer.instance.deliver_message("#{self.class.name} Deleting decayed job; it just took too long.", ex)
|
62
|
-
logger.debug "Deleting job"
|
63
|
-
delete_job(job)
|
64
|
-
else
|
65
|
-
logger.debug "Decaying job"
|
66
|
-
decay_job(job)
|
67
|
-
end
|
54
|
+
end # reserve_job...
|
55
|
+
|
68
56
|
end
|
69
57
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
58
|
+
private
|
59
|
+
|
60
|
+
#
|
61
|
+
# The main loop that gets job from the beanstalk queue to process. When a job is
|
62
|
+
# received it will be passed to the block for this method.
|
63
|
+
#
|
64
|
+
def reserve_job
|
65
|
+
while true
|
66
|
+
job = nil
|
67
|
+
|
68
|
+
begin
|
69
|
+
|
70
|
+
# Sit around and wait for a job to become available
|
71
|
+
job = pool.reserve
|
72
|
+
|
73
|
+
rescue Exception => ex
|
74
|
+
|
75
|
+
logger.error "Unexpected exception in reserve_job: #{ex}\n#{ex.backtrace.join("\n")}"
|
76
|
+
Ayl::Mailer.instance.deliver_message("Unexpected exception in process_messages.", ex)
|
77
|
+
job.ayl_delete unless job.nil?
|
78
|
+
|
79
|
+
# Notice that we are just breaking out of the loop here. Why?
|
80
|
+
# Think about the kinds of exceptions that occur here. They will be
|
81
|
+
# things like Beanstalk::OUT_OF_MEMORY errors, beanstalkd connection
|
82
|
+
# errors, etc. At this point, the worker is finished, kaput. Might as
|
83
|
+
# well report and die. That's exactly what will happen.
|
84
|
+
break
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
break if job.nil?
|
89
|
+
|
90
|
+
yield job
|
91
|
+
|
92
|
+
end
|
75
93
|
end
|
76
94
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
95
|
+
#
|
96
|
+
# Deals with the decision to decay or delete job when an unexpected
|
97
|
+
# exception is encountered.
|
98
|
+
#
|
99
|
+
def deal_with_unexpected_exception(job, ex)
|
100
|
+
logger.error "Unexpected exception in process_messages: #{ex}\n#{ex.backtrace.join("\n")}"
|
101
|
+
case job.ayl_message.options.failed_job_handler
|
102
|
+
when 'decay'
|
103
|
+
job.handle_decay(ex)
|
104
|
+
when 'delete'
|
105
|
+
job.ayl_delete
|
106
|
+
Ayl::Mailer.instance.deliver_message("Exception in process_messages. Job deleted.", ex)
|
107
|
+
when 'bury'
|
108
|
+
job.ayl_bury
|
109
|
+
Ayl::Mailer.instance.deliver_message("Exception in process_messages. Job buried", ex)
|
110
|
+
else
|
111
|
+
Ayl::Mailer.instance.deliver_message("Invalid failed job handler specified: #{job.ayl_message.options.failed_job_handler}", ex)
|
112
|
+
end
|
82
113
|
end
|
83
114
|
|
84
115
|
end
|
data/lib/ayl-beanstalk.rb
CHANGED
data/spec/engine_spec.rb
CHANGED
@@ -47,7 +47,7 @@ describe Ayl::Beanstalk::Engine do
|
|
47
47
|
it "should submit the specified message to beanstalk" do
|
48
48
|
mock_pool = mock("Beanstalk::Pool")
|
49
49
|
mock_pool.should_receive(:use).with("default")
|
50
|
-
mock_pool.should_receive(:yput).with( { :type => :ayl, :
|
50
|
+
mock_pool.should_receive(:yput).with( { :type => :ayl, :failed_job_handler => 'delete', :code => "23.to_s(2)" }, 512, 0, 120)
|
51
51
|
|
52
52
|
::Beanstalk::Pool.should_receive(:new).with([ "localhost:11300" ]).and_return(mock_pool)
|
53
53
|
|
data/spec/job_spec.rb
ADDED
@@ -0,0 +1,167 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'beanstalk-client'
|
3
|
+
|
4
|
+
describe Beanstalk::Job do
|
5
|
+
|
6
|
+
before(:each) do
|
7
|
+
@job = Beanstalk::Job.new(nil,nil,nil)
|
8
|
+
@job.stub_chain(:logger, :error)
|
9
|
+
@job.stub_chain(:logger, :debug)
|
10
|
+
end
|
11
|
+
|
12
|
+
context '#ayl_message' do
|
13
|
+
|
14
|
+
it "should return the message constructed from the body of the job" do
|
15
|
+
@job.stub(:ybody).and_return('the body')
|
16
|
+
msg = Ayl::Message.new(nil,nil,nil)
|
17
|
+
Ayl::Message.should_receive(:from_hash).with('the body').and_return(msg)
|
18
|
+
|
19
|
+
@job.ayl_message.should == msg
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should return the same message constructed from the body of the job on subsequent calls" do
|
23
|
+
@job.stub(:ybody).and_return('the body')
|
24
|
+
msg = Ayl::Message.new(nil,nil,nil)
|
25
|
+
Ayl::Message.should_receive(:from_hash).with('the body').and_return(msg)
|
26
|
+
|
27
|
+
@job.ayl_message.should == msg
|
28
|
+
|
29
|
+
@job.ayl_message.should == msg
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should return nil and send an email if the message body was bad" do
|
33
|
+
@job.stub(:ybody).and_return('the body')
|
34
|
+
msg = Ayl::Message.new(nil,nil,nil)
|
35
|
+
ex = Ayl::UnrecoverableMessageException.new
|
36
|
+
Ayl::Message.should_receive(:from_hash).with('the body').and_raise(ex)
|
37
|
+
|
38
|
+
Ayl::Mailer.should_receive(:instance).and_return do
|
39
|
+
mock("Mailer").tap do | mock_mailer |
|
40
|
+
mock_mailer.should_receive(:deliver_message).with(anything(), ex)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
@job.ayl_message.should == nil
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
context '#ayl_delete' do
|
50
|
+
|
51
|
+
it "should call the delete method on the job" do
|
52
|
+
@job.should_receive(:delete)
|
53
|
+
|
54
|
+
@job.ayl_delete
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should send an email if the delete method raises an exception" do
|
58
|
+
ex = Exception.new
|
59
|
+
@job.should_receive(:delete).and_raise(ex)
|
60
|
+
|
61
|
+
Ayl::Mailer.should_receive(:instance).and_return do
|
62
|
+
mock("Mailer").tap do | mock_mailer |
|
63
|
+
mock_mailer.should_receive(:deliver_message).with(anything(), ex)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
@job.ayl_delete
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
context '#ayl_decay' do
|
73
|
+
|
74
|
+
it "should call the decay with no arguments when nil delay is specified" do
|
75
|
+
@job.should_receive(:decay).with()
|
76
|
+
|
77
|
+
@job.ayl_decay(nil)
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should call the decay with no arguments when no delay is specified" do
|
81
|
+
@job.should_receive(:decay).with()
|
82
|
+
|
83
|
+
@job.ayl_decay
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should call the decay with no arguments when no delay is specified" do
|
87
|
+
@job.should_receive(:decay).with(10)
|
88
|
+
|
89
|
+
@job.ayl_decay(10)
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should send an email if the decay method raises an exception" do
|
93
|
+
ex = Exception.new
|
94
|
+
@job.should_receive(:decay).and_raise(ex)
|
95
|
+
|
96
|
+
Ayl::Mailer.should_receive(:instance).and_return do
|
97
|
+
mock("Mailer").tap do | mock_mailer |
|
98
|
+
mock_mailer.should_receive(:deliver_message).with(anything(), ex)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
@job.ayl_decay
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
context '#ayl_bury' do
|
108
|
+
|
109
|
+
it "should call the bury method on the job" do
|
110
|
+
@job.should_receive(:bury)
|
111
|
+
|
112
|
+
@job.ayl_bury
|
113
|
+
end
|
114
|
+
|
115
|
+
it "should send an email if the bury method raises an exception" do
|
116
|
+
ex = Exception.new
|
117
|
+
@job.should_receive(:bury).and_raise(ex)
|
118
|
+
|
119
|
+
Ayl::Mailer.should_receive(:instance).and_return do
|
120
|
+
mock("Mailer").tap do | mock_mailer |
|
121
|
+
mock_mailer.should_receive(:deliver_message).with(anything(), ex)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
@job.ayl_bury
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
|
130
|
+
context '#handle_decay' do
|
131
|
+
|
132
|
+
it "should decay the job if the age of the job is less than 60 seconds" do
|
133
|
+
@job.should_receive(:age).at_least(1).times.and_return(2)
|
134
|
+
@job.should_receive(:ayl_decay)
|
135
|
+
|
136
|
+
ex = Exception.new
|
137
|
+
|
138
|
+
@job.handle_decay(ex)
|
139
|
+
end
|
140
|
+
|
141
|
+
it "should decay the job if the age of the job is exactly 60 seconds" do
|
142
|
+
@job.should_receive(:age).at_least(1).times.and_return(60)
|
143
|
+
@job.should_receive(:ayl_decay)
|
144
|
+
|
145
|
+
ex = Exception.new
|
146
|
+
|
147
|
+
@job.handle_decay(ex)
|
148
|
+
end
|
149
|
+
|
150
|
+
it "should delete the job and send an email if the job was older than 60 seconds" do
|
151
|
+
@job.should_receive(:age).at_least(1).times.and_return(61)
|
152
|
+
@job.should_receive(:ayl_delete)
|
153
|
+
|
154
|
+
ex = Exception.new
|
155
|
+
|
156
|
+
Ayl::Mailer.should_receive(:instance).and_return do
|
157
|
+
mock("Mailer").tap do | mock_mailer |
|
158
|
+
mock_mailer.should_receive(:deliver_message).with(anything(), ex)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
@job.handle_decay(ex)
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
166
|
+
|
167
|
+
end
|
data/spec/worker_spec.rb
CHANGED
@@ -5,7 +5,7 @@ require 'active_record/errors'
|
|
5
5
|
|
6
6
|
describe Ayl::Beanstalk::Worker do
|
7
7
|
|
8
|
-
context
|
8
|
+
context '#reserve_job' do
|
9
9
|
|
10
10
|
before(:each) do
|
11
11
|
Kernel.stub(:puts)
|
@@ -13,217 +13,222 @@ describe Ayl::Beanstalk::Worker do
|
|
13
13
|
@worker.stub_chain(:logger, :info)
|
14
14
|
@worker.stub_chain(:logger, :error)
|
15
15
|
@worker.stub_chain(:logger, :debug)
|
16
|
-
Ayl::MessageOptions.default_queue_name = 'default'
|
17
|
-
end
|
18
|
-
|
19
|
-
it "should wait for a message to be received from beanstalk and process it" do
|
20
|
-
Ayl::MessageOptions.default_queue_name = 'the_queue_name'
|
21
|
-
mock_job = mock("Beanstalk::Job")
|
22
|
-
mock_job.should_receive(:delete)
|
23
|
-
mock_job.should_receive(:ybody).and_return({ :type => :ayl, :code => "23.to_s(2)" })
|
24
16
|
|
25
|
-
mock_pool = mock("Beanstalk::Pool")
|
26
|
-
mock_pool.should_receive(:watch).with("the_queue_name")
|
27
|
-
# Returns nil on the second call.
|
28
|
-
mock_pool.should_receive(:reserve).and_return(mock_job, nil)
|
17
|
+
@mock_pool = mock("Beanstalk::Pool")
|
29
18
|
|
30
|
-
|
31
|
-
|
32
|
-
Ayl::Message.stub(:from_hash).with({ :type => :ayl, :code => "23.to_s(2)" }).and_return(mock_message)
|
19
|
+
::Beanstalk::Pool.should_receive(:new).with([ "localhost:11300" ]).and_return(@mock_pool)
|
20
|
+
end
|
33
21
|
|
34
|
-
|
22
|
+
it "should loop until there are no more jobs from beanstalk" do
|
23
|
+
mock_job1 = mock("Beanstalk::Job")
|
24
|
+
mock_job2 = mock("Beanstalk::Job")
|
35
25
|
|
36
|
-
@
|
26
|
+
@mock_pool.should_receive(:reserve).and_return(mock_job1, mock_job2, nil)
|
27
|
+
|
28
|
+
index = 0
|
29
|
+
|
30
|
+
@worker.send(:reserve_job) do | job |
|
31
|
+
job.should == [ mock_job1, mock_job2 ][index]
|
32
|
+
index += 1
|
33
|
+
end
|
34
|
+
|
35
|
+
index.should == 2
|
37
36
|
|
38
37
|
end
|
39
38
|
|
40
|
-
it "should
|
41
|
-
|
42
|
-
mock_job.should_receive(:delete)
|
43
|
-
mock_job.should_receive(:ybody).and_return("a string")
|
44
|
-
mock_job.should_not_receive(:age)
|
45
|
-
|
46
|
-
mock_pool = mock("Beanstalk::Pool")
|
47
|
-
mock_pool.should_receive(:watch).with("default")
|
48
|
-
# Returns nil on the second call.
|
49
|
-
mock_pool.should_receive(:reserve).and_return(mock_job, nil)
|
39
|
+
it "should report any exception while waiting for a job" do
|
40
|
+
@mock_pool.should_receive(:reserve).and_raise('It blew')
|
50
41
|
|
51
|
-
|
42
|
+
mock_mailer = mock("Mailer")
|
43
|
+
mock_mailer.should_receive(:deliver_message).with(any_args())
|
52
44
|
|
53
|
-
::
|
45
|
+
Ayl::Mailer.should_receive(:instance).and_return(mock_mailer)
|
54
46
|
|
55
|
-
@worker.
|
47
|
+
@worker.send(:reserve_job) do | job |
|
48
|
+
end
|
56
49
|
end
|
57
50
|
|
58
|
-
|
59
|
-
mock_job = mock("Beanstalk::Job")
|
60
|
-
mock_job.should_receive(:delete)
|
61
|
-
mock_job.should_receive(:ybody).and_return({ :type => :junk, :code => "Dog" })
|
62
|
-
mock_job.should_not_receive(:age)
|
51
|
+
end
|
63
52
|
|
64
|
-
|
65
|
-
mock_pool.should_receive(:watch).with("default")
|
66
|
-
# Returns nil on the second call.
|
67
|
-
mock_pool.should_receive(:reserve).and_return(mock_job, nil)
|
53
|
+
context '#deal_with_unexpected_exception' do
|
68
54
|
|
69
|
-
|
55
|
+
before(:each) do
|
56
|
+
Kernel.stub(:puts)
|
57
|
+
@worker = Ayl::Beanstalk::Worker.new
|
58
|
+
@worker.stub_chain(:logger, :info)
|
59
|
+
@worker.stub_chain(:logger, :error)
|
60
|
+
@worker.stub_chain(:logger, :debug)
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should call the job's handle_decay method if the message requires the job to decay" do
|
64
|
+
mock_job = mock("Beanstalk::Job")
|
65
|
+
mock_job.should_receive(:ayl_message).and_return do
|
66
|
+
mock("message").tap do | mock_message |
|
67
|
+
mock_message.should_receive(:options).and_return do
|
68
|
+
mock("options").tap do | mock_options |
|
69
|
+
mock_options.should_receive(:failed_job_handler).and_return('decay')
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
70
74
|
|
71
|
-
|
75
|
+
mock_exception = mock("Exception")
|
76
|
+
mock_exception.should_receive(:backtrace).and_return([])
|
77
|
+
mock_job.should_receive(:handle_decay).with(mock_exception)
|
72
78
|
|
73
|
-
@worker.
|
79
|
+
@worker.send(:deal_with_unexpected_exception, mock_job, mock_exception)
|
74
80
|
end
|
75
81
|
|
76
|
-
it "should
|
82
|
+
it "should call the job's ayl_delete method if the message requires the job to be deleted" do
|
77
83
|
mock_job = mock("Beanstalk::Job")
|
78
|
-
mock_job.should_receive(:
|
79
|
-
|
80
|
-
|
84
|
+
mock_job.should_receive(:ayl_message).and_return do
|
85
|
+
mock("message").tap do | mock_message |
|
86
|
+
mock_message.should_receive(:options).and_return do
|
87
|
+
mock("options").tap do | mock_options |
|
88
|
+
mock_options.should_receive(:failed_job_handler).and_return('delete')
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
81
93
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
mock_pool.should_receive(:reserve).and_return(mock_job, nil)
|
94
|
+
mock_exception = mock("Exception")
|
95
|
+
mock_exception.should_receive(:backtrace).and_return([])
|
96
|
+
mock_job.should_receive(:ayl_delete)
|
86
97
|
|
87
|
-
|
98
|
+
mock_mailer = mock("Mailer")
|
99
|
+
mock_mailer.should_receive(:deliver_message).with(any_args())
|
88
100
|
|
89
|
-
::
|
101
|
+
Ayl::Mailer.should_receive(:instance).and_return(mock_mailer)
|
90
102
|
|
91
|
-
@worker.
|
103
|
+
@worker.send(:deal_with_unexpected_exception, mock_job, mock_exception)
|
92
104
|
end
|
93
105
|
|
94
|
-
it "should
|
106
|
+
it "should call the job's ayl_bury method if the message requires the job to be buried" do
|
95
107
|
mock_job = mock("Beanstalk::Job")
|
96
|
-
mock_job.
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
mock_pool.should_receive(:reserve).and_return(mock_job, nil)
|
104
|
-
|
105
|
-
mock_message = mock("Ayl::Message")
|
106
|
-
mock_message.should_receive(:evaluate).and_raise(ActiveRecord::RecordNotFound)
|
107
|
-
mock_message.should_receive(:options).and_return do
|
108
|
-
mock("Options").tap do | mock_opts |
|
109
|
-
mock_opts.should_receive(:decay_failed_job).and_return(false)
|
108
|
+
mock_job.should_receive(:ayl_message).and_return do
|
109
|
+
mock("message").tap do | mock_message |
|
110
|
+
mock_message.should_receive(:options).and_return do
|
111
|
+
mock("options").tap do | mock_options |
|
112
|
+
mock_options.should_receive(:failed_job_handler).and_return('bury')
|
113
|
+
end
|
114
|
+
end
|
110
115
|
end
|
111
116
|
end
|
112
|
-
Ayl::Message.stub(:from_hash).with({ :type => :ayl, :code => "Dog" }).and_return(mock_message)
|
113
117
|
|
114
|
-
|
118
|
+
mock_exception = mock("Exception")
|
119
|
+
mock_exception.should_receive(:backtrace).and_return([])
|
120
|
+
mock_job.should_receive(:ayl_bury)
|
115
121
|
|
116
|
-
|
117
|
-
|
122
|
+
mock_mailer = mock("Mailer")
|
123
|
+
mock_mailer.should_receive(:deliver_message).with(any_args())
|
124
|
+
|
125
|
+
Ayl::Mailer.should_receive(:instance).and_return(mock_mailer)
|
126
|
+
|
127
|
+
@worker.send(:deal_with_unexpected_exception, mock_job, mock_exception)
|
118
128
|
end
|
119
129
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
130
|
+
end
|
131
|
+
|
132
|
+
context "Message Processing" do
|
133
|
+
|
134
|
+
before(:each) do
|
135
|
+
Kernel.stub(:puts)
|
136
|
+
@worker = Ayl::Beanstalk::Worker.new
|
137
|
+
@worker.stub_chain(:logger, :info)
|
138
|
+
@worker.stub_chain(:logger, :error)
|
139
|
+
@worker.stub_chain(:logger, :debug)
|
140
|
+
Ayl::MessageOptions.default_queue_name = 'the queue name'
|
125
141
|
|
126
|
-
mock_pool = mock("Beanstalk::Pool")
|
127
|
-
mock_pool.should_receive(:watch).with(
|
128
|
-
|
142
|
+
@mock_pool = mock("Beanstalk::Pool")
|
143
|
+
@mock_pool.should_receive(:watch).with('the queue name')
|
144
|
+
@worker.stub(:pool).and_return(@mock_pool)
|
145
|
+
end
|
129
146
|
|
147
|
+
it "should process a message received from beanstalk" do
|
130
148
|
mock_message = mock("Ayl::Message")
|
131
|
-
mock_message.should_receive(:evaluate).and_raise(ActiveRecord::RecordNotFound)
|
132
|
-
mock_message.should_receive(:options).and_return do
|
133
|
-
mock("Options").tap do | mock_opts |
|
134
|
-
mock_opts.should_receive(:decay_failed_job).and_return(true)
|
135
|
-
end
|
136
|
-
end
|
137
|
-
Ayl::Message.stub(:from_hash).with({ :type => :ayl, :code => "Dog" }).and_return(mock_message)
|
138
149
|
|
139
|
-
|
150
|
+
mock_job = mock("Beanstalk::Job")
|
151
|
+
mock_job.should_receive(:ayl_message).at_least(1).times.and_return(mock_message)
|
152
|
+
mock_job.should_receive(:ayl_delete)
|
153
|
+
|
154
|
+
@worker.should_receive(:process_message).with(mock_message)
|
155
|
+
|
156
|
+
@worker.should_receive(:reserve_job).and_yield(mock_job)
|
140
157
|
|
141
158
|
@worker.process_messages
|
142
159
|
end
|
143
160
|
|
144
|
-
it "should
|
161
|
+
it "should do nothing if the received message is invalid (nil)" do
|
145
162
|
mock_job = mock("Beanstalk::Job")
|
146
|
-
mock_job.should_receive(:
|
147
|
-
mock_job.should_receive(:
|
148
|
-
mock_job.should_receive(:age).exactly(2).times.and_return(65)
|
163
|
+
mock_job.should_receive(:ayl_message).at_least(1).times.and_return(nil)
|
164
|
+
mock_job.should_receive(:ayl_delete)
|
149
165
|
|
150
|
-
|
151
|
-
mock_pool.should_receive(:watch).with("default")
|
152
|
-
mock_pool.should_receive(:reserve).and_return(mock_job, nil)
|
166
|
+
@worker.should_not_receive(:process_message)
|
153
167
|
|
154
|
-
|
155
|
-
mock_message.should_receive(:evaluate).and_raise(ActiveRecord::RecordNotFound)
|
156
|
-
mock_message.should_receive(:options).and_return do
|
157
|
-
mock("Options").tap do | mock_opts |
|
158
|
-
mock_opts.should_receive(:decay_failed_job).and_return(true)
|
159
|
-
end
|
160
|
-
end
|
161
|
-
Ayl::Message.stub(:from_hash).with({ :type => :ayl, :code => "Dog" }).and_return(mock_message)
|
162
|
-
|
163
|
-
::Beanstalk::Pool.should_receive(:new).with([ "localhost:11300" ]).and_return(mock_pool)
|
168
|
+
@worker.should_receive(:reserve_job).and_yield(mock_job)
|
164
169
|
|
165
170
|
@worker.process_messages
|
166
171
|
end
|
167
172
|
|
168
|
-
it "should
|
173
|
+
it "should delete the job and re-raise the exception on a SystemExit" do
|
174
|
+
mock_message = mock("Ayl::Message")
|
169
175
|
|
170
176
|
mock_job = mock("Beanstalk::Job")
|
171
|
-
mock_job.should_receive(:
|
172
|
-
mock_job.should_receive(:
|
173
|
-
|
177
|
+
mock_job.should_receive(:ayl_message).at_least(1).times.and_return(mock_message)
|
178
|
+
mock_job.should_receive(:ayl_delete)
|
179
|
+
|
180
|
+
@worker.should_receive(:process_message).with(mock_message).and_raise(SystemExit)
|
174
181
|
|
175
|
-
|
176
|
-
mock_pool.should_receive(:watch).with("default")
|
177
|
-
mock_pool.should_receive(:reserve).and_return(mock_job, nil)
|
182
|
+
@worker.should_receive(:reserve_job).and_yield(mock_job)
|
178
183
|
|
184
|
+
lambda { @worker.process_messages }.should raise_error(SystemExit)
|
185
|
+
end
|
186
|
+
|
187
|
+
it "should decay the job if the message requires it" do
|
179
188
|
mock_message = mock("Ayl::Message")
|
180
|
-
mock_message.should_receive(:evaluate)
|
181
|
-
Ayl::Message.stub(:from_hash).with({ :type => :ayl, :code => "23.to_s(2)" }).and_return(mock_message)
|
182
189
|
|
183
|
-
|
190
|
+
mock_job = mock("Beanstalk::Job")
|
191
|
+
mock_job.should_receive(:ayl_message).at_least(1).times.and_return(mock_message)
|
192
|
+
mock_job.should_receive(:ayl_decay).with(20)
|
184
193
|
|
185
|
-
|
186
|
-
end
|
187
|
-
end
|
194
|
+
@worker.should_receive(:process_message).with(mock_message).and_raise(Ayl::Beanstalk::RequiresJobDecay.new(20))
|
188
195
|
|
189
|
-
|
196
|
+
@worker.should_receive(:reserve_job).and_yield(mock_job)
|
190
197
|
|
191
|
-
|
192
|
-
Kernel.stub(:puts)
|
193
|
-
@worker = Ayl::Beanstalk::Worker.new
|
194
|
-
@worker.stub_chain(:logger, :info)
|
195
|
-
@worker.stub_chain(:logger, :error)
|
196
|
-
@worker.stub_chain(:logger, :debug)
|
197
|
-
Ayl::MessageOptions.default_queue_name = 'default'
|
198
|
+
@worker.process_messages
|
198
199
|
end
|
199
200
|
|
200
|
-
it "should
|
201
|
-
|
202
|
-
mock_job.should_receive(:delete).and_raise("Delete failed for some reason")
|
201
|
+
it "should bury the job if the message requires it" do
|
202
|
+
mock_message = mock("Ayl::Message")
|
203
203
|
|
204
|
-
|
205
|
-
|
204
|
+
mock_job = mock("Beanstalk::Job")
|
205
|
+
mock_job.should_receive(:ayl_message).at_least(1).times.and_return(mock_message)
|
206
|
+
mock_job.should_receive(:ayl_bury)
|
206
207
|
|
207
|
-
|
208
|
+
@worker.should_receive(:process_message).with(mock_message).and_raise(Ayl::Beanstalk::RequiresJobBury)
|
208
209
|
|
209
|
-
|
210
|
+
@worker.should_receive(:reserve_job).and_yield(mock_job)
|
210
211
|
|
211
|
-
|
212
|
-
Kernel.stub(:puts)
|
213
|
-
@worker = Ayl::Beanstalk::Worker.new
|
214
|
-
@worker.stub_chain(:logger, :info)
|
215
|
-
@worker.stub_chain(:logger, :error)
|
216
|
-
@worker.stub_chain(:logger, :debug)
|
217
|
-
Ayl::MessageOptions.default_queue_name = 'default'
|
212
|
+
@worker.process_messages
|
218
213
|
end
|
219
214
|
|
220
|
-
it "should
|
221
|
-
|
222
|
-
|
215
|
+
it "should handle all other unexpected exceptions" do
|
216
|
+
mock_message = mock("Ayl::Message")
|
217
|
+
|
218
|
+
mock_job = mock("Beanstalk::Job")
|
219
|
+
mock_job.should_receive(:ayl_message).at_least(1).times.and_return(mock_message)
|
223
220
|
|
224
|
-
|
221
|
+
ex = Exception.new
|
222
|
+
|
223
|
+
@worker.should_receive(:process_message).with(mock_message).and_raise(ex)
|
224
|
+
|
225
|
+
@worker.should_receive(:reserve_job).and_yield(mock_job)
|
226
|
+
@worker.should_receive(:deal_with_unexpected_exception).with(mock_job, ex)
|
227
|
+
|
228
|
+
@worker.process_messages
|
225
229
|
end
|
226
230
|
|
227
231
|
end
|
228
232
|
|
233
|
+
|
229
234
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ayl-beanstalk
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-07
|
12
|
+
date: 2013-08-07 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: ayl
|
@@ -18,7 +18,7 @@ dependencies:
|
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
20
20
|
- !ruby/object:Gem::Version
|
21
|
-
version: 0.
|
21
|
+
version: 0.3.0
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -26,7 +26,7 @@ dependencies:
|
|
26
26
|
requirements:
|
27
27
|
- - ! '>='
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
version: 0.
|
29
|
+
version: 0.3.0
|
30
30
|
- !ruby/object:Gem::Dependency
|
31
31
|
name: beanstalk-client
|
32
32
|
requirement: !ruby/object:Gem::Requirement
|
@@ -165,12 +165,15 @@ files:
|
|
165
165
|
- lib/ayl-beanstalk.rb
|
166
166
|
- lib/ayl-beanstalk/command_line.rb
|
167
167
|
- lib/ayl-beanstalk/engine.rb
|
168
|
+
- lib/ayl-beanstalk/exceptions.rb
|
169
|
+
- lib/ayl-beanstalk/job.rb
|
168
170
|
- lib/ayl-beanstalk/pool.rb
|
169
171
|
- lib/ayl-beanstalk/worker.rb
|
170
172
|
- spec/ayl_worker_control_spec.rb
|
171
173
|
- spec/ayl_worker_spec.rb
|
172
174
|
- spec/command_line_spec.rb
|
173
175
|
- spec/engine_spec.rb
|
176
|
+
- spec/job_spec.rb
|
174
177
|
- spec/spec_helper.rb
|
175
178
|
- spec/support/config/environment.rb
|
176
179
|
- spec/support/exit_code_matchers.rb
|
@@ -190,7 +193,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
190
193
|
version: '0'
|
191
194
|
segments:
|
192
195
|
- 0
|
193
|
-
hash:
|
196
|
+
hash: -3806134538379917538
|
194
197
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
195
198
|
none: false
|
196
199
|
requirements:
|