backburner 1.1.0 → 1.2.0.pre
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.
- 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
|