backburner 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,10 +1,17 @@
1
1
  # CHANGELOG
2
2
 
3
- ## Released 0.1.1 (Nov 6th 2012)
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
- ## Released 0.1.0 (Nov 4th 2012)
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 = 120
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
- You can setup the error handler for jobs using configure:
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 beanstalk queue errors will show up on airbrake.
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
- Right now, all logging happens to standard out and can be piped to a file or any other output manually. More on logging coming later.
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
- - Front-end in sinatra for viewing beanstalk jobs
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
@@ -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.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'
@@ -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
@@ -13,7 +13,7 @@ module Backburner
13
13
  connect!
14
14
  end
15
15
 
16
- # Sets the delegator object to the underlying beanstalk connection
16
+ # Sets the delegator object to the underlying beaneater pool
17
17
  # self.put(...)
18
18
  def __getobj__
19
19
  __setobj__(@beanstalk)
@@ -68,13 +68,13 @@ module Backburner
68
68
  constant
69
69
  end
70
70
 
71
- # Returns tube_namespace for backburner
71
+ # Returns configuration options for backburner
72
72
  #
73
73
  # @example
74
- # tube_namespace => "some.namespace"
74
+ # config.max_job_retries => 3
75
75
  #
76
- def tube_namespace
77
- Backburner.configuration.tube_namespace
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
@@ -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
@@ -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(body)
10
- log_info [ "Working", body ].join(' ')
11
- @job_begun = Time.now
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, failed=false)
16
- ellapsed = Time.now - @job_begun
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 "Finished #{name} in #{ms}ms #{failed ? ' (failed)' : ''}"
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
- puts msg
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 msg
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
@@ -1,3 +1,3 @@
1
1
  module Backburner
2
- VERSION = "0.1.1"
2
+ VERSION = "0.1.2"
3
3
  end
@@ -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.class.log_job_begin(job.body)
96
+ self.log_job_begin(job.name, job.args)
97
97
  job.process
98
- self.class.log_job_end(job.name)
99
- rescue => e
100
- self.class.log_error self.class.exception_message(e)
101
- if job # bury failed job
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.class.log_job_end(job.name, 'failed') if @job_begun
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
 
@@ -44,13 +44,13 @@ describe "Backburner::Helpers module" do
44
44
  end
45
45
  end # exception_message
46
46
 
47
- describe "for tube_namespace" do
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 # tube_namespace
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.")) }
@@ -13,35 +13,50 @@ module NestedDemo
13
13
  end
14
14
 
15
15
  describe "Backburner::Job module" do
16
- describe "for initialize method with hash" do
17
- before do
18
- @task_body = { "class" => "NewsletterSender", "args" => ["foo@bar.com", "bar@foo.com"] }
19
- @task = stub(:body => @task_body, :ttr => 120, :delete => true, :bury => true)
20
- end
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
- it "should create job with correct task data" do
23
- @job = Backburner::Job.new(@task)
24
- assert_equal @task, @job.task
25
- assert_equal ["class", "args"], @job.body.keys
26
- assert_equal @task_body["class"], @job.name
27
- assert_equal @task_body["args"], @job.args
28
- end
29
- end # initialize with json
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
- describe "for initialize method with json string" do
32
- before do
33
- @task_body = { "class" => "NewsletterSender", "args" => ["foo@bar.com", "bar@foo.com"] }
34
- @task = stub(:body => @task_body.to_json, :ttr => 120, :delete => true, :bury => true)
35
- end
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
- it "should create job with correct task data" do
38
- @job = Backburner::Job.new(@task)
39
- assert_equal @task, @job.task
40
- assert_equal ["class", "args"], @job.body.keys
41
- assert_equal @task_body["class"], @job.name
42
- assert_equal @task_body["args"], @job.args
43
- end
44
- end # initialize with json
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 bury method" do
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 # bury
114
+ end # simple delegation
100
115
  end
@@ -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
@@ -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
@@ -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 { Backburner.default_queues.clear }
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
- it "should work an enqueued job" do
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
- $worker_test_count = 0
160
- Backburner::Worker.enqueue TestFailJob, [1, 2], :queue => "foo.bar.fail"
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.fail')
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
- $worker_test_count = 0
173
- TestAsyncJob.async(:queue => "bar.baz").foo(3, 5)
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.baz')
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.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-06 00:00:00.000000000 Z
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.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.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