backburner 1.3.1 → 1.6.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: ffcc49440304bb0d25a3710a3d94442fd1881f51
4
- data.tar.gz: 2e9318efc9a04979135fecc8edfce4e8c8912386
2
+ SHA256:
3
+ metadata.gz: 93c9b77199209b894ae963d9474a3234212225021537f99b9fee11433e73f6c4
4
+ data.tar.gz: 49e775e7a32ccc85a557f27ba89a146617553b45e0a252ddfe12f84d1e501706
5
5
  SHA512:
6
- metadata.gz: caa6aa25343e41b2e8097be38d6b5679f304b9f417f2b0559e085caa2165660fc3ab132f5a920a49360faf16629b84b2d1b740ddf8332b0106df0437458a6f40
7
- data.tar.gz: 457cb6f9ce73c900e0c54face79f78c8f0f6a89b1462580208f5afa9450f17c717fd7b2dd5701299addb334e260e55baa5c5360583867268c9f4ea921c94e38b
6
+ metadata.gz: f6e5cdea7a5e3092b8dfc61199ccabf1eb898b3eae4c5645171a7fecc415d7c1c467d1fd173d78732dee6c60bfd2f5928cb728a52e13269bbd951d6d304a2ceb
7
+ data.tar.gz: e0da0a58b32ea88704e33a4e19a03b3f3b1c8cb63428091743953112841e6b2fc1bf27d5de18d51a8777cad9ee45e069a02a0f9cc132fdf8a0e036bd0379e162
data/.travis.yml CHANGED
@@ -2,6 +2,11 @@
2
2
  rvm:
3
3
  - 1.9.3
4
4
  - 2.0.0
5
+ - 2.1
6
+ - 2.2
7
+ - 2.3
8
+ - 2.4
9
+ - 2.5
5
10
  - rbx-2
6
11
  before_install:
7
12
  - curl -L https://github.com/kr/beanstalkd/archive/v1.9.tar.gz | tar xz -C /tmp
@@ -9,11 +14,13 @@ before_install:
9
14
  - make
10
15
  - ./beanstalkd &
11
16
  - cd $TRAVIS_BUILD_DIR
17
+ - gem update --system
18
+ - gem update bundler
12
19
  matrix:
13
20
  allow_failures:
14
21
  - rvm: rbx-2
22
+ - rvm: 2.0.0
15
23
  script:
16
- - bundle install
17
24
  - bundle exec rake test
18
25
  gemfile: Gemfile
19
26
  notifications:
data/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## Version 1.6.0 (December 30 2021)
4
+
5
+ * TBD (please help backfill)
6
+
7
+ ## Version 1.5.0 (September 10 2018)
8
+
9
+ * TBD
10
+
11
+ ## Version 1.4.1 (June 10 2017)
12
+
13
+ * Fix warning for constant ::Fixnum is deprecated (@amatsuda)
14
+
15
+ ## Version 1.4.0 (May 13 2017)
16
+
17
+ * Fix unit tests to be more consistent (@eltone)
18
+ * Ensure job supports body hash with symbol keys (@eltone)
19
+ * Add support for custom serialization formats (@eltone)
20
+ * Log the params when a job timeout occurs (@nathantsoi)
21
+
3
22
  ## Version 1.3.1 (April 21 2016)
4
23
 
5
24
  * Addition of thread-pool-based concurrency (@contentfree)
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
- # Backburner
1
+ # Backburner [![Build Status](https://travis-ci.org/nesquena/backburner.svg?branch=master)](https://travis-ci.org/nesquena/backburner)
2
2
 
