backburner 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +9 -2
- data/README.md +38 -8
- data/TODO +4 -7
- data/backburner.gemspec +1 -1
- data/examples/retried.rb +31 -0
- data/lib/backburner/configuration.rb +6 -0
- data/lib/backburner/connection.rb +1 -1
- data/lib/backburner/helpers.rb +6 -6
- data/lib/backburner/job.rb +12 -6
- data/lib/backburner/logger.rb +19 -8
- data/lib/backburner/version.rb +1 -1
- data/lib/backburner/worker.rb +16 -8
- data/test/helpers_test.rb +4 -4
- data/test/job_test.rb +43 -28
- data/test/logger_test.rb +25 -0
- data/test/test_helper.rb +8 -0
- data/test/worker_test.rb +92 -9
- metadata +5 -4
data/CHANGELOG.md
CHANGED
@@ -1,10 +1,17 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
|
-
##
|
3
|
+
## Version 0.1.3 (Unreleased)
|
4
|
+
|
5
|
+
## Version 0.1.2 (Nov 7th 2012)
|
6
|
+
|
7
|
+
* Adds ability to specify a custom logger.
|
8
|
+
* Adds job retry configuration and worker support.
|
9
|
+
|
10
|
+
## Version 0.1.1 (Nov 6th 2012)
|
4
11
|
|
5
12
|
* Fix issue with timed out reserves
|
6
13
|
|
7
|
-
##
|
14
|
+
## Version 0.1.0 (Nov 4th 2012)
|
8
15
|
|
9
16
|
* Switch to beaneater as new ruby beanstalkd client
|
10
17
|
* Add support for array of connections in `beanstalk_url`
|
data/README.md
CHANGED
@@ -86,14 +86,20 @@ Backburner.configure do |config|
|
|
86
86
|
config.beanstalk_url = ["beanstalk://127.0.0.1", "beanstalk://127.0.0.1:11301"]
|
87
87
|
config.tube_namespace = "some.app.production"
|
88
88
|
config.on_error = lambda { |e| puts e }
|
89
|
+
config.max_job_retries = 3 # default 0 retries
|
90
|
+
config.retry_delay = 2 # default 5 seconds
|
89
91
|
config.default_priority = 65536
|
90
|
-
config.respond_timeout
|
92
|
+
config.respond_timeout = 120
|
93
|
+
config.logger = Logger.new(STDOUT)
|
91
94
|
end
|
92
95
|
```
|
93
96
|
|
94
|
-
The `beanstalk_url` supports a string such as 'beanstalk://127.0.0.1' or an array of addresses.
|
95
|
-
The `tube_namespace` is the prefix used for all tubes related to this backburner queue.
|
96
|
-
The `on_error` is a callback that gets invoked with the error whenever a job fails
|
97
|
+
* The `beanstalk_url` supports a string such as 'beanstalk://127.0.0.1' or an array of addresses.
|
98
|
+
* The `tube_namespace` is the prefix used for all tubes related to this backburner queue.
|
99
|
+
* The `on_error` is a callback that gets invoked with the error whenever a job fails.
|
100
|
+
* The `max_job_retries` determines how many times to retry a job before burying
|
101
|
+
* The `retry_delay` determines the base time to wait (in secs) between retries
|
102
|
+
* The `logger` is the logger object written to when backburner wants to report info or errors.
|
97
103
|
|
98
104
|
## Usage
|
99
105
|
|
@@ -227,7 +233,20 @@ The `default_queues` stores the specific list of queues that should be processed
|
|
227
233
|
|
228
234
|
### Failures
|
229
235
|
|
230
|
-
|
236
|
+
When a job fails in backburner (usually because an exception was raised), the job will be released
|
237
|
+
and retried again (with progressive delays in between) until the `max_job_retries` configuration is reached.
|
238
|
+
|
239
|
+
```ruby
|
240
|
+
Backburner.configure do |config|
|
241
|
+
config.max_job_retries = 3 # retry jobs 3 times
|
242
|
+
config.retry_delay = 2 # wait 2 seconds in between retries
|
243
|
+
end
|
244
|
+
```
|
245
|
+
|
246
|
+
Note the default `max_job_retries` is 0, meaning that by default **jobs are not retried**.
|
247
|
+
If continued retry attempts fail, the job will be buried and can be 'kicked' later for inspection.
|
248
|
+
|
249
|
+
You can also setup a custom error handler for jobs using configure:
|
231
250
|
|
232
251
|
```ruby
|
233
252
|
Backburner.configure do |config|
|
@@ -235,12 +254,23 @@ Backburner.configure do |config|
|
|
235
254
|
end
|
236
255
|
```
|
237
256
|
|
238
|
-
Now all
|
239
|
-
If a job fails in beanstalk, the job is automatically buried and must be 'kicked' later.
|
257
|
+
Now all backburner queue errors will appear on airbrake for deeper inspection.
|
240
258
|
|
241
259
|
### Logging
|
242
260
|
|
243
|
-
|
261
|
+
Logging in backburner is rather simple. When a job is run, the log records that. When a job
|
262
|
+
fails, the log records that. When any exceptions occur during processing, the log records that.
|
263
|
+
|
264
|
+
By default, the log will print to standard out. You can customize the log to output to any
|
265
|
+
standard logger by controlling the configuration option:
|
266
|
+
|
267
|
+
```ruby
|
268
|
+
Backburner.configure do |config|
|
269
|
+
config.logger = Logger.new(STDOUT)
|
270
|
+
end
|
271
|
+
```
|
272
|
+
|
273
|
+
Be sure to check logs whenever things do not seem to be processing.
|
244
274
|
|
245
275
|
### Web Front-end
|
246
276
|
|
data/TODO
CHANGED
@@ -1,9 +1,6 @@
|
|
1
|
-
-
|
2
|
-
- https://github.com/denniskuczynski/beanstalkd_view
|
3
|
-
- Better logging
|
4
|
-
- https://github.com/dpree/stalker/commit/70e94b7b445e08166bf2288dd1b7853396b26a86
|
5
|
-
- Better error handling (failures, retries)
|
6
|
-
- https://github.com/michaeldwan/stalker/commit/afd7e94d20991bcd96b65eda7c979d92d8746c3b
|
7
|
-
- https://github.com/michaeldwan/stalker/blob/master/lib/stalker/worker.rb
|
1
|
+
- Custom front-end in sinatra for viewing beanstalk jobs
|
2
|
+
- Refer to https://github.com/denniskuczynski/beanstalkd_view
|
8
3
|
- Fork jobs to control memory
|
4
|
+
- https://github.com/michaeldwan/stalker/commit/386267690a7c03e11d1a8b7b6f08b7c9c7cd2c0d
|
5
|
+
- Add hooks
|
9
6
|
- https://github.com/michaeldwan/stalker/commit/386267690a7c03e11d1a8b7b6f08b7c9c7cd2c0d
|
data/backburner.gemspec
CHANGED
@@ -15,7 +15,7 @@ Gem::Specification.new do |s|
|
|
15
15
|
s.require_paths = ["lib"]
|
16
16
|
s.version = Backburner::VERSION
|
17
17
|
|
18
|
-
s.add_runtime_dependency 'beaneater', '~> 0.1.
|
18
|
+
s.add_runtime_dependency 'beaneater', '~> 0.1.2'
|
19
19
|
s.add_runtime_dependency 'dante', '~> 0.1.5'
|
20
20
|
|
21
21
|
s.add_development_dependency 'rake'
|
data/examples/retried.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
$:.unshift "lib"
|
2
|
+
require 'backburner'
|
3
|
+
|
4
|
+
$error = 0
|
5
|
+
|
6
|
+
class User
|
7
|
+
include Backburner::Performable
|
8
|
+
attr_accessor :id, :name
|
9
|
+
|
10
|
+
def self.foo(x, y)
|
11
|
+
$error += 1
|
12
|
+
raise "fail #{$error}" unless $error > 3
|
13
|
+
puts "User #foo args [#{x}, #{y}] Success!!"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Configure Backburner
|
18
|
+
Backburner.configure do |config|
|
19
|
+
config.beanstalk_url = "beanstalk://127.0.0.1"
|
20
|
+
config.tube_namespace = "demo.production"
|
21
|
+
config.on_error = lambda { |e| puts "HEY!!! #{e.class}" }
|
22
|
+
config.max_job_retries = 3
|
23
|
+
config.retry_delay = 0
|
24
|
+
end
|
25
|
+
|
26
|
+
# Enqueue tasks
|
27
|
+
User.async(:queue => "retried").foo("bar", "baz")
|
28
|
+
|
29
|
+
# Run work
|
30
|
+
# Backburner.default_queues << "user"
|
31
|
+
Backburner.work
|
@@ -5,7 +5,10 @@ module Backburner
|
|
5
5
|
attr_accessor :default_priority # default job priority
|
6
6
|
attr_accessor :respond_timeout # default job timeout
|
7
7
|
attr_accessor :on_error # error handler
|
8
|
+
attr_accessor :max_job_retries # max job retries
|
9
|
+
attr_accessor :retry_delay # retry delay in seconds
|
8
10
|
attr_accessor :default_queues # default queues
|
11
|
+
attr_accessor :logger # logger
|
9
12
|
|
10
13
|
def initialize
|
11
14
|
@beanstalk_url = "beanstalk://localhost"
|
@@ -13,7 +16,10 @@ module Backburner
|
|
13
16
|
@default_priority = 65536
|
14
17
|
@respond_timeout = 120
|
15
18
|
@on_error = nil
|
19
|
+
@max_job_retries = 0
|
20
|
+
@retry_delay = 5
|
16
21
|
@default_queues = []
|
22
|
+
@logger = nil
|
17
23
|
end
|
18
24
|
end # Configuration
|
19
25
|
end # Backburner
|
data/lib/backburner/helpers.rb
CHANGED
@@ -68,13 +68,13 @@ module Backburner
|
|
68
68
|
constant
|
69
69
|
end
|
70
70
|
|
71
|
-
# Returns
|
71
|
+
# Returns configuration options for backburner
|
72
72
|
#
|
73
73
|
# @example
|
74
|
-
#
|
74
|
+
# config.max_job_retries => 3
|
75
75
|
#
|
76
|
-
def
|
77
|
-
Backburner.configuration
|
76
|
+
def config
|
77
|
+
Backburner.configuration
|
78
78
|
end
|
79
79
|
|
80
80
|
# Expands a tube to include the prefix
|
@@ -84,7 +84,7 @@ module Backburner
|
|
84
84
|
# expand_tube_name(FooJob) # => <prefix>.foo-job
|
85
85
|
#
|
86
86
|
def expand_tube_name(tube)
|
87
|
-
prefix = tube_namespace
|
87
|
+
prefix = config.tube_namespace
|
88
88
|
queue_name = if tube.is_a?(String)
|
89
89
|
tube
|
90
90
|
elsif tube.respond_to?(:queue) # use queue name
|
@@ -94,7 +94,7 @@ module Backburner
|
|
94
94
|
else # turn into a string
|
95
95
|
tube.to_s
|
96
96
|
end
|
97
|
-
[prefix.gsub(/\.$/, ''), dasherize(queue_name).gsub(/^#{prefix}/, '')].join(".")
|
97
|
+
[prefix.gsub(/\.$/, ''), dasherize(queue_name).gsub(/^#{prefix}/, '')].join(".").gsub(/\.+/, '.')
|
98
98
|
end
|
99
99
|
|
100
100
|
end
|
data/lib/backburner/job.rb
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
module Backburner
|
2
2
|
# A single backburner job which can be processed and removed by the worker
|
3
|
-
class Job
|
3
|
+
class Job < SimpleDelegator
|
4
4
|
include Backburner::Helpers
|
5
5
|
|
6
6
|
# Raises when a job times out
|
7
7
|
class JobTimeout < RuntimeError; end
|
8
8
|
class JobNotFound < RuntimeError; end
|
9
|
+
class JobFormatInvalid < RuntimeError; end
|
9
10
|
|
10
11
|
attr_accessor :task, :body, :name, :args
|
11
12
|
|
@@ -21,6 +22,16 @@ module Backburner
|
|
21
22
|
@task = task
|
22
23
|
@body = task.body.is_a?(Hash) ? task.body : JSON.parse(task.body)
|
23
24
|
@name, @args = body["class"], body["args"]
|
25
|
+
rescue => ex # Job was not valid format
|
26
|
+
self.bury
|
27
|
+
raise JobFormatInvalid, "Job body could not be parsed: #{ex.inspect}"
|
28
|
+
end
|
29
|
+
|
30
|
+
# Sets the delegator object to the underlying beaneater job
|
31
|
+
# self.bury
|
32
|
+
def __getobj__
|
33
|
+
__setobj__(@task)
|
34
|
+
super
|
24
35
|
end
|
25
36
|
|
26
37
|
# Processes a job and handles any failure, deleting the job once complete
|
@@ -33,11 +44,6 @@ module Backburner
|
|
33
44
|
task.delete
|
34
45
|
end
|
35
46
|
|
36
|
-
# Bury a job out of the active queue if that job fails
|
37
|
-
def bury
|
38
|
-
task.bury
|
39
|
-
end
|
40
|
-
|
41
47
|
protected
|
42
48
|
|
43
49
|
# Returns the class for the job handler
|
data/lib/backburner/logger.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
1
3
|
module Backburner
|
2
4
|
module Logger
|
3
5
|
# Loads in instance and class levels
|
@@ -6,16 +8,21 @@ module Backburner
|
|
6
8
|
end
|
7
9
|
|
8
10
|
# Print out when a job is about to begin
|
9
|
-
def log_job_begin(
|
10
|
-
log_info
|
11
|
-
@
|
11
|
+
def log_job_begin(name, args)
|
12
|
+
log_info "Work job #{name} with #{args.inspect}"
|
13
|
+
@job_started_at = Time.now
|
12
14
|
end
|
13
15
|
|
14
16
|
# Print out when a job completed
|
15
|
-
def log_job_end(name,
|
16
|
-
ellapsed = Time.now -
|
17
|
+
def log_job_end(name, message = nil)
|
18
|
+
ellapsed = Time.now - job_started_at
|
17
19
|
ms = (ellapsed.to_f * 1000).to_i
|
18
|
-
log_info
|
20
|
+
log_info("Finished #{name} in #{ms}ms #{message}")
|
21
|
+
end
|
22
|
+
|
23
|
+
# Returns true if the job logging started
|
24
|
+
def job_started_at
|
25
|
+
@job_started_at
|
19
26
|
end
|
20
27
|
|
21
28
|
# Print a message to stdout
|
@@ -24,7 +31,7 @@ module Backburner
|
|
24
31
|
# log_info("Working on task")
|
25
32
|
#
|
26
33
|
def log_info(msg)
|
27
|
-
|
34
|
+
logger ? logger.info(msg) : puts(msg)
|
28
35
|
end
|
29
36
|
|
30
37
|
# Print an error to stderr
|
@@ -33,8 +40,12 @@ module Backburner
|
|
33
40
|
# log_error("Task failed!")
|
34
41
|
#
|
35
42
|
def log_error(msg)
|
36
|
-
$stderr.puts
|
43
|
+
logger ? logger.error(msg) : $stderr.puts(msg)
|
37
44
|
end
|
38
45
|
|
46
|
+
# Return logger if specified
|
47
|
+
def logger
|
48
|
+
Backburner.configuration.logger
|
49
|
+
end
|
39
50
|
end
|
40
51
|
end
|
data/lib/backburner/version.rb
CHANGED
data/lib/backburner/worker.rb
CHANGED
@@ -93,16 +93,24 @@ module Backburner
|
|
93
93
|
#
|
94
94
|
def work_one_job
|
95
95
|
job = Backburner::Job.new(self.connection.tubes.reserve)
|
96
|
-
self.
|
96
|
+
self.log_job_begin(job.name, job.args)
|
97
97
|
job.process
|
98
|
-
self.
|
99
|
-
rescue => e
|
100
|
-
self.
|
101
|
-
|
98
|
+
self.log_job_end(job.name)
|
99
|
+
rescue Backburner::Job::JobFormatInvalid => e
|
100
|
+
self.log_error self.exception_message(e)
|
101
|
+
rescue => e # Error occurred processing job
|
102
|
+
self.log_error self.exception_message(e)
|
103
|
+
num_retries = job.stats.releases
|
104
|
+
retry_status = "failed: attempt #{num_retries+1} of #{config.max_job_retries+1}"
|
105
|
+
if num_retries < config.max_job_retries # retry again
|
106
|
+
delay = config.retry_delay + num_retries ** 3
|
107
|
+
job.release(:delay => delay)
|
108
|
+
self.log_job_end(job.name, "#{retry_status}, retrying in #{delay}s") if job_started_at
|
109
|
+
else # retries failed, bury
|
102
110
|
job.bury
|
103
|
-
self.
|
104
|
-
handle_error(e, job.name, job.args)
|
111
|
+
self.log_job_end(job.name, "#{retry_status}, burying") if job_started_at
|
105
112
|
end
|
113
|
+
handle_error(e, job.name, job.args)
|
106
114
|
end
|
107
115
|
|
108
116
|
protected
|
@@ -111,7 +119,7 @@ module Backburner
|
|
111
119
|
# Filtered for tubes that match the known prefix
|
112
120
|
def all_existing_queues
|
113
121
|
known_queues = Backburner::Worker.known_queue_classes.map(&:queue)
|
114
|
-
existing_tubes = self.connection.tubes.all.map(&:name).select { |tube| tube =~ /^#{tube_namespace}/ }
|
122
|
+
existing_tubes = self.connection.tubes.all.map(&:name).select { |tube| tube =~ /^#{config.tube_namespace}/ }
|
115
123
|
known_queues + existing_tubes
|
116
124
|
end
|
117
125
|
|
data/test/helpers_test.rb
CHANGED
@@ -44,13 +44,13 @@ describe "Backburner::Helpers module" do
|
|
44
44
|
end
|
45
45
|
end # exception_message
|
46
46
|
|
47
|
-
describe "for
|
47
|
+
describe "for config" do
|
48
48
|
before { Backburner.expects(:configuration).returns(stub(:tube_namespace => "test.foo.job")) }
|
49
49
|
|
50
|
-
it "accesses correct value" do
|
51
|
-
assert_equal "test.foo.job", tube_namespace
|
50
|
+
it "accesses correct value for namespace" do
|
51
|
+
assert_equal "test.foo.job", config.tube_namespace
|
52
52
|
end
|
53
|
-
end #
|
53
|
+
end # config
|
54
54
|
|
55
55
|
describe "for expand_tube_name method" do
|
56
56
|
before { Backburner.expects(:configuration).returns(stub(:tube_namespace => "test.foo.job.")) }
|
data/test/job_test.rb
CHANGED
@@ -13,35 +13,50 @@ module NestedDemo
|
|
13
13
|
end
|
14
14
|
|
15
15
|
describe "Backburner::Job module" do
|
16
|
-
describe "for initialize
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
16
|
+
describe "for initialize" do
|
17
|
+
describe "with hash" do
|
18
|
+
before do
|
19
|
+
@task_body = { "class" => "NewsletterSender", "args" => ["foo@bar.com", "bar@foo.com"] }
|
20
|
+
@task = stub(:body => @task_body, :ttr => 120, :delete => true, :bury => true)
|
21
|
+
end
|
21
22
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
23
|
+
it "should create job with correct task data" do
|
24
|
+
@job = Backburner::Job.new(@task)
|
25
|
+
assert_equal @task, @job.task
|
26
|
+
assert_equal ["class", "args"], @job.body.keys
|
27
|
+
assert_equal @task_body["class"], @job.name
|
28
|
+
assert_equal @task_body["args"], @job.args
|
29
|
+
end
|
30
|
+
end # with hash
|
30
31
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
32
|
+
describe "with json string" do
|
33
|
+
before do
|
34
|
+
@task_body = { "class" => "NewsletterSender", "args" => ["foo@bar.com", "bar@foo.com"] }
|
35
|
+
@task = stub(:body => @task_body.to_json, :ttr => 120, :delete => true, :bury => true)
|
36
|
+
end
|
36
37
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
38
|
+
it "should create job with correct task data" do
|
39
|
+
@job = Backburner::Job.new(@task)
|
40
|
+
assert_equal @task, @job.task
|
41
|
+
assert_equal ["class", "args"], @job.body.keys
|
42
|
+
assert_equal @task_body["class"], @job.name
|
43
|
+
assert_equal @task_body["args"], @job.args
|
44
|
+
end
|
45
|
+
end # with json
|
46
|
+
|
47
|
+
describe "with invalid string" do
|
48
|
+
before do
|
49
|
+
@task_body = "^%$*&^*"
|
50
|
+
@task = stub(:body => @task_body, :ttr => 120, :delete => true, :bury => true)
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should raise a job format exception" do
|
54
|
+
assert_raises(Backburner::Job::JobFormatInvalid) {
|
55
|
+
@job = Backburner::Job.new(@task)
|
56
|
+
}
|
57
|
+
end
|
58
|
+
end # invalid
|
59
|
+
end # initialize
|
45
60
|
|
46
61
|
describe "for process method" do
|
47
62
|
describe "with valid task" do
|
@@ -85,7 +100,7 @@ describe "Backburner::Job module" do
|
|
85
100
|
end # invalid
|
86
101
|
end # process
|
87
102
|
|
88
|
-
describe "for
|
103
|
+
describe "for simple delegation method" do
|
89
104
|
before do
|
90
105
|
@task_body = { "class" => "NestedDemo::TestJobC", "args" => [56] }
|
91
106
|
@task = stub(:body => @task_body, :ttr => 120, :delete => true, :bury => true)
|
@@ -96,5 +111,5 @@ describe "Backburner::Job module" do
|
|
96
111
|
@job = Backburner::Job.new(@task)
|
97
112
|
@job.bury
|
98
113
|
end # bury
|
99
|
-
end #
|
114
|
+
end # simple delegation
|
100
115
|
end
|
data/test/logger_test.rb
CHANGED
@@ -3,11 +3,26 @@ require File.expand_path('../test_helper', __FILE__)
|
|
3
3
|
describe "Backburner::Logger module" do
|
4
4
|
include Backburner::Logger
|
5
5
|
|
6
|
+
before do
|
7
|
+
@strio = StringIO.new
|
8
|
+
@logger = Logger.new(@strio)
|
9
|
+
end
|
10
|
+
|
6
11
|
describe "for log_info method" do
|
7
12
|
it "prints out to std out" do
|
8
13
|
output = capture_stdout { log_info("foo") }
|
9
14
|
assert_equal "foo\n", output
|
10
15
|
end
|
16
|
+
|
17
|
+
it "can be configured to log to logger" do
|
18
|
+
Backburner.configure { |config| config.logger = @logger }
|
19
|
+
log_info("foo")
|
20
|
+
assert_match /I,.*?foo/, @strio.string
|
21
|
+
end
|
22
|
+
|
23
|
+
after do
|
24
|
+
Backburner.configure { |config| config.logger = nil }
|
25
|
+
end
|
11
26
|
end # log_info
|
12
27
|
|
13
28
|
describe "for log_error method" do
|
@@ -15,5 +30,15 @@ describe "Backburner::Logger module" do
|
|
15
30
|
output = capture_stdout { log_error("bar") }
|
16
31
|
assert_equal "bar\n", output
|
17
32
|
end
|
33
|
+
|
34
|
+
it "can be configured to log to logger" do
|
35
|
+
Backburner.configure { |config| config.logger = @logger }
|
36
|
+
log_error("bar")
|
37
|
+
assert_match /E,.*?bar/, @strio.string
|
38
|
+
end
|
39
|
+
|
40
|
+
after do
|
41
|
+
Backburner.configure { |config| config.logger = nil }
|
42
|
+
end
|
18
43
|
end # log_error
|
19
44
|
end
|
data/test/test_helper.rb
CHANGED
@@ -90,4 +90,12 @@ class MiniTest::Spec
|
|
90
90
|
silenced(3) { @res = connection.tubes.reserve }
|
91
91
|
return @res, @res.body
|
92
92
|
end
|
93
|
+
|
94
|
+
# clear_jobs!('foo')
|
95
|
+
def clear_jobs!(*tube_names)
|
96
|
+
tube_names.each do |tube_name|
|
97
|
+
expanded_name = [Backburner.configuration.tube_namespace, tube_name].join(".")
|
98
|
+
Backburner::Worker.connection.tubes.find(expanded_name).clear
|
99
|
+
end
|
100
|
+
end
|
93
101
|
end # MiniTest::Spec
|
data/test/worker_test.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require File.expand_path('../test_helper', __FILE__)
|
2
2
|
|
3
3
|
$worker_test_count = 0
|
4
|
+
$worker_success = false
|
4
5
|
|
5
6
|
class TestJob
|
6
7
|
include Backburner::Queue
|
@@ -13,13 +14,24 @@ class TestFailJob
|
|
13
14
|
def self.perform(x, y); raise RuntimeError; end
|
14
15
|
end
|
15
16
|
|
17
|
+
class TestRetryJob
|
18
|
+
include Backburner::Queue
|
19
|
+
def self.perform(x, y)
|
20
|
+
$worker_test_count += 1
|
21
|
+
raise RuntimeError unless $worker_test_count > 2
|
22
|
+
$worker_success = true
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
16
26
|
class TestAsyncJob
|
17
27
|
include Backburner::Performable
|
18
28
|
def self.foo(x, y); $worker_test_count = x * y; end
|
19
29
|
end
|
20
30
|
|
21
31
|
describe "Backburner::Worker module" do
|
22
|
-
before
|
32
|
+
before do
|
33
|
+
Backburner.default_queues.clear
|
34
|
+
end
|
23
35
|
|
24
36
|
describe "for enqueue class method" do
|
25
37
|
it "should support enqueuing job" do
|
@@ -144,8 +156,13 @@ describe "Backburner::Worker module" do
|
|
144
156
|
end # prepare
|
145
157
|
|
146
158
|
describe "for work_one_job method" do
|
147
|
-
|
159
|
+
before do
|
148
160
|
$worker_test_count = 0
|
161
|
+
$worker_success = false
|
162
|
+
end
|
163
|
+
|
164
|
+
it "should work an enqueued job" do
|
165
|
+
clear_jobs!("foo.bar")
|
149
166
|
Backburner::Worker.enqueue TestJob, [1, 2], :queue => "foo.bar"
|
150
167
|
silenced(2) do
|
151
168
|
worker = Backburner::Worker.new('foo.bar')
|
@@ -155,28 +172,94 @@ describe "Backburner::Worker module" do
|
|
155
172
|
assert_equal 3, $worker_test_count
|
156
173
|
end # enqueue
|
157
174
|
|
175
|
+
it "should fail quietly if there's an argument error" do
|
176
|
+
clear_jobs!("foo.bar")
|
177
|
+
Backburner::Worker.enqueue TestJob, ["bam", "foo", "bar"], :queue => "foo.bar"
|
178
|
+
out = silenced(2) do
|
179
|
+
worker = Backburner::Worker.new('foo.bar')
|
180
|
+
worker.prepare
|
181
|
+
worker.work_one_job
|
182
|
+
end
|
183
|
+
assert_match(/Exception ArgumentError/, out)
|
184
|
+
assert_equal 0, $worker_test_count
|
185
|
+
end # fail, argument
|
186
|
+
|
158
187
|
it "should work an enqueued failing job" do
|
159
|
-
|
160
|
-
Backburner::Worker.enqueue TestFailJob, [1, 2], :queue => "foo.bar
|
188
|
+
clear_jobs!("foo.bar")
|
189
|
+
Backburner::Worker.enqueue TestFailJob, [1, 2], :queue => "foo.bar"
|
161
190
|
Backburner::Job.any_instance.expects(:bury).once
|
162
191
|
out = silenced(2) do
|
163
|
-
worker = Backburner::Worker.new('foo.bar
|
192
|
+
worker = Backburner::Worker.new('foo.bar')
|
164
193
|
worker.prepare
|
165
194
|
worker.work_one_job
|
166
195
|
end
|
167
196
|
assert_match(/Exception RuntimeError/, out)
|
168
197
|
assert_equal 0, $worker_test_count
|
169
|
-
end # fail
|
198
|
+
end # fail, runtime error
|
199
|
+
|
200
|
+
it "should work an invalid job parsed" do
|
201
|
+
Beaneater::Tubes.any_instance.expects(:reserve).returns(stub(:body => "{%$^}", :bury => true))
|
202
|
+
out = silenced(2) do
|
203
|
+
worker = Backburner::Worker.new('foo.bar')
|
204
|
+
worker.prepare
|
205
|
+
worker.work_one_job
|
206
|
+
end
|
207
|
+
assert_match(/Exception Backburner::Job::JobFormatInvalid/, out)
|
208
|
+
assert_equal 0, $worker_test_count
|
209
|
+
end # fail, runtime error
|
170
210
|
|
171
211
|
it "should work for an async job" do
|
172
|
-
|
173
|
-
TestAsyncJob.async(:queue =>
|
212
|
+
clear_jobs!('foo.bar')
|
213
|
+
TestAsyncJob.async(:queue => 'foo.bar').foo(3, 5)
|
174
214
|
silenced(2) do
|
175
|
-
worker = Backburner::Worker.new('bar
|
215
|
+
worker = Backburner::Worker.new('foo.bar')
|
176
216
|
worker.prepare
|
177
217
|
worker.work_one_job
|
178
218
|
end
|
179
219
|
assert_equal 15, $worker_test_count
|
180
220
|
end # async
|
221
|
+
|
222
|
+
it "should support retrying jobs and burying" do
|
223
|
+
clear_jobs!('foo.bar')
|
224
|
+
Backburner.configure { |config| config.max_job_retries = 1; config.retry_delay = 0 }
|
225
|
+
Backburner::Worker.enqueue TestRetryJob, ["bam", "foo"], :queue => 'foo.bar'
|
226
|
+
out = []
|
227
|
+
2.times do
|
228
|
+
out << silenced(2) do
|
229
|
+
worker = Backburner::Worker.new('foo.bar')
|
230
|
+
worker.prepare
|
231
|
+
worker.work_one_job
|
232
|
+
end
|
233
|
+
end
|
234
|
+
assert_match /attempt 1 of 2, retrying/, out.first
|
235
|
+
assert_match /Finished TestRetryJob/m, out.last
|
236
|
+
assert_match /attempt 2 of 2, burying/m, out.last
|
237
|
+
assert_equal 2, $worker_test_count
|
238
|
+
assert_equal false, $worker_success
|
239
|
+
end # retry, bury
|
240
|
+
|
241
|
+
it "should support retrying jobs and succeeds" do
|
242
|
+
clear_jobs!('foo.bar')
|
243
|
+
Backburner.configure { |config| config.max_job_retries = 2; config.retry_delay = 0 }
|
244
|
+
Backburner::Worker.enqueue TestRetryJob, ["bam", "foo"], :queue => 'foo.bar'
|
245
|
+
out = []
|
246
|
+
3.times do
|
247
|
+
out << silenced(2) do
|
248
|
+
worker = Backburner::Worker.new('foo.bar')
|
249
|
+
worker.prepare
|
250
|
+
worker.work_one_job
|
251
|
+
end
|
252
|
+
end
|
253
|
+
assert_match /attempt 1 of 3, retrying/, out.first
|
254
|
+
assert_match /attempt 2 of 3, retrying/, out[1]
|
255
|
+
assert_match /Finished TestRetryJob/m, out.last
|
256
|
+
refute_match(/failed/, out.last)
|
257
|
+
assert_equal 3, $worker_test_count
|
258
|
+
assert_equal true, $worker_success
|
259
|
+
end # retrying, succeeds
|
260
|
+
|
261
|
+
after do
|
262
|
+
Backburner.configure { |config| config.max_job_retries = 0; config.retry_delay = 5 }
|
263
|
+
end
|
181
264
|
end # work_one_job
|
182
265
|
end # Backburner::Worker
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: backburner
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
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: 2012-11-
|
12
|
+
date: 2012-11-07 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: beaneater
|
@@ -18,7 +18,7 @@ dependencies:
|
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
20
20
|
- !ruby/object:Gem::Version
|
21
|
-
version: 0.1.
|
21
|
+
version: 0.1.2
|
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.1.
|
29
|
+
version: 0.1.2
|
30
30
|
- !ruby/object:Gem::Dependency
|
31
31
|
name: dante
|
32
32
|
requirement: !ruby/object:Gem::Requirement
|
@@ -112,6 +112,7 @@ files:
|
|
112
112
|
- examples/custom.rb
|
113
113
|
- examples/demo.rb
|
114
114
|
- examples/god.rb
|
115
|
+
- examples/retried.rb
|
115
116
|
- examples/simple.rb
|
116
117
|
- lib/backburner.rb
|
117
118
|
- lib/backburner/async_proxy.rb
|