backburner 1.3.1 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
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: