backburner 0.1.1 → 0.1.2
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/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
|