3
- Backburner is a [beanstalkd](http://kr.github.com/beanstalkd/)-powered job queue that can handle a very high volume of jobs.
3
+ Backburner is a [beanstalkd](https://beanstalkd.github.io/)-powered job queue that can handle a very high volume of jobs.
4
4
  You create background jobs and place them on multiple work queues to be processed later.
5
5
 
6
6
  Processing background jobs reliably has never been easier than with beanstalkd and Backburner. This gem works with any ruby-based
@@ -34,7 +34,7 @@ The real question then is... "Why Beanstalk?".
34
34
 
35
35
  Illya has an excellent blog post
36
36
  [Scalable Work Queues with Beanstalk](http://www.igvita.com/2010/05/20/scalable-work-queues-with-beanstalk/) and
37
- Adam Wiggins posted [an excellent comparison](http://adam.heroku.com/past/2010/4/24/beanstalk_a_simple_and_fast_queueing_backend/).
37
+ Adam Wiggins posted [an excellent comparison](http://adam.herokuapp.com/past/2010/4/24/beanstalk_a_simple_and_fast_queueing_backend/).
38
38
 
39
39
  You will quickly see that **beanstalkd** is an underrated but incredible project that is extremely well-suited as a job queue.
40
40
  Significantly better suited for this task than Redis or a database. Beanstalk is a simple,
@@ -65,7 +65,7 @@ In the end, **beanstalk is the ideal job queue** while also being ridiculously e
65
65
 
66
66
  ## Installation
67
67
 
68
- First, you probably want to [install beanstalkd](http://kr.github.com/beanstalkd/download.html), which powers the job queues.
68
+ First, you probably want to [install beanstalkd](https://beanstalkd.github.io/download.html), which powers the job queues.
69
69
  Depending on your platform, this should be as simple as (for Ubuntu):
70
70
 
71
71
  $ sudo apt-get install beanstalkd
@@ -88,7 +88,7 @@ Backburner is extremely simple to setup. Just configure basic settings for backb
88
88
 
89
89
  ```ruby
90
90
  Backburner.configure do |config|
91
- config.beanstalk_url = ["beanstalk://127.0.0.1", "..."]
91
+ config.beanstalk_url = "beanstalk://127.0.0.1"
92
92
  config.tube_namespace = "some.app.production"
93
93
  config.namespace_separator = "."
94
94
  config.on_error = lambda { |e| puts e }
@@ -102,6 +102,9 @@ Backburner.configure do |config|
102
102
  config.primary_queue = "backburner-jobs"
103
103
  config.priority_labels = { :custom => 50, :useless => 1000 }
104
104
  config.reserve_timeout = nil
105
+ config.job_serializer_proc = lambda { |body| JSON.dump(body) }
106
+ config.job_parser_proc = lambda { |body| JSON.parse(body) }
107
+
105
108
  end
106
109
  ```
107
110
 
@@ -109,7 +112,7 @@ The key options available are:
109
112
 
110
113
  | Option | Description |
111
114
  | ----------------- | ------------------------------- |
112
- | `beanstalk_url` | Address such as 'beanstalk://127.0.0.1' or an array of addresses. |
115
+ | `beanstalk_url` | Address for beanstalkd connection i.e 'beanstalk://127.0.0.1' |
113
116
  | `tube_namespace` | Prefix used for all tubes related to this backburner queue. |
114
117
  | `namespace_separator` | Separator used for namespace and queue name |
115
118
  | `on_error` | Lambda invoked with the error whenever any job in the system fails. |
@@ -123,6 +126,8 @@ The key options available are:
123
126
  | `primary_queue` | Primary queue used for a job when an alternate queue is not given. |
124
127
  | `priority_labels` | Hash of named priority definitions for your app. |
125
128
  | `reserve_timeout` | Duration to wait for work from a single server, or nil for forever. |
129
+ | `job_serializer_proc` | Lambda serializes a job body to a string to write to the task |
130
+ | `job_parser_proc` | Lambda parses a task body string to a hash |
126
131
 
127
132
  ## Breaking Changes
128
133
 
@@ -158,10 +163,25 @@ class NewsletterJob
158
163
  1000 # most urgent priority is 0
159
164
  end
160
165
 
161
- # optional, defaults to respond_timeout
166
+ # optional, defaults to respond_timeout in config
162
167
  def self.queue_respond_timeout
163
168
  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
164
169
  end
170
+
171
+ # optional, defaults to retry_delay_proc in config
172
+ def self.queue_retry_delay_proc
173
+ lambda { |min_retry_delay, num_retries| min_retry_delay + (num_retries ** 5) }
174
+ end
175
+
176
+ # optional, defaults to retry_delay in config
177
+ def self.queue_retry_delay
178
+ 5
179
+ end
180
+
181
+ # optional, defaults to max_job_retries in config
182
+ def self.queue_max_job_retries
183
+ 5
184
+ end
165
185
  end
166
186
  ```
167
187
 
@@ -508,6 +528,9 @@ end
508
528
 
509
529
  Now all backburner queue errors will appear on airbrake for deeper inspection.
510
530
 
531
+ If you wish to retry a job without logging an error (for example when handling transient issues in a cloud or service oriented environment),
532
+ simply raise a `Backburner::Job::RetryJob` error.
533
+
511
534
  ### Logging
512
535
 
513
536
  Logging in backburner is rather simple. When a job is run, the log records that. When a job
data/backburner.gemspec CHANGED
@@ -18,10 +18,9 @@ Gem::Specification.new do |s|
18
18
 
19
19
  s.add_runtime_dependency 'beaneater', '~> 1.0'
20
20
  s.add_runtime_dependency 'dante', '> 0.1.5'
21
- s.add_runtime_dependency 'concurrent-ruby', '~> 1.0.1'
21
+ s.add_runtime_dependency 'concurrent-ruby', '~> 1.0', '>= 1.0.1'
22
22
 
23
23
  s.add_development_dependency 'rake'
24
24
  s.add_development_dependency 'minitest', '3.2.0'
25
25
  s.add_development_dependency 'mocha'
26
- s.add_development_dependency 'byebug'
27
26
  end
@@ -17,6 +17,8 @@ module Backburner
17
17
  attr_accessor :primary_queue # the general queue
18
18
  attr_accessor :priority_labels # priority labels
19
19
  attr_accessor :reserve_timeout # duration to wait to reserve on a single server
20
+ attr_accessor :job_serializer_proc # proc to write the job body to a string
21
+ attr_accessor :job_parser_proc # proc to parse a job body from a string
20
22
 
21
23
  def initialize
22
24
  @beanstalk_url = "beanstalk://127.0.0.1"
@@ -34,6 +36,8 @@ module Backburner
34
36
  @primary_queue = "backburner-jobs"
35
37
  @priority_labels = PRIORITY_LABELS
36
38
  @reserve_timeout = nil
39
+ @job_serializer_proc = lambda { |body| body.to_json }
40
+ @job_parser_proc = lambda { |body| JSON.parse(body) }
37
41
  end
38
42
 
39
43
  def namespace_separator=(val)
@@ -34,7 +34,7 @@ module Backburner
34
34
  def connected?
35
35
  begin
36
36
  !!(@beanstalk && @beanstalk.connection && @beanstalk.connection.connection && !@beanstalk.connection.connection.closed?) # Would be nice if beaneater provided a connected? method
37
- rescue => e
37
+ rescue
38
38
  false
39
39
  end
40
40
  end
@@ -114,7 +114,7 @@ module Backburner
114
114
  resolve_priority(pri.queue_priority)
115
115
  elsif pri.is_a?(String) || pri.is_a?(Symbol) # named priority
116
116
  resolve_priority(Backburner.configuration.priority_labels[pri.to_sym])
117
- elsif pri.is_a?(Fixnum) # numerical
117
+ elsif pri.is_a?(Integer) # numerical
118
118
  pri
119
119
  else # default
120
120
  Backburner.configuration.default_priority
@@ -131,12 +131,63 @@ module Backburner
131
131
  def resolve_respond_timeout(ttr)
132
132
  if ttr.respond_to?(:queue_respond_timeout)
133
133
  resolve_respond_timeout(ttr.queue_respond_timeout)
134
- elsif ttr.is_a?(Fixnum) # numerical
134
+ elsif ttr.is_a?(Integer) # numerical
135
135
  ttr
136
136
  else # default
137
137
  Backburner.configuration.respond_timeout
138
138
  end
139
139
  end
140
140
 
141
+ # Resolves max retries based on the value given. Can be integer, a class or nothing
142
+ #
143
+ # @example
144
+ # resolve_max_job_retries(5) => 5
145
+ # resolve_max_job_retries(FooBar) => <queue max_job_retries>
146
+ # resolve_max_job_retries(nil) => <default max_job_retries>
147
+ #
148
+ def resolve_max_job_retries(retries)
149
+ if retries.respond_to?(:queue_max_job_retries)
150
+ resolve_max_job_retries(retries.queue_max_job_retries)
151
+ elsif retries.is_a?(Integer) # numerical
152
+ retries
153
+ else # default
154
+ Backburner.configuration.max_job_retries
155
+ end
156
+ end
157
+
158
+ # Resolves retry delay based on the value given. Can be integer, a class or nothing
159
+ #
160
+ # @example
161
+ # resolve_retry_delay(5) => 5
162
+ # resolve_retry_delay(FooBar) => <queue retry_delay>
163
+ # resolve_retry_delay(nil) => <default retry_delay>
164
+ #
165
+ def resolve_retry_delay(delay)
166
+ if delay.respond_to?(:queue_retry_delay)
167
+ resolve_retry_delay(delay.queue_retry_delay)
168
+ elsif delay.is_a?(Integer) # numerical
169
+ delay
170
+ else # default
171
+ Backburner.configuration.retry_delay
172
+ end
173
+ end
174
+
175
+ # Resolves retry delay proc based on the value given. Can be proc, a class or nothing
176
+ #
177
+ # @example
178
+ # resolve_retry_delay_proc(proc) => proc
179
+ # resolve_retry_delay_proc(FooBar) => <queue retry_delay_proc>
180
+ # resolve_retry_delay_proc(nil) => <default retry_delay_proc>
181
+ #
182
+ def resolve_retry_delay_proc(proc)
183
+ if proc.respond_to?(:queue_retry_delay_proc)
184
+ resolve_retry_delay_proc(proc.queue_retry_delay_proc)
185
+ elsif proc.is_a?(Proc)
186
+ proc
187
+ else # default
188
+ Backburner.configuration.retry_delay_proc
189
+ end
190
+ end
191
+
141
192
  end # Helpers
142
193
  end # Backburner
@@ -7,6 +7,7 @@ module Backburner
7
7
  class JobTimeout < RuntimeError; end
8
8
  class JobNotFound < RuntimeError; end
9
9
  class JobFormatInvalid < RuntimeError; end
10
+ class RetryJob < RuntimeError; end
10
11
 
11
12
  attr_accessor :task, :body, :name, :args
12
13
 
@@ -21,8 +22,9 @@ module Backburner
21
22
  def initialize(task)
22
23
  @hooks = Backburner::Hooks
23
24
  @task = task
24
- @body = task.body.is_a?(Hash) ? task.body : JSON.parse(task.body)
25
- @name, @args = body["class"], body["args"]
25
+ @body = task.body.is_a?(Hash) ? task.body : Backburner.configuration.job_parser_proc.call(task.body)
26
+ @name = body["class"] || body[:class]
27
+ @args = body["args"] || body[:args]
26
28
  rescue => ex # Job was not valid format
27
29
  self.bury
28
30
  raise JobFormatInvalid, "Job body could not be parsed: #{ex.inspect}"
@@ -42,10 +44,10 @@ module Backburner
42
44
  #
43
45
  def process
44
46
  # Invoke before hook and stop if false
45
- res = @hooks.invoke_hook_events(job_class, :before_perform, *args)
47
+ res = @hooks.invoke_hook_events(job_name, :before_perform, *args)
46
48
  return false unless res
47
49
  # Execute the job
48
- @hooks.around_hook_events(job_class, :around_perform, *args) do
50
+ @hooks.around_hook_events(job_name, :around_perform, *args) do
49
51
  # We subtract one to ensure we timeout before beanstalkd does, except if:
50
52
  # a) ttr == 0, to support never timing out
51
53
  # b) ttr == 1, so that we don't accidentally set it to never time out
@@ -55,35 +57,50 @@ module Backburner
55
57
  end
56
58
  task.delete
57
59
  # Invoke after perform hook
58
- @hooks.invoke_hook_events(job_class, :after_perform, *args)
60
+ @hooks.invoke_hook_events(job_name, :after_perform, *args)
59
61
  rescue => e
60
- @hooks.invoke_hook_events(job_class, :on_failure, e, *args)
62
+ @hooks.invoke_hook_events(job_name, :on_failure, e, *args)
61
63
  raise e
62
64
  end
63
65
 
64
66
  def bury
65
- @hooks.invoke_hook_events(job_class, :on_bury, *args)
67
+ @hooks.invoke_hook_events(job_name, :on_bury, *args)
66
68
  task.bury
67
69
  end
68
70
 
69
71
  def retry(count, delay)
70
- @hooks.invoke_hook_events(job_class, :on_retry, count, delay, *args)
72
+ @hooks.invoke_hook_events(job_name, :on_retry, count, delay, *args)
71
73
  task.release(delay: delay)
72
74
  end
73
75
 
74
- protected
75
-
76
76
  # Returns the class for the job handler
77
77
  #
78
78
  # @example
79
79
  # job_class # => NewsletterSender
80
80
  #
81
81
  def job_class
82
- handler = constantize(self.name) rescue nil
82
+ handler = try_job_class
83
83
  raise(JobNotFound, self.name) unless handler
84
84
  handler
85
85
  end
86
86
 
87
+ protected
88
+
89
+ # Attempts to return a constantized job name, otherwise reverts to the name string
90
+ #
91
+ # @example
92
+ # job_name # => "SomeUnknownJob"
93
+ def job_name
94
+ handler = try_job_class
95
+ handler ? handler : self.name
96
+ end
97
+
98
+ def try_job_class
99
+ constantize(self.name)
100
+ rescue NameError
101
+ nil
102
+ end
103
+
87
104
  # Timeout job within specified block after given time.
88
105
  #
89
106
  # @example
@@ -93,7 +110,7 @@ module Backburner
93
110
  begin
94
111
  Timeout::timeout(secs) { yield }
95
112
  rescue Timeout::Error => e
96
- raise JobTimeout, "#{name} hit #{secs}s timeout.\nbacktrace: #{e.backtrace}"
113
+ raise JobTimeout, "#{name}(#{(@args||[]).join(', ')}) hit #{secs}s timeout.\nbacktrace: #{e.backtrace}"
97
114
  end
98
115
  end
99
116
 
@@ -10,7 +10,7 @@ module Backburner
10
10
  # Print out when a job is about to begin
11
11
  def log_job_begin(name, args)
12
12
  log_info "Work job #{name} with #{args.inspect}"
13
- @job_started_at = Time.now
13
+ Thread.current[:job_started_at] = Time.now
14
14
  end
15
15
 
16
16
  # Print out when a job completed
@@ -24,7 +24,7 @@ module Backburner
24
24
 
25
25
  # Returns true if the job logging started
26
26
  def job_started_at
27
- @job_started_at
27
+ Thread.current[:job_started_at]
28
28
  end
29
29
 
30
30
  # Print a message to stdout
@@ -50,4 +50,4 @@ module Backburner
50
50
  Backburner.configuration.logger
51
51
  end
52
52
  end
53
- end
53
+ end
@@ -4,6 +4,9 @@ module Backburner
4
4
  base.instance_variable_set(:@queue_name, nil)
5
5
  base.instance_variable_set(:@queue_priority, nil)
6
6
  base.instance_variable_set(:@queue_respond_timeout, nil)
7
+ base.instance_variable_set(:@queue_max_job_retries, nil)
8
+ base.instance_variable_set(:@queue_retry_delay, nil)
9
+ base.instance_variable_set(:@queue_retry_delay_proc, nil)
7
10
  base.instance_variable_set(:@queue_jobs_limit, nil)
8
11
  base.instance_variable_set(:@queue_garbage_limit, nil)
9
12
  base.instance_variable_set(:@queue_retry_limit, nil)
@@ -54,6 +57,48 @@ module Backburner
54
57
  end
55
58
  end
56
59
 
60
+ # Returns or assigns queue max_job_retries for this job
61
+ #
62
+ # @example
63
+ # queue_max_job_retries 120
64
+ # @klass.queue_max_job_retries # => 120
65
+ #
66
+ def queue_max_job_retries(delay=nil)
67
+ if delay
68
+ @queue_max_job_retries = delay
69
+ else # accessor
70
+ @queue_max_job_retries
71
+ end
72
+ end
73
+
74
+ # Returns or assigns queue retry_delay for this job
75
+ #
76
+ # @example
77
+ # queue_retry_delay 120
78
+ # @klass.queue_retry_delay # => 120
79
+ #
80
+ def queue_retry_delay(delay=nil)
81
+ if delay
82
+ @queue_retry_delay = delay
83
+ else # accessor
84
+ @queue_retry_delay
85
+ end
86
+ end
87
+
88
+ # Returns or assigns queue retry_delay_proc for this job
89
+ #
90
+ # @example
91
+ # queue_retry_delay_proc lambda { |min_retry_delay, num_retries| min_retry_delay + (num_retries ** 2) }
92
+ # @klass.queue_retry_delay_proc # => lambda { |min_retry_delay, num_retries| min_retry_delay + (num_retries ** 2) }
93
+ #
94
+ def queue_retry_delay_proc(proc=nil)
95
+ if proc
96
+ @queue_retry_delay_proc = proc
97
+ else # accessor
98
+ @queue_retry_delay_proc
99
+ end
100
+ end
101
+
57
102
  # Returns or assigns queue parallel active jobs limit (only ThreadsOnFork and Threading workers)
58
103
  #
59
104
  # @example
@@ -1,3 +1,3 @@
1
1
  module Backburner
2
- VERSION = "1.3.1"
2
+ VERSION = "1.6.0"
3
3
  end
@@ -39,7 +39,8 @@ module Backburner
39
39
  connection = Backburner::Connection.new(Backburner.configuration.beanstalk_url)
40
40
  connection.retryable do
41
41
  tube = connection.tubes[expand_tube_name(queue || job_class)]
42
- response = tube.put(data.to_json, :pri => pri, :delay => delay, :ttr => ttr)
42
+ serialized_data = Backburner.configuration.job_serializer_proc.call(data)
43
+ response = tube.put(serialized_data, :pri => pri, :delay => delay, :ttr => ttr)
43
44
  end
44
45
  return nil unless Backburner::Hooks.invoke_hook_events(job_class, :after_enqueue, *args)
45
46
  ensure
@@ -139,7 +140,7 @@ module Backburner
139
140
  rescue Backburner::Job::JobFormatInvalid => e
140
141
  self.log_error self.exception_message(e)
141
142
  rescue => e # Error occurred processing job
142
- self.log_error self.exception_message(e)
143
+ self.log_error self.exception_message(e) unless e.is_a?(Backburner::Job::RetryJob)
143
144
 
144
145
  unless job
145
146
  self.log_error "Error occurred before we were able to assign a job. Giving up without retrying!"
@@ -149,9 +150,11 @@ module Backburner
149
150
  # NB: There's a slight chance here that the connection to beanstalkd has
150
151
  # gone down between the time we reserved / processed the job and here.
151
152
  num_retries = job.stats.releases
152
- retry_status = "failed: attempt #{num_retries+1} of #{queue_config.max_job_retries+1}"
153
- if num_retries < queue_config.max_job_retries # retry again
154
- delay = queue_config.retry_delay_proc.call(queue_config.retry_delay, num_retries) rescue queue_config.retry_delay
153
+ max_job_retries = resolve_max_job_retries(job.job_class)
154
+ retry_status = "failed: attempt #{num_retries+1} of #{max_job_retries+1}"
155
+ if num_retries < max_job_retries # retry again
156
+ retry_delay = resolve_retry_delay(job.job_class)
157
+ delay = resolve_retry_delay_proc(job.job_class).call(retry_delay, num_retries) rescue retry_delay
155
158
  job.retry(num_retries + 1, delay)
156
159
  self.log_job_end(job.name, "#{retry_status}, retrying in #{delay}s") if job_started_at
157
160
  else # retries failed, bury
@@ -3,9 +3,13 @@ require 'concurrent'
3
3
  module Backburner
4
4
  module Workers
5
5
  class Threading < Worker
6
+ attr_accessor :self_read, :self_write, :exit_on_shutdown
7
+
8
+ @shutdown_timeout = 10
9
+
6
10
  class << self
7
- attr_accessor :shutdown
8
11
  attr_accessor :threads_number
12
+ attr_accessor :shutdown_timeout
9
13
  end
10
14
 
11
15
  # Custom initializer just to set @tubes_data
@@ -13,6 +17,7 @@ module Backburner
13
17
  @tubes_data = {}
14
18
  super
15
19
  self.process_tube_options
20
+ @exit_on_shutdown = true
16
21
  end
17
22
 
18
23
  # Used to prepare job queues before processing jobs.
@@ -48,16 +53,19 @@ module Backburner
48
53
  connection.on_reconnect = lambda { |conn| conn.tubes.watch!(tube_name) }
49
54
 
50
55
  # Make it work jobs using its own connection per thread
51
- pool.post(connection) { |connection|
52
- loop {
56
+ pool.post(connection) do |memo_connection|
57
+ # TODO: use read-write lock?
58
+ loop do
53
59
  begin
54
- work_one_job(connection)
55
-
60
+ break if @in_shutdown
61
+ work_one_job(memo_connection)
56
62
  rescue => e
57
63
  log_error("Exception caught in thread pool loop. Continuing. -> #{e.message}\nBacktrace: #{e.backtrace}")
58
64
  end
59
- }
60
- }
65
+ end
66
+
67
+ connection.close
68
+ end
61
69
  end
62
70
  end
63
71
 
@@ -111,18 +119,44 @@ module Backburner
111
119
 
112
120
  # Wait for the shutdown signel
113
121
  def wait_for_shutdown!
114
- while !self.class.shutdown do
115
- sleep 0.5
122
+ raise Interrupt while IO.select([self_read])
123
+ rescue Interrupt
124
+ shutdown
125
+ end
126
+
127
+ def shutdown_threadpools
128
+ @thread_pools.each { |_name, pool| pool.shutdown }
129
+ shutdown_time = Time.now
130
+ @in_shutdown = true
131
+ all_shutdown = @thread_pools.all? do |_name, pool|
132
+ time_to_wait = self.class.shutdown_timeout - (Time.now - shutdown_time).to_i
133
+ pool.wait_for_termination(time_to_wait) if time_to_wait > 0
116
134
  end
135
+ rescue Interrupt
136
+ log_info "graceful shutdown aborted, shutting down immediately"
137
+ ensure
138
+ kill unless all_shutdown
139
+ end
117
140
 
118
- # Shutting down
119
- # FIXME: Shut down each thread's connection
120
- @thread_pools.each { |name, pool| pool.kill }
141
+ def kill
142
+ @thread_pools.each { |_name, pool| pool.kill unless pool.shutdown? }
121
143
  end
122
144
 
123
145
  def shutdown
124
- Backburner::Workers::Threading.shutdown = true
125
- super
146
+ log_info "beginning graceful worker shutdown"
147
+ shutdown_threadpools
148
+ super if @exit_on_shutdown
149
+ end
150
+
151
+ # Registers signal handlers TERM and INT to trigger
152
+ def register_signal_handlers!
153
+ @self_read, @self_write = IO.pipe
154
+ %w[TERM INT].each do |sig|
155
+ trap(sig) do
156
+ raise Interrupt if @in_shutdown
157
+ self_write.puts(sig)
158
+ end
159
+ end
126
160
  end
127
161
  end # Threading
128
162
  end # Workers
@@ -13,6 +13,24 @@ class TestJob
13
13
  def self.perform(x, y); $worker_test_count += x + y; end
14
14
  end
15
15
 
16
+ class TestSlowJob
17
+ include Backburner::Queue
18
+ queue_priority :medium
19
+ queue_respond_timeout 300
20
+ def self.perform(x, y); sleep 1; $worker_test_count += x + y; end
21
+ end
22
+
23
+ class TestStuckJob
24
+ include Backburner::Queue
25
+ queue_priority :medium
26
+ queue_respond_timeout 300
27
+ def self.perform(_x, _y)
28
+ loop do
29
+ sleep 0.5
30
+ end
31
+ end
32
+ end
33
+
16
34
  class TestFailJob
17
35
  include Backburner::Queue
18
36
  def self.perform(x, y); raise RuntimeError; end
@@ -36,6 +54,27 @@ class TestConfigurableRetryJob
36
54
  end
37
55
  end
38
56
 
57
+ class TestRetryWithQueueOverridesJob
58
+ include Backburner::Queue
59
+ def self.perform(retry_count)
60
+ $worker_test_count += 1
61
+ raise RuntimeError unless $worker_test_count > retry_count
62
+ $worker_success = true
63
+ end
64
+
65
+ def self.queue_max_job_retries
66
+ 3
67
+ end
68
+
69
+ def self.queue_retry_delay
70
+ 0
71
+ end
72
+
73
+ def self.queue_retry_delay_proc
74
+ lambda { |min_retry_delay, num_retries| min_retry_delay + (num_retries ** 2) }
75
+ end
76
+ end
77
+
39
78
  class TestAsyncJob
40
79
  include Backburner::Performable
41
80
  def self.foo(x, y); $worker_test_count = x * y; end
data/test/helpers_test.rb CHANGED
@@ -179,4 +179,100 @@ describe "Backburner::Helpers module" do
179
179
  assert_equal 300, resolve_respond_timeout(nil)
180
180
  end
181
181
  end # resolve_respond_timeout
182
+
183
+ describe "for resolve_max_job_retries method" do
184
+ before do
185
+ @original_max_job_retries = Backburner.configuration.max_job_retries
186
+ Backburner.configure { |config| config.max_job_retries = 300 }
187
+ end
188
+ after { Backburner.configure { |config| config.max_job_retries = @original_max_job_retries } }
189
+
190
+ it "supports fix num max_job_retries" do
191
+ assert_equal 500, resolve_max_job_retries(500)
192
+ end
193
+
194
+ it "supports classes which respond to queue_max_job_retries" do
195
+ job = stub(:queue_max_job_retries => 600)
196
+ assert_equal 600, resolve_max_job_retries(job)
197
+ end
198
+
199
+ it "supports classes which return null queue_max_job_retries" do
200
+ job = stub(:queue_max_job_retries => nil)
201
+ assert_equal 300, resolve_max_job_retries(job)
202
+ end
203
+
204
+ it "supports classes which don't respond to queue_max_job_retries" do
205
+ job = stub(:fake => true)
206
+ assert_equal 300, resolve_max_job_retries(job)
207
+ end
208
+
209
+ it "supports default max_job_retries for null values" do
210
+ assert_equal 300, resolve_max_job_retries(nil)
211
+ end
212
+ end # resolve_max_job_retries
213
+
214
+ describe "for resolve_retry_delay method" do
215
+ before do
216
+ @original_retry_delay = Backburner.configuration.retry_delay
217
+ Backburner.configure { |config| config.retry_delay = 300 }
218
+ end
219
+ after { Backburner.configure { |config| config.retry_delay = @original_retry_delay } }
220
+
221
+ it "supports fix num retry_delay" do
222
+ assert_equal 500, resolve_retry_delay(500)
223
+ end
224
+
225
+ it "supports classes which respond to queue_retry_delay" do
226
+ job = stub(:queue_retry_delay => 600)
227
+ assert_equal 600, resolve_retry_delay(job)
228
+ end
229
+
230
+ it "supports classes which return null queue_retry_delay" do
231
+ job = stub(:queue_retry_delay => nil)
232
+ assert_equal 300, resolve_retry_delay(job)
233
+ end
234
+
235
+ it "supports classes which don't respond to queue_retry_delay" do
236
+ job = stub(:fake => true)
237
+ assert_equal 300, resolve_retry_delay(job)
238
+ end
239
+
240
+ it "supports default retry_delay for null values" do
241
+ assert_equal 300, resolve_retry_delay(nil)
242
+ end
243
+ end # resolve_retry_delay
244
+
245
+ describe "for resolve_retry_delay_proc method" do
246
+ before do
247
+ @config_retry_delay_proc = lambda { |x, y| x + y } # Default config proc adds two values
248
+ @override_delay_proc = lambda { |x, y| x - y } # Overriden proc subtracts values
249
+ @original_retry_delay_proc = Backburner.configuration.retry_delay_proc
250
+ Backburner.configure { |config| config.retry_delay_proc = @config_retry_delay_proc }
251
+ end
252
+ after { Backburner.configure { |config| config.retry_delay_proc = @original_retry_delay_proc } }
253
+
254
+ # Rather than compare Procs execute them and compare the output
255
+ it "supports proc retry_delay_proc" do
256
+ assert_equal @override_delay_proc.call(2, 1), resolve_retry_delay_proc(@override_delay_proc).call(2, 1)
257
+ end
258
+
259
+ it "supports classes which respond to queue_retry_delay_proc" do
260
+ job = stub(:queue_retry_delay_proc => @override_delay_proc)
261
+ assert_equal @override_delay_proc.call(2, 1), resolve_retry_delay_proc(job).call(2, 1)
262
+ end
263
+
264
+ it "supports classes which return null queue_retry_delay_proc" do
265
+ job = stub(:queue_retry_delay_proc => nil)
266
+ assert_equal @original_retry_delay_proc.call(2, 1), resolve_retry_delay_proc(job).call(2, 1)
267
+ end
268
+
269
+ it "supports classes which don't respond to queue_retry_delay_proc" do
270
+ job = stub(:fake => true)
271
+ assert_equal @original_retry_delay_proc.call(2, 1), resolve_retry_delay_proc(job).call(2, 1)
272
+ end
273
+
274
+ it "supports default retry_delay_proc for null values" do
275
+ assert_equal @original_retry_delay_proc.call(2, 1), resolve_retry_delay_proc(nil).call(2, 1)
276
+ end
277
+ end # resolve_retry_delay_proc
182
278
  end
data/test/job_test.rb CHANGED
@@ -15,16 +15,29 @@ describe "Backburner::Job module" do
15
15
  describe "for initialize" do
16
16
  describe "with hash" do
17
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)
18
+ @task = stub(:body => task_body, :ttr => 120, :delete => true, :bury => true)
20
19
  end
21
20
 
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
21
+ describe "with string keys" do
22
+ let(:task_body) { { "class" => "NewsletterSender", "args" => ["foo@bar.com", "bar@foo.com"] } }
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
31
+
32
+ describe "with symbol keys" do
33
+ let(:task_body) { { :class => "NewsletterSender", :args => ["foo@bar.com", "bar@foo.com"] } }
34
+ it "should create job with correct task data" do
35
+ @job = Backburner::Job.new(@task)
36
+ assert_equal @task, @job.task
37
+ assert_equal [:class, :args], @job.body.keys
38
+ assert_equal task_body[:class], @job.name
39
+ assert_equal task_body[:args], @job.args
40
+ end
28
41
  end
29
42
  end # with hash
30
43
 
@@ -100,16 +113,41 @@ describe "Backburner::Job module" do
100
113
  end # process
101
114
 
102
115
  describe "for simple delegation method" do
103
- before do
104
- @task_body = { "class" => "NestedDemo::TestJobC", "args" => [56] }
105
- @task = stub(:body => @task_body, :ttr => 120, :delete => true, :bury => true)
106
- @task.expects(:bury).once
116
+ describe "with valid class" do
117
+ before do
118
+ @task_body = { "class" => "NestedDemo::TestJobC", "args" => [56] }
119
+ @task = stub(:body => @task_body, :ttr => 120, :delete => true, :bury => true)
120
+ @task.expects(:bury).once
121
+ end
122
+
123
+ it "should call bury for task" do
124
+ @job = Backburner::Job.new(@task)
125
+ @job.bury
126
+ end # bury
107
127
  end
108
128
 
109
- it "should call bury for task" do
110
- @job = Backburner::Job.new(@task)
111
- @job.bury
112
- end # bury
129
+ describe "with invalid class" do
130
+ before do
131
+ @task_body = { "class" => "AnUnknownClass", "args" => [] }
132
+ @task = stub(:body => @task_body, :ttr => 120, :delete => true, :bury => true, :release => true)
133
+ end
134
+
135
+ it "should call bury for task" do
136
+ @task.expects(:bury).once
137
+ @job = Backburner::Job.new(@task)
138
+ Backburner::Hooks.expects(:invoke_hook_events)
139
+ .with("AnUnknownClass", :on_bury, anything)
140
+ @job.bury
141
+ end
142
+
143
+ it "should call retry for task" do
144
+ @task.expects(:release).once
145
+ @job = Backburner::Job.new(@task)
146
+ Backburner::Hooks.expects(:invoke_hook_events)
147
+ .with("AnUnknownClass", :on_retry, 0, is_a(Integer), anything)
148
+ @job.retry(0, 0)
149
+ end
150
+ end
113
151
  end # simple delegation
114
152
 
115
153
  describe "timing out for various values of ttr" do
data/test/queue_test.rb CHANGED
@@ -44,4 +44,26 @@ describe "Backburner::Queue module" do
44
44
  assert_equal 300, NestedDemo::TestJobB.queue_respond_timeout
45
45
  end
46
46
  end # queue_respond_timeout
47
+
48
+ describe "for queue_max_job_retries assignment method" do
49
+ it "should allow queue max_job_retries to be assigned" do
50
+ NestedDemo::TestJobB.queue_max_job_retries(5)
51
+ assert_equal 5, NestedDemo::TestJobB.queue_max_job_retries
52
+ end
53
+ end # queue_max_job_retries
54
+
55
+ describe "for queue_retry_delay assignment method" do
56
+ it "should allow queue retry_delay to be assigned" do
57
+ NestedDemo::TestJobB.queue_retry_delay(300)
58
+ assert_equal 300, NestedDemo::TestJobB.queue_retry_delay
59
+ end
60
+ end # queue_retry_delay
61
+
62
+ describe "for queue_retry_delay_proc assignment method" do
63
+ it "should allow queue retry_delay_proc to be assigned" do
64
+ retry_delay_proc = lambda { |x, y| x - y }
65
+ NestedDemo::TestJobB.queue_retry_delay_proc(retry_delay_proc)
66
+ assert_equal retry_delay_proc.call(2, 1), NestedDemo::TestJobB.queue_retry_delay_proc.call(2, 1)
67
+ end
68
+ end # queue_retry_delay_proc
47
69
  end # Backburner::Queue
data/test/test_helper.rb CHANGED
@@ -6,7 +6,7 @@ begin
6
6
  rescue LoadError
7
7
  require 'mocha'
8
8
  end
9
- $:.unshift File.expand_path("../../lib")
9
+ $LOAD_PATH.unshift File.expand_path("lib")
10
10
  require 'backburner'
11
11
  require File.expand_path('../helpers/templogger', __FILE__)
12
12
 
@@ -92,12 +92,12 @@ class MiniTest::Spec
92
92
  end
93
93
 
94
94
  # pop_one_job(tube_name)
95
- def pop_one_job(tube_name=Backburner.configuration.primary_queue, &block)
95
+ def pop_one_job(tube_name=Backburner.configuration.primary_queue)
96
96
  tube_name = [Backburner.configuration.tube_namespace, tube_name].join(".")
97
97
  connection = beanstalk_connection
98
98
  connection.tubes.watch!(tube_name)
99
99
  silenced(3) { @res = connection.tubes.reserve }
100
- yield @res, JSON.parse(@res.body)
100
+ yield @res, Backburner.configuration.job_parser_proc.call(@res.body)
101
101
  ensure
102
102
  connection.close if connection
103
103
  end
data/test/worker_test.rb CHANGED
@@ -125,4 +125,33 @@ describe "Backburner::Worker module" do
125
125
  assert_equal ['baz', 'bam'], worker.tube_names
126
126
  end
127
127
  end # tube_names
128
+
129
+ describe "for custom serialization" do
130
+ before do
131
+ Backburner.configure do |config|
132
+ @old_parser = config.job_parser_proc
133
+ @old_serializer = config.job_serializer_proc
134
+ config.job_parser_proc = lambda { |body| Marshal.load(body) }
135
+ config.job_serializer_proc = lambda { |body| Marshal.dump(body) }
136
+ end
137
+ end
138
+
139
+ after do
140
+ clear_jobs!('test-plain')
141
+ Backburner.configure do |config|
142
+ config.job_parser_proc = @old_parser
143
+ config.job_serializer_proc = @old_serializer
144
+ end
145
+ end
146
+
147
+ it "should support enqueuing a job" do
148
+ Backburner::Worker.enqueue TestPlainJob, [7, 9], :ttr => 100, :pri => 2000
149
+ pop_one_job("test-plain") do |job, body|
150
+ assert_equal "TestPlainJob", body[:class]
151
+ assert_equal [7, 9], body[:args]
152
+ assert_equal 100, job.ttr
153
+ assert_equal 2000, job.pri
154
+ end
155
+ end
156
+ end # custom serialization
128
157
  end # Backburner::Worker
@@ -11,7 +11,7 @@ describe "Backburner::Workers::Forking module" do
11
11
  describe "for prepare method" do
12
12
  it "should make tube names array always unique to avoid duplication" do
13
13
  worker = @worker_class.new(["foo", "demo.test.foo"])
14
- worker.prepare
14
+ capture_stdout { worker.prepare }
15
15
  assert_equal ["demo.test.foo"], worker.tube_names
16
16
  end
17
17
 
@@ -96,6 +96,7 @@ describe "Backburner::Workers::Forking module" do
96
96
 
97
97
  after do
98
98
  @templogger.close
99
+ Backburner.configuration.logger = nil
99
100
  clear_jobs!('response')
100
101
  clear_jobs!('bar.foo.1', 'bar.foo.2', 'bar.foo.3', 'bar.foo.4', 'bar.foo.5')
101
102
  end
@@ -11,7 +11,7 @@ describe "Backburner::Workers::Simple module" do
11
11
  describe "for prepare method" do
12
12
  it "should make tube names array always unique to avoid duplication" do
13
13
  worker = @worker_class.new(["foo", "demo.test.foo"])
14
- worker.prepare
14
+ capture_stdout { worker.prepare }
15
15
  assert_equal ["demo.test.foo"], worker.tube_names
16
16
  end
17
17
 
@@ -229,6 +229,33 @@ describe "Backburner::Workers::Simple module" do
229
229
  assert_equal true, $worker_success
230
230
  end
231
231
 
232
+ it "should allow queue override of retries" do
233
+ max_job_retries = TestRetryWithQueueOverridesJob.queue_max_job_retries
234
+ clear_jobs!('foo.bar')
235
+ Backburner.configure do |config|
236
+ # Config should be overridden by queue overrides
237
+ config.max_job_retries = 20
238
+ config.retry_delay = 60
239
+ #config.retry_delay_proc = lambda { |min_retry_delay, num_retries| min_retry_delay + (num_retries ** 3) } # default retry_delay_proc
240
+ end
241
+ @worker_class.enqueue TestRetryWithQueueOverridesJob, [max_job_retries], :queue => 'foo.bar'
242
+ out = []
243
+ (max_job_retries + 1).times do
244
+ out << silenced(5) do
245
+ worker = @worker_class.new('foo.bar')
246
+ worker.prepare
247
+ worker.work_one_job
248
+ end
249
+ end
250
+ assert_match(/attempt 1 of 4, retrying in 0/, out.first)
251
+ assert_match(/attempt 2 of 4, retrying in 1/, out[1])
252
+ assert_match(/attempt 3 of 4, retrying in 4/, out[2])
253
+ assert_match(/Completed TestRetryWithQueueOverridesJob/m, out.last)
254
+ refute_match(/failed/, out.last)
255
+ assert_equal 4, $worker_test_count
256
+ assert_equal true, $worker_success
257
+ end
258
+
232
259
  it "should support event hooks without retry" do
233
260
  $hooked_fail_count = 0
234
261
  clear_jobs!('foo.bar.events')
@@ -309,7 +336,7 @@ describe "Backburner::Workers::Simple module" do
309
336
  worker = @worker_class.new('foo.bar')
310
337
  connection = mock('connection')
311
338
  worker.expects(:reserve_job).with(connection).returns(stub_everything('job'))
312
- worker.work_one_job(connection)
339
+ capture_stdout { worker.work_one_job(connection) }
313
340
  end
314
341
 
315
342
  after do
@@ -6,24 +6,25 @@ describe "Backburner::Workers::Threading worker" do
6
6
  before do
7
7
  Backburner.default_queues.clear
8
8
  @worker_class = Backburner::Workers::Threading
9
+ @worker_class.shutdown_timeout = 2
9
10
  end
10
11
 
11
12
  describe "for prepare method" do
12
13
  it "should make tube names array always unique to avoid duplication" do
13
14
  worker = @worker_class.new(["foo", "demo.test.foo"])
14
- worker.prepare
15
+ capture_stdout { worker.prepare }
15
16
  assert_equal ["demo.test.foo"], worker.tube_names
16
17
  end
17
18
 
18
19
  it 'creates a thread pool per queue' do
19
20
  worker = @worker_class.new(%w(foo bar))
20
- out = capture_stdout { worker.prepare }
21
+ capture_stdout { worker.prepare }
21
22
  assert_equal 2, worker.instance_variable_get("@thread_pools").keys.size
22
23
  end
23
24
 
24
25
  it 'uses Concurrent.processor_count if no custom thread count is provided' do
25
26
  worker = @worker_class.new("foo")
26
- out = capture_stdout { worker.prepare }
27
+ capture_stdout { worker.prepare }
27
28
  assert_equal ::Concurrent.processor_count, worker.instance_variable_get("@thread_pools")["demo.test.foo"].max_length
28
29
  end
29
30
  end # prepare
@@ -54,15 +55,50 @@ describe "Backburner::Workers::Threading worker" do
54
55
  before do
55
56
  @worker = @worker_class.new(["foo:3"])
56
57
  capture_stdout { @worker.prepare }
58
+ $worker_test_count = 0
59
+ $worker_success = false
57
60
  end
58
61
 
59
62
  it 'runs work_on_job per thread' do
60
63
  clear_jobs!("foo")
61
64
  job_count=10
62
- job_count.times { @worker_class.enqueue TestJob, [1, 0], :queue => "foo" } # TestJob adds the given arguments together and then to $worker_test_count
63
- @worker.start(false) # don't wait for shutdown
64
- sleep 0.5 # Wait for threads to do their work
65
+ # TestJob adds the given arguments together and then to $worker_test_count
66
+ job_count.times { @worker_class.enqueue TestJob, [1, 0], :queue => "foo" }
67
+ capture_stdout do
68
+ @worker.start(false) # don't wait for shutdown
69
+ sleep 0.5 # Wait for threads to do their work
70
+ end
65
71
  assert_equal job_count, $worker_test_count
66
72
  end
67
73
  end # working a queue
74
+
75
+ describe 'shutting down' do
76
+ before do
77
+ @thread_count = 3
78
+ @worker = @worker_class.new(["threaded-shutdown:#{@thread_count}"])
79
+ @worker.exit_on_shutdown = false
80
+ $worker_test_count = 0
81
+ clear_jobs!("threaded-shutdown")
82
+ end
83
+
84
+ it 'gracefully exits and completes all in-flight jobs' do
85
+ 6.times { @worker_class.enqueue TestSlowJob, [1, 0], :queue => "threaded-shutdown" }
86
+ Thread.new { sleep 0.1; @worker.self_write.puts("TERM") }
87
+ capture_stdout do
88
+ @worker.start
89
+ end
90
+
91
+ assert_equal @thread_count, $worker_test_count
92
+ end
93
+
94
+ it 'forces an exit when a job is stuck' do
95
+ 6.times { @worker_class.enqueue TestStuckJob, [1, 0], :queue => "threaded-shutdown" }
96
+ Thread.new { sleep 0.1; @worker.self_write.puts("TERM") }
97
+ capture_stdout do
98
+ @worker.start
99
+ end
100
+
101
+ assert_equal 0, $worker_test_count
102
+ end
103
+ end
68
104
  end
@@ -70,7 +70,7 @@ describe "Backburner::Workers::ThreadsOnFork module" do
70
70
 
71
71
  it "should make tube names array always unique to avoid duplication" do
72
72
  worker = @worker_class.new(["foo", "demo.test.foo"])
73
- worker.prepare
73
+ capture_stdout { worker.prepare }
74
74
  assert_equal ["demo.test.foo"], worker.tube_names
75
75
  end
76
76
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: backburner
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.1
4
+ version: 1.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nathan Esquenazi
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-10-02 00:00:00.000000000 Z
11
+ date: 2021-12-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: beaneater
@@ -43,6 +43,9 @@ dependencies:
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.0'
48
+ - - ">="
46
49
  - !ruby/object:Gem::Version
47
50
  version: 1.0.1
48
51
  type: :runtime
@@ -50,6 +53,9 @@ dependencies:
50
53
  version_requirements: !ruby/object:Gem::Requirement
51
54
  requirements:
52
55
  - - "~>"
56
+ - !ruby/object:Gem::Version
57
+ version: '1.0'
58
+ - - ">="
53
59
  - !ruby/object:Gem::Version
54
60
  version: 1.0.1
55
61
  - !ruby/object:Gem::Dependency
@@ -94,20 +100,6 @@ dependencies:
94
100
  - - ">="
95
101
  - !ruby/object:Gem::Version
96
102
  version: '0'
97
- - !ruby/object:Gem::Dependency
98
- name: byebug
99
- requirement: !ruby/object:Gem::Requirement
100
- requirements:
101
- - - ">="
102
- - !ruby/object:Gem::Version
103
- version: '0'
104
- type: :development
105
- prerelease: false
106
- version_requirements: !ruby/object:Gem::Requirement
107
- requirements:
108
- - - ">="
109
- - !ruby/object:Gem::Version
110
- version: '0'
111
103
  description: Beanstalk background job processing made easy
112
104
  email:
113
105
  - nesquena@gmail.com
@@ -179,7 +171,7 @@ homepage: http://github.com/nesquena/backburner
179
171
  licenses:
180
172
  - MIT
181
173
  metadata: {}
182
- post_install_message:
174
+ post_install_message:
183
175
  rdoc_options: []
184
176
  require_paths:
185
177
  - lib
@@ -194,9 +186,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
194
186
  - !ruby/object:Gem::Version
195
187
  version: '0'
196
188
  requirements: []
197
- rubyforge_project:
198
- rubygems_version: 2.4.8
199
- signing_key:
189
+ rubygems_version: 3.0.8
190
+ signing_key:
200
191
  specification_version: 4
201
192
  summary: Reliable beanstalk background job processing made easy for Ruby and Sinatra
202
193
  test_files: