backburner 1.1.0 → 1.2.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/HOOKS.md +16 -5
- data/README.md +8 -6
- data/examples/demo.rb +2 -2
- data/lib/backburner/configuration.rb +1 -1
- data/lib/backburner/connection.rb +104 -10
- data/lib/backburner/hooks.rb +11 -11
- data/lib/backburner/job.rb +6 -1
- data/lib/backburner/version.rb +1 -1
- data/lib/backburner/worker.rb +42 -59
- data/lib/backburner/workers/forking.rb +5 -1
- data/lib/backburner/workers/simple.rb +1 -1
- data/lib/backburner/workers/threads_on_fork.rb +19 -19
- data/test/async_proxy_test.rb +15 -13
- data/test/connection_test.rb +143 -7
- data/test/fixtures/hooked.rb +6 -0
- data/test/fixtures/test_fork_jobs.rb +13 -1
- data/test/helpers/templogger.rb +1 -1
- data/test/hooks_test.rb +7 -0
- data/test/job_test.rb +33 -0
- data/test/test_helper.rb +30 -8
- data/test/worker_test.rb +41 -41
- data/test/workers/forking_worker_test.rb +10 -13
- data/test/workers/simple_worker_test.rb +23 -10
- data/test/workers/threads_on_fork_worker_test.rb +20 -16
- metadata +16 -17
- data/.DS_Store +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 47ef7389bf1b31ea5b36b286dbcde88b8df5ef6b
|
4
|
+
data.tar.gz: d218e32791da8764998ad6ca58a7bc1de0d35ae6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1f8d54cfe41edbc49995342663ee6a5a23975cca9717e72d2cc8855a2634764a635f8a766bb511044c2ea0fee196c6adb817cafd2773ef945fd78c041f23bb15
|
7
|
+
data.tar.gz: 0fd38fbe520aa7efc312eb468f4dda7c452c53e1f6936667e1896abf4f1b9b212775c825d2bb9b40c6a7868a586ee886720f36abc359d8c64bd5d322911457fa
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
|
+
## Version 1.2.0.pre (October 24 2015)
|
4
|
+
|
5
|
+
* FIX Replace static Beaneater connection with individual connections per worker instance/thread (@contentfree)
|
6
|
+
* FIX Beaneater connections try really hard to repair themselves if disconnected accidentally (@contentfree)
|
7
|
+
* NEW Event hook for workers: on_reconnect (@contentfree)
|
8
|
+
|
3
9
|
## Version 1.1.0 (September 14 2015)
|
4
10
|
|
5
11
|
* NEW Ability to configure namespace separator (@bfolkens)
|
data/HOOKS.md
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
# Backburner Hooks
|
2
2
|
|
3
|
-
You can customize Backburner or write plugins using its hook API.
|
3
|
+
You can customize Backburner or write plugins using its hook API.
|
4
4
|
In many cases you can use a hook rather than mess around with Backburner's internals.
|
5
5
|
|
6
|
-
## Job Hooks
|
6
|
+
## Job Hooks
|
7
7
|
|
8
8
|
Hooks are transparently adapted from [Resque](https://github.com/defunkt/resque/blob/master/docs/HOOKS.md), so
|
9
9
|
if you are familiar with their hook API, now you can use nearly the same ones with beanstalkd and backburner!
|
@@ -26,7 +26,7 @@ There are a variety of hooks available that are triggered during the lifecycle o
|
|
26
26
|
to perform the job (but is not required to do so). It may handle exceptions
|
27
27
|
thrown by perform, but uncaught exceptions will be treated like regular job exceptions.
|
28
28
|
|
29
|
-
* `on_retry`: Called with the retry count, the delay and the job args whenever a job is retried.
|
29
|
+
* `on_retry`: Called with the retry count, the delay and the job args whenever a job is retried.
|
30
30
|
|
31
31
|
* `on_bury`: Called with the job args when the job is buried.
|
32
32
|
|
@@ -49,7 +49,7 @@ class SomeJob
|
|
49
49
|
def self.perform(*args)
|
50
50
|
# ...
|
51
51
|
end
|
52
|
-
|
52
|
+
|
53
53
|
def self.logger
|
54
54
|
@_logger ||= Logger.new(STDOUT)
|
55
55
|
end
|
@@ -60,5 +60,16 @@ You can also setup modules to create compose-able and reusable hooks for your jo
|
|
60
60
|
|
61
61
|
## Worker Hooks
|
62
62
|
|
63
|
-
|
63
|
+
Currently, there is just one hook:
|
64
|
+
|
65
|
+
* `on_reconnect`: Called on the worker whose connection has been reset. The connection
|
66
|
+
is given as the argument
|
67
|
+
|
68
|
+
An example:
|
64
69
|
|
70
|
+
```ruby
|
71
|
+
class MyWorker < Backburner::Worker
|
72
|
+
def on_reconnect(conn)
|
73
|
+
prepare
|
74
|
+
end
|
75
|
+
end
|
data/README.md
CHANGED
@@ -113,10 +113,12 @@ The key options available are:
|
|
113
113
|
| `tube_namespace` | Prefix used for all tubes related to this backburner queue. |
|
114
114
|
| `namespace_separator` | Separator used for namespace and queue name |
|
115
115
|
| `on_error` | Lambda invoked with the error whenever any job in the system fails. |
|
116
|
-
| `default_worker` | Worker class that will be used if no other worker is specified. |
|
117
116
|
| `max_job_retries` | Integer defines how many times to retry a job before burying. |
|
118
117
|
| `retry_delay` | Integer defines the base time to wait (in secs) between job retries. |
|
119
118
|
| `retry_delay_proc` | Lambda calculates the delay used, allowing for exponential back-off. |
|
119
|
+
| `default_priority` | Integer The default priority of jobs |
|
120
|
+
| `respond_timeout` | Integer defines how long a job has to complete its task |
|
121
|
+
| `default_worker` | Worker class that will be used if no other worker is specified. |
|
120
122
|
| `logger` | Logger recorded to when backburner wants to report info or errors. |
|
121
123
|
| `primary_queue` | Primary queue used for a job when an alternate queue is not given. |
|
122
124
|
| `priority_labels` | Hash of named priority definitions for your app. |
|
@@ -158,7 +160,7 @@ class NewsletterJob
|
|
158
160
|
|
159
161
|
# optional, defaults to respond_timeout
|
160
162
|
def self.queue_respond_timeout
|
161
|
-
300 # number of seconds before job times out, 0 to avoid timeout
|
163
|
+
300 # number of seconds before job times out, 0 to avoid timeout. NB: A timeout of 1 second will likely lead to race conditions between Backburner and beanstalkd and should be avoided
|
162
164
|
end
|
163
165
|
end
|
164
166
|
```
|
@@ -238,7 +240,7 @@ User.async(:queue => lambda { |user_klass| ["queue1","queue2"].sample(1).first }
|
|
238
240
|
|
239
241
|
### Using Async Asynchronously ###
|
240
242
|
|
241
|
-
It's often useful to be able to configure your app in production such that every invocation of a method is asynchronous by default as seen in [delayed_job](https://github.com/collectiveidea/delayed_job#queuing-jobs). To accomplish this, the `Backburner::Performable` module exposes two `handle_asynchronously` convenience methods
|
243
|
+
It's often useful to be able to configure your app in production such that every invocation of a method is asynchronous by default as seen in [delayed_job](https://github.com/collectiveidea/delayed_job#queuing-jobs). To accomplish this, the `Backburner::Performable` module exposes two `handle_asynchronously` convenience methods
|
242
244
|
which accept the same options as the `async` method:
|
243
245
|
|
244
246
|
```ruby
|
@@ -248,14 +250,14 @@ class User
|
|
248
250
|
def send_welcome_email
|
249
251
|
# ...
|
250
252
|
end
|
251
|
-
|
253
|
+
|
252
254
|
# ---> For instance methods
|
253
255
|
handle_asynchronously :send_welcome_email, queue: 'send-mail', pri: 5000, ttr: 60
|
254
256
|
|
255
257
|
def self.update_recent_visitors
|
256
258
|
# ...
|
257
259
|
end
|
258
|
-
|
260
|
+
|
259
261
|
# ---> For class methods
|
260
262
|
handle_static_asynchronously :update_recent_visitors, queue: 'long-tasks', ttr: 300
|
261
263
|
end
|
@@ -471,7 +473,7 @@ end
|
|
471
473
|
Note the default `max_job_retries` is 0, meaning that by default **jobs are not retried**.
|
472
474
|
|
473
475
|
As jobs are retried, a progressively-increasing delay is added to give time for transient
|
474
|
-
problems to resolve themselves. This may be configured using `retry_delay_proc`. It expects
|
476
|
+
problems to resolve themselves. This may be configured using `retry_delay_proc`. It expects
|
475
477
|
an object that responds to `#call` and receives the value of `retry_delay` and the number
|
476
478
|
of times the job has been retried already. The default is a cubic back-off, eg:
|
477
479
|
|
data/examples/demo.rb
CHANGED
@@ -38,7 +38,7 @@ module Tester
|
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
|
-
# connection = Backburner::Connection.new("beanstalk://
|
41
|
+
# connection = Backburner::Connection.new("beanstalk://127.0.0.1")
|
42
42
|
|
43
43
|
Backburner.configure do |config|
|
44
44
|
config.beanstalk_url = "beanstalk://127.0.0.1"
|
@@ -57,4 +57,4 @@ Tester::UserModel.async.foo("bar", "baz")
|
|
57
57
|
Backburner.default_queues.concat([Tester::TestJob.queue, Tester::UserModel.queue])
|
58
58
|
Backburner.work
|
59
59
|
# Backburner.work("test.job")
|
60
|
-
# Backburner.work("tester/user-model")
|
60
|
+
# Backburner.work("tester/user-model")
|
@@ -19,7 +19,7 @@ module Backburner
|
|
19
19
|
attr_accessor :reserve_timeout # duration to wait to reserve on a single server
|
20
20
|
|
21
21
|
def initialize
|
22
|
-
@beanstalk_url = "beanstalk://
|
22
|
+
@beanstalk_url = "beanstalk://127.0.0.1"
|
23
23
|
@tube_namespace = "backburner.worker.queue"
|
24
24
|
@namespace_separator = "."
|
25
25
|
@default_priority = 65536
|
@@ -6,32 +6,126 @@ module Backburner
|
|
6
6
|
|
7
7
|
attr_accessor :url, :beanstalk
|
8
8
|
|
9
|
+
# If a proc is provided, it will be called (and given this connection as an
|
10
|
+
# argument) whenever the connection is reconnected.
|
11
|
+
# @example
|
12
|
+
# connection.on_reconnect = lambda { |conn| puts 'reconnected!' }
|
13
|
+
attr_accessor :on_reconnect
|
14
|
+
|
9
15
|
# Constructs a backburner connection
|
10
|
-
# `url` can be a string i.e '
|
11
|
-
|
16
|
+
# `url` can be a string i.e '127.0.0.1:3001' or an array of
|
17
|
+
# addresses (however, only the first element in the array will
|
18
|
+
# be used)
|
19
|
+
def initialize(url, &on_reconnect)
|
12
20
|
@url = url
|
13
21
|
@beanstalk = nil
|
22
|
+
@on_reconnect = on_reconnect
|
14
23
|
connect!
|
15
24
|
end
|
16
25
|
|
17
|
-
#
|
18
|
-
|
19
|
-
|
26
|
+
# Close the connection, if it exists
|
27
|
+
def close
|
28
|
+
@beanstalk.close if @beanstalk
|
29
|
+
@beanstalk = nil
|
20
30
|
__setobj__(@beanstalk)
|
21
|
-
|
31
|
+
end
|
32
|
+
|
33
|
+
# Determines if the connection to Beanstalk is currently open
|
34
|
+
def connected?
|
35
|
+
begin
|
36
|
+
!!(@beanstalk && @beanstalk.connection && @beanstalk.connection.connection && !@beanstalk.connection.connection.closed?) # Would be nice if beaneater provided a connected? method
|
37
|
+
rescue => e
|
38
|
+
false
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Attempt to reconnect to Beanstalk. Note: the connection will not be watching
|
43
|
+
# or using the tubes it was before it was reconnected (as it's actually a
|
44
|
+
# completely new connection)
|
45
|
+
# @raise [Beaneater::NotConnected] If beanstalk fails to connect
|
46
|
+
def reconnect!
|
47
|
+
close
|
48
|
+
connect!
|
49
|
+
@on_reconnect.call(self) if @on_reconnect.respond_to?(:call)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Yield to a block that will be retried several times if the connection to
|
53
|
+
# beanstalk goes down and is able to be re-established.
|
54
|
+
#
|
55
|
+
# @param options Hash Options. Valid options are:
|
56
|
+
# :max_retries Integer The maximum number of times the block will be yielded to.
|
57
|
+
# Defaults to 4
|
58
|
+
# :on_retry Proc An optional proc that will be called for each retry. Will be
|
59
|
+
# called after the connection is re-established and :retry_delay
|
60
|
+
# has passed but before the block is yielded to again
|
61
|
+
# :retry_delay Float The amount to sleep before retrying. Defaults to 1.0
|
62
|
+
# @raise Beaneater::NotConnected If a connection is unable to be re-established
|
63
|
+
def retryable(options = {}, &block)
|
64
|
+
options = {:max_retries => 4, :on_retry => nil, :retry_delay => 1.0}.merge!(options)
|
65
|
+
retry_count = options[:max_retries]
|
66
|
+
|
67
|
+
begin
|
68
|
+
yield
|
69
|
+
|
70
|
+
rescue Beaneater::NotConnected
|
71
|
+
if retry_count > 0
|
72
|
+
reconnect!
|
73
|
+
retry_count -= 1
|
74
|
+
sleep options[:retry_delay]
|
75
|
+
options[:on_retry].call if options[:on_retry].respond_to?(:call)
|
76
|
+
retry
|
77
|
+
else # stop retrying
|
78
|
+
raise e
|
79
|
+
end
|
80
|
+
end
|
22
81
|
end
|
23
82
|
|
24
83
|
protected
|
25
84
|
|
85
|
+
# Attempt to ensure we're connected to Beanstalk if the missing method is
|
86
|
+
# present in the delegate and we haven't shut down the connection on purpose
|
87
|
+
# @raise [Beaneater::NotConnected] If beanstalk fails to connect after multiple attempts.
|
88
|
+
def method_missing(m, *args, &block)
|
89
|
+
ensure_connected! if respond_to_missing?(m, false)
|
90
|
+
super
|
91
|
+
end
|
92
|
+
|
26
93
|
# Connects to a beanstalk queue
|
94
|
+
# @raise Beaneater::NotConnected if the connection cannot be established
|
27
95
|
def connect!
|
28
|
-
@beanstalk
|
96
|
+
@beanstalk = Beaneater.new(beanstalk_addresses)
|
97
|
+
__setobj__(@beanstalk)
|
98
|
+
@beanstalk
|
99
|
+
end
|
100
|
+
|
101
|
+
# Attempts to ensure a connection to beanstalk is established but only if
|
102
|
+
# we're not connected already
|
103
|
+
# @param max_retries Integer The maximum number of times to attempt connecting. Defaults to 4
|
104
|
+
# @param retry_delay Float The time to wait between retrying to connect. Defaults to 1.0
|
105
|
+
# @raise [Beaneater::NotConnected] If beanstalk fails to connect after multiple attempts.
|
106
|
+
# @return Connection This Connection is returned if the connection to beanstalk is open or was able to be reconnected
|
107
|
+
def ensure_connected!(max_retries = 4, retry_delay = 1.0)
|
108
|
+
return self if connected?
|
109
|
+
|
110
|
+
begin
|
111
|
+
reconnect!
|
112
|
+
return self
|
113
|
+
|
114
|
+
rescue Beaneater::NotConnected => e
|
115
|
+
if max_retries > 0
|
116
|
+
max_retries -= 1
|
117
|
+
sleep retry_delay
|
118
|
+
retry
|
119
|
+
else # stop retrying
|
120
|
+
raise e
|
121
|
+
end
|
122
|
+
end
|
29
123
|
end
|
30
124
|
|
31
125
|
# Returns the beanstalk queue addresses
|
32
126
|
#
|
33
127
|
# @example
|
34
|
-
# beanstalk_addresses => ["
|
128
|
+
# beanstalk_addresses => ["127.0.0.1:11300"]
|
35
129
|
#
|
36
130
|
def beanstalk_addresses
|
37
131
|
uri = self.url.is_a?(Array) ? self.url.first : self.url
|
@@ -41,11 +135,11 @@ module Backburner
|
|
41
135
|
# Returns a host and port based on the uri_string given
|
42
136
|
#
|
43
137
|
# @example
|
44
|
-
# beanstalk_host_and_port("beanstalk://
|
138
|
+
# beanstalk_host_and_port("beanstalk://127.0.0.1") => "127.0.0.1:11300"
|
45
139
|
#
|
46
140
|
def beanstalk_host_and_port(uri_string)
|
47
141
|
uri = URI.parse(uri_string)
|
48
|
-
raise(BadURL, uri_string) if uri.scheme != 'beanstalk'
|
142
|
+
raise(BadURL, uri_string) if uri.scheme != 'beanstalk'.freeze
|
49
143
|
"#{uri.host}:#{uri.port || 11300}"
|
50
144
|
end
|
51
145
|
end # Connection
|
data/lib/backburner/hooks.rb
CHANGED
@@ -4,11 +4,11 @@ module Backburner
|
|
4
4
|
# Triggers all method hooks that match the given event type with specified arguments.
|
5
5
|
#
|
6
6
|
# @example
|
7
|
-
# invoke_hook_events(:before_enqueue, 'some', 'args')
|
8
|
-
# invoke_hook_events(:after_perform, 5)
|
7
|
+
# invoke_hook_events(hookable, :before_enqueue, 'some', 'args')
|
8
|
+
# invoke_hook_events(hookable, :after_perform, 5)
|
9
9
|
#
|
10
|
-
def invoke_hook_events(
|
11
|
-
res = find_hook_events(
|
10
|
+
def invoke_hook_events(hookable, event, *args)
|
11
|
+
res = find_hook_events(hookable, event).map { |e| hookable.send(e, *args) }
|
12
12
|
return false if res.any? { |result| result == false }
|
13
13
|
res
|
14
14
|
end
|
@@ -20,16 +20,16 @@ module Backburner
|
|
20
20
|
# original task after calling all other around blocks.
|
21
21
|
#
|
22
22
|
# @example
|
23
|
-
# around_hook_events(:around_perform) {
|
23
|
+
# around_hook_events(hookable, :around_perform) { hookable.perform }
|
24
24
|
#
|
25
|
-
def around_hook_events(
|
25
|
+
def around_hook_events(hookable, event, *args, &block)
|
26
26
|
raise "Please pass a block to hook events!" unless block_given?
|
27
|
-
around_hooks = find_hook_events(
|
27
|
+
around_hooks = find_hook_events(hookable, event).reverse
|
28
28
|
aggregate_filter = Proc.new { |&blk| blk.call }
|
29
29
|
around_hooks.each do |ah|
|
30
30
|
prior_around_filter = aggregate_filter
|
31
31
|
aggregate_filter = Proc.new do |&blk|
|
32
|
-
|
32
|
+
hookable.method(ah).call(*args) do
|
33
33
|
prior_around_filter.call(&blk)
|
34
34
|
end
|
35
35
|
end
|
@@ -45,9 +45,9 @@ module Backburner
|
|
45
45
|
# find_hook_events(:before_enqueue)
|
46
46
|
# # => ['before_enqueue_foo', 'before_enqueue_bar']
|
47
47
|
#
|
48
|
-
def find_hook_events(
|
49
|
-
(
|
48
|
+
def find_hook_events(hookable, event)
|
49
|
+
(hookable.methods - Object.methods).grep(/^#{event}/).sort
|
50
50
|
end
|
51
51
|
end
|
52
52
|
end # Hooks
|
53
|
-
end # Backburner
|
53
|
+
end # Backburner
|
data/lib/backburner/job.rb
CHANGED
@@ -46,7 +46,12 @@ module Backburner
|
|
46
46
|
return false unless res
|
47
47
|
# Execute the job
|
48
48
|
@hooks.around_hook_events(job_class, :around_perform, *args) do
|
49
|
-
|
49
|
+
# We subtract one to ensure we timeout before beanstalkd does, except if:
|
50
|
+
# a) ttr == 0, to support never timing out
|
51
|
+
# b) ttr == 1, so that we don't accidentally set it to never time out
|
52
|
+
# NB: A ttr of 1 will likely result in race conditions between
|
53
|
+
# Backburner and beanstalkd and should probably be avoided
|
54
|
+
timeout_job_after(task.ttr > 1 ? task.ttr - 1 : task.ttr) { job_class.perform(*args) }
|
50
55
|
end
|
51
56
|
task.delete
|
52
57
|
# Invoke after perform hook
|
data/lib/backburner/version.rb
CHANGED
data/lib/backburner/worker.rb
CHANGED
@@ -27,15 +27,24 @@ module Backburner
|
|
27
27
|
pri = resolve_priority(opts[:pri] || job_class)
|
28
28
|
delay = [0, opts[:delay].to_i].max
|
29
29
|
ttr = resolve_respond_timeout(opts[:ttr] || job_class)
|
30
|
-
res
|
30
|
+
res = Backburner::Hooks.invoke_hook_events(job_class, :before_enqueue, *args)
|
31
|
+
|
31
32
|
return false unless res # stop if hook is false
|
33
|
+
|
32
34
|
data = { :class => job_class.name, :args => args }
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
35
|
+
queue = opts[:queue] && (Proc === opts[:queue] ? opts[:queue].call(job_class) : opts[:queue])
|
36
|
+
|
37
|
+
begin
|
38
|
+
connection = Backburner::Connection.new(Backburner.configuration.beanstalk_url)
|
39
|
+
connection.retryable do
|
40
|
+
tube = connection.tubes[expand_tube_name(queue || job_class)]
|
41
|
+
tube.put(data.to_json, :pri => pri, :delay => delay, :ttr => ttr)
|
42
|
+
end
|
43
|
+
Backburner::Hooks.invoke_hook_events(job_class, :after_enqueue, *args)
|
44
|
+
ensure
|
45
|
+
connection.close if connection
|
37
46
|
end
|
38
|
-
|
47
|
+
|
39
48
|
return true
|
40
49
|
end
|
41
50
|
|
@@ -52,21 +61,15 @@ module Backburner
|
|
52
61
|
end
|
53
62
|
end
|
54
63
|
|
55
|
-
# Returns the worker connection.
|
56
|
-
# @example
|
57
|
-
# Backburner::Worker.connection # => <Beaneater::Connection>
|
58
|
-
def self.connection
|
59
|
-
@connection ||= Connection.new(Backburner.configuration.beanstalk_url)
|
60
|
-
end
|
61
|
-
|
62
64
|
# List of tube names to be watched and processed
|
63
|
-
attr_accessor :tube_names
|
65
|
+
attr_accessor :tube_names, :connection
|
64
66
|
|
65
67
|
# Constructs a new worker for processing jobs within specified tubes.
|
66
68
|
#
|
67
69
|
# @example
|
68
70
|
# Worker.new(['test.job'])
|
69
71
|
def initialize(tube_names=nil)
|
72
|
+
@connection = new_connection
|
70
73
|
@tube_names = self.process_tube_names(tube_names)
|
71
74
|
register_signal_handlers!
|
72
75
|
end
|
@@ -116,27 +119,34 @@ module Backburner
|
|
116
119
|
compact_tube_names(tube_names)
|
117
120
|
end
|
118
121
|
|
119
|
-
#
|
120
|
-
# Pops the job off and serializes the job to JSON.
|
121
|
-
# Each job is performed by invoking `perform` on the job class.
|
122
|
+
# Performs a job by reserving a job from beanstalk and processing it
|
122
123
|
#
|
123
124
|
# @example
|
124
125
|
# @worker.work_one_job
|
125
|
-
#
|
126
|
-
def work_one_job(conn =
|
127
|
-
conn ||= self.connection
|
126
|
+
# @raise [Beaneater::NotConnected] If beanstalk fails to connect multiple times.
|
127
|
+
def work_one_job(conn = connection)
|
128
128
|
begin
|
129
|
-
job =
|
129
|
+
job = reserve_job(conn)
|
130
130
|
rescue Beaneater::TimedOutError => e
|
131
131
|
return
|
132
132
|
end
|
133
|
+
|
133
134
|
self.log_job_begin(job.name, job.args)
|
134
135
|
job.process
|
135
136
|
self.log_job_end(job.name)
|
137
|
+
|
136
138
|
rescue Backburner::Job::JobFormatInvalid => e
|
137
139
|
self.log_error self.exception_message(e)
|
138
140
|
rescue => e # Error occurred processing job
|
139
141
|
self.log_error self.exception_message(e)
|
142
|
+
|
143
|
+
unless job
|
144
|
+
self.log_error "Error occurred before we were able to assign a job. Giving up without retrying!"
|
145
|
+
return
|
146
|
+
end
|
147
|
+
|
148
|
+
# NB: There's a slight chance here that the connection to beanstalkd has
|
149
|
+
# gone down between the time we reserved / processed the job and here.
|
140
150
|
num_retries = job.stats.releases
|
141
151
|
retry_status = "failed: attempt #{num_retries+1} of #{queue_config.max_job_retries+1}"
|
142
152
|
if num_retries < queue_config.max_job_retries # retry again
|
@@ -147,46 +157,23 @@ module Backburner
|
|
147
157
|
job.bury
|
148
158
|
self.log_job_end(job.name, "#{retry_status}, burying") if job_started_at
|
149
159
|
end
|
150
|
-
handle_error(e, job.name, job.args, job)
|
151
|
-
end
|
152
160
|
|
153
|
-
|
154
|
-
# Used to execute beanstalkd commands in a retryable way
|
155
|
-
#
|
156
|
-
# @example
|
157
|
-
# retryable_command { ... }
|
158
|
-
# @raise [Beaneater::NotConnected] If beanstalk fails to connect multiple times.
|
159
|
-
#
|
160
|
-
def self.retryable_command(max_tries=8, &block)
|
161
|
-
begin
|
162
|
-
yield
|
163
|
-
rescue Beaneater::NotConnected
|
164
|
-
retry_connection!(max_tries)
|
165
|
-
yield
|
166
|
-
end
|
161
|
+
handle_error(e, job.name, job.args, job)
|
167
162
|
end
|
168
163
|
|
169
|
-
# Retries to make a connection to beanstalkd if that connection failed.
|
170
|
-
# @raise [Beaneater::NotConnected] If beanstalk fails to connect multiple times.
|
171
|
-
#
|
172
|
-
def self.retry_connection!(max_tries=8)
|
173
|
-
retry_count = 0
|
174
|
-
begin
|
175
|
-
@connection = nil
|
176
|
-
self.connection.stats
|
177
|
-
rescue Beaneater::NotConnected => e
|
178
|
-
if retry_count < max_tries
|
179
|
-
retry_count += 1
|
180
|
-
sleep 1
|
181
|
-
retry
|
182
|
-
else # stop retrying
|
183
|
-
raise e
|
184
|
-
end
|
185
|
-
end
|
186
|
-
end # retry_connection!
|
187
164
|
|
188
165
|
protected
|
189
166
|
|
167
|
+
# Return a new connection instance
|
168
|
+
def new_connection
|
169
|
+
Connection.new(Backburner.configuration.beanstalk_url) { |conn| Backburner::Hooks.invoke_hook_events(self, :on_reconnect, conn) }
|
170
|
+
end
|
171
|
+
|
172
|
+
# Reserve a job from the watched queues
|
173
|
+
def reserve_job(conn, reserve_timeout = Backburner.configuration.reserve_timeout)
|
174
|
+
Backburner::Job.new(conn.tubes.reserve(reserve_timeout))
|
175
|
+
end
|
176
|
+
|
190
177
|
# Returns a list of all tubes known within the system
|
191
178
|
# Filtered for tubes that match the known prefix
|
192
179
|
def all_existing_queues
|
@@ -195,10 +182,6 @@ module Backburner
|
|
195
182
|
existing_tubes + known_queues + [queue_config.primary_queue]
|
196
183
|
end
|
197
184
|
|
198
|
-
# Returns a reference to the beanstalk connection
|
199
|
-
def connection
|
200
|
-
self.class.connection
|
201
|
-
end
|
202
185
|
|
203
186
|
# Handles an error according to custom definition
|
204
187
|
# Used when processing a job that errors out
|