postburner 1.0.0.pre.15 → 1.0.0.pre.16

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
2
  SHA256:
3
- metadata.gz: 971531132068ea4bddfdb5b5a8aaae4c788ea576aae268010fb6cd4dbd2b82f1
4
- data.tar.gz: 44053985f2bebd385bc2a17eb1eb655d89de17d77a8c25b4a1210177cefead66
3
+ metadata.gz: 8e032f7d37f1680fe34a1402f8eb9f0b3ca61aa9231f6d540c35c3a919e3f58d
4
+ data.tar.gz: bd35338dd14bc499c4c7d9f57d30eff3902615cd72876f766f1e379f12baba77
5
5
  SHA512:
6
- metadata.gz: 19c023063d9eb06913fcb59eaaf581a537b8a944b8ab8713b95210475517223a42887f239bd8788cd313919ffe9bfb89c6c9f80c8b24869239b943bfd2c300ad
7
- data.tar.gz: 0e0ed766a67ddd9ef02c0b97eff3a25c1e923c79068608a4d31db779ae23187eb85a5b075e8100644faf1c196f0bafaf1a7711e09db8a948b0ab09e9138f9b49
6
+ metadata.gz: f207c1d4dd840bee6b00ad71de9cc1b774d9b2376f7f34125fcc3a164220a340badc6b1ef17931d3dc09a0bb78a09c66b1f1f9ffdf1ead3e9737c44a39fe9a29
7
+ data.tar.gz: e0212499c62223a87720462226660045585ac4bde21258eb54aeed9d85683c0c9652477a6cf5824bdffa7e30314efb1e1cbb9d146eae8ef3ade2202af73f2abe
data/README.md CHANGED
@@ -97,6 +97,7 @@ bundle exec rake postburner:work WORKER=default
97
97
  - [Postburner::Job](#postburnerjob)
98
98
  - [Scheduler](#scheduler)
99
99
  - [Job Management](#job-management)
100
+ - [Writing Jobs](#writing-jobs)
100
101
  - [Queue Strategies](#queue-strategies)
101
102
  - [Testing](#testing)
102
103
  - [Workers](#workers)
@@ -177,16 +178,45 @@ config.active_job.queue_adapter = :postburner
177
178
  #config.active_job.queue_name_prefix = Postburner.tube_prefix(Rails.env) # i.e. "postburner.#{Rails.env}"
178
179
  config.action_mailer.deliver_later_queue_name = 'mailers' # gets prefixed by config.active_job.queue_name_prefix
179
180
 
180
-
181
181
  bundle exec postburner # start with bin/postburner
182
182
  bundle exec rake postburner:work # or with rake task
183
183
  ```
184
184
 
185
- ## Usage
185
+ ### Enqueueing Jobs
186
186
 
187
- **Job Idempotency:** Jobs should be designed to be idempotent and safely re-runnable. Like all job queues, Postburner provides at-least-once delivery—in rare errant cases outside of Postburner's control, a job may be executed more than once, i.e. network issues, etc.
187
+ ```ruby
188
+ # ActiveJob (standard Rails API)
189
+ SendEmailJob.perform_later(user_id) # Enqueue immediately
190
+ SendEmailJob.set(wait: 1.hour).perform_later(user_id) # Delay by duration
191
+ SendEmailJob.set(wait_until: Date.tomorrow.noon).perform_later(user_id) # Run at specific time
192
+ SendEmailJob.set(queue: 'critical').perform_later(user_id) # Override queue
193
+ SendEmailJob.set(priority: 0).perform_later(user_id) # Override priority
194
+
195
+ # Postburner::Job (always tracked, full API)
196
+ job = ProcessPayment.create!(args: { 'payment_id' => 123 })
197
+ job.queue! # Enqueue immediately
198
+ job.queue!(delay: 1.hour) # Delay by duration
199
+ job.queue!(at: Date.tomorrow.noon) # Run at specific time
200
+ job.queue!(queue: 'critical') # Override queue
201
+ job.queue!(priority: 0, ttr: 600) # Set priority and TTR
202
+ ```
188
203
 
189
- **TTR (Time-to-Run):** If a job exceeds its TTR without completion, Beanstalkd releases it back to the queue while still running—causing duplicate execution. For long-running jobs, call `extend!` periodically to reset the TTR, or set a sufficiently high TTR value. You must include the `Postburner::Beanstalkd` or `Postburner::Tracked` module with `ActiveJob` to use `extend!`.
204
+ ### ActiveJob vs Postburner::Job TL;DR
205
+
206
+ `Postburner::Job` as simple subclass of `ActiveRecord`, so the normal
207
+ `ActiveRecord` API applies! Thus the workflow is to create an instance,
208
+ then queue it!
209
+
210
+ | Operation | ActiveJob | Postburner::Job |
211
+ |-----------|-----------|-----------------|
212
+ | **Enqueue immediately** | `MyJob.perform_later(args)` | `MyJob.create!(args: {}).queue!` |
213
+ | **Delay** | `.set(wait: 1.hour)` | `job.queue!(delay: 1.hour)` |
214
+ | **Run at** | `.set(wait_until: time)` | `job.queue!(at: time)` |
215
+ | **Set queue** | `.set(queue: 'critical')` | `job.queue!(queue: 'critical')` |
216
+ | **Set priority** | `.set(priority: 0)` | `job.queue!(priority: 0)` |
217
+ | **Set TTR** | `.set(ttr: 300)` | `job.queue!(ttr: 300)` |
218
+
219
+ ## Usage
190
220
 
191
221
  ### Default Jobs
192
222
 
@@ -335,7 +365,7 @@ job.duration # Execution time in milliseconds
335
365
  job.lag # Queue lag in milliseconds
336
366
  ```
337
367
 
338
- ### Direct Postburner::Job Usage
368
+ ### Postburner::Job Usage
339
369
 
340
370
  Direct `Postburner::Job` subclasses are **always tracked**:
341
371
 
@@ -363,6 +393,15 @@ job.queue!(delay: 1.hour)
363
393
  job.queue!(at: 2.days.from_now)
364
394
  ```
365
395
 
396
+ > **Note:** The `args` parameter in `perform(args)` is optional. It's a convenience accessor to `self.args`, which is stored in a JSONB column on the job record. You can omit the parameter and access args directly:
397
+ >
398
+ > ```ruby
399
+ > def perform
400
+ > payment = Payment.find(self.args['payment_id'])
401
+ > # ...
402
+ > end
403
+ > ```
404
+
366
405
  #### Instance-Level Queue Configuration
367
406
 
368
407
  Override queue priority and TTR per job instance for dynamic behavior:
@@ -775,6 +814,14 @@ job.errata # Array of exceptions with backtraces
775
814
  job.attempts # Array of attempt timestamps
776
815
  ```
777
816
 
817
+ ## Writing Jobs
818
+
819
+ Pay attention to the following when writing jobs:
820
+
821
+ **Job Idempotency:** Jobs should be designed to be idempotent and safely re-runnable. Like all job queues, Postburner provides at-least-once delivery—in rare errant cases outside of Postburner's control, a job may be executed more than once, i.e. network issues, etc.
822
+
823
+ **TTR (Time-to-Run):** If a job exceeds its TTR without completion, Beanstalkd releases it back to the queue while still running—causing duplicate execution. For long-running jobs, call `extend!` periodically to reset the TTR, or set a sufficiently high TTR value. You must include the `Postburner::Beanstalkd` or `Postburner::Tracked` module with `ActiveJob` to use `extend!`.
824
+
778
825
  ## Queue Strategies
779
826
 
780
827
  Postburner uses different strategies to control job execution. These affect `Postburner::Job` subclasses (not ActiveJob classes).
@@ -134,7 +134,7 @@ module Postburner
134
134
 
135
135
  run_callbacks :processing do
136
136
  begin
137
- self.perform(args)
137
+ method(:perform).arity == 0 ? self.perform : self.perform(args)
138
138
  rescue Exception => exception
139
139
  self.persist_metadata!
140
140
  self.log! '[Postburner] Exception raised during perform prevented completion.'
@@ -37,10 +37,12 @@ module Postburner
37
37
  # @param options [Hash] Queue options
38
38
  # @option options [Time, ActiveSupport::Duration] :at Absolute time to run the job
39
39
  # @option options [Integer, ActiveSupport::Duration] :delay Seconds to delay execution
40
- # @option options [Integer] :pri Beanstalkd priority (lower = higher priority)
41
- # @option options [Integer] :ttr Time-to-run in seconds before job times out
40
+ # @option options [Integer] :priority Priority (0-4294967295, 0 = HIGHEST), sets instance attribute
41
+ # @option options [Integer] :pri Beanstalkd priority (pass-through, for backwards compatibility)
42
+ # @option options [Integer] :ttr Time-to-run in seconds (1-4294967295, 0 is silently changed to 1)
43
+ # @option options [String] :queue Queue name override
42
44
  #
43
- # @return [void]
45
+ # @return [true] on success (including if already queued)
44
46
  #
45
47
  # @raise [ActiveRecord::RecordInvalid] if job is not valid
46
48
  # @raise [AlreadyProcessed] if job was already processed
@@ -57,17 +59,25 @@ module Postburner
57
59
  # job.queue!(at: '2025-01-15 09:00:00'.in_time_zone)
58
60
  # job.queue!(at: Time.parse('2025-01-15 09:00:00 EST'))
59
61
  #
60
- # @example Queue with priority
61
- # job.queue!(pri: 0, delay: 30.minutes)
62
+ # @example Queue with priority and TTR
63
+ # job.queue!(priority: 0, ttr: 600)
64
+ #
65
+ # @example Queue to specific queue
66
+ # job.queue!(queue: 'critical', delay: 30.minutes)
62
67
  #
63
68
  # @see #requeue!
64
69
  # @see Postburner.queue_strategy
65
70
  #
66
71
  def queue!(options={})
67
- return if self.queued_at.present? && self.bkid.present?
72
+ return true if self.queued_at.present? && self.bkid.present?
68
73
  raise ActiveRecord::RecordInvalid, "Can't queue unless valid." unless self.valid?
69
74
  raise AlreadyProcessed, "Processed at #{self.processed_at}" if self.processed_at
70
75
 
76
+ # Extract and set instance-level overrides
77
+ self.priority = options.delete(:priority) if options.key?(:priority)
78
+ self.ttr = options.delete(:ttr) if options.key?(:ttr)
79
+ self.queue_name = options.delete(:queue) if options.key?(:queue)
80
+
71
81
  at = options.delete(:at)
72
82
  now = Time.current
73
83
 
@@ -86,6 +96,8 @@ module Postburner
86
96
  run_callbacks :enqueue do
87
97
  self.save!
88
98
  end
99
+
100
+ true
89
101
  end
90
102
 
91
103
  # Re-queues an existing job by removing it from Beanstalkd and queueing again.
@@ -96,10 +108,11 @@ module Postburner
96
108
  # @param options [Hash] Queue options (same as {#queue!})
97
109
  # @option options [Time, ActiveSupport::Duration] :at Absolute time to run the job
98
110
  # @option options [Integer, ActiveSupport::Duration] :delay Seconds to delay execution
99
- # @option options [Integer] :pri Beanstalkd priority
100
- # @option options [Integer] :ttr Time-to-run in seconds
111
+ # @option options [Integer] :priority Priority (0-4294967295, lower = higher priority)
112
+ # @option options [Integer] :ttr Time-to-run in seconds (1-4294967295)
113
+ # @option options [String] :queue Queue name override
101
114
  #
102
- # @return [void]
115
+ # @return [true] on success
103
116
  #
104
117
  # @raise [ActiveRecord::RecordInvalid] if job is not valid
105
118
  # @raise [Beaneater::NotConnected] if Beanstalkd connection fails
@@ -23,7 +23,7 @@ module Postburner
23
23
 
24
24
  included do
25
25
  # Instance-level queue configuration (overrides class-level defaults)
26
- attr_writer :priority, :ttr
26
+ attr_writer :priority, :ttr, :queue_name
27
27
 
28
28
  class_attribute :postburner_queue_name, default: 'default'
29
29
  class_attribute :postburner_priority, default: nil
@@ -166,8 +166,13 @@ module Postburner
166
166
  # job = MyJob.create!(args: {})
167
167
  # job.queue_name # => 'critical'
168
168
  #
169
+ # @example Instance-level override
170
+ # job = MyJob.create!(args: {})
171
+ # job.queue_name = 'urgent'
172
+ # job.queue_name # => 'urgent'
173
+ #
169
174
  def queue_name
170
- self.class.queue
175
+ @queue_name || self.class.queue
171
176
  end
172
177
 
173
178
  # Returns the full tube name with environment prefix.
@@ -96,13 +96,13 @@ module Postburner
96
96
  #
97
97
  # @abstract Subclasses must implement this method
98
98
  #
99
- # @param args [Hash] Job arguments from the args JSONB column
99
+ # @param args [Hash] Job arguments from the args JSONB column (optional)
100
100
  #
101
101
  # @return [void]
102
102
  #
103
103
  # @raise [NotImplementedError] if subclass does not implement this method
104
104
  #
105
- # @example
105
+ # @example With args parameter
106
106
  # class ProcessPayment < Postburner::Job
107
107
  # def perform(args)
108
108
  # payment = Payment.find(args['payment_id'])
@@ -111,14 +111,23 @@ module Postburner
111
111
  # end
112
112
  # end
113
113
  #
114
+ # @example Without args parameter (access via self.args)
115
+ # class CleanupJob < Postburner::Job
116
+ # def perform
117
+ # log "Cleaning up #{self.args['table']}"
118
+ # # self.args is always available
119
+ # end
120
+ # end
121
+ #
114
122
  # @note Use {#log} or {#log!} within perform to add entries to the job's audit trail
115
123
  # @note Exceptions will be caught, logged to errata, and re-raised
124
+ # @note Args are always accessible via self.args regardless of method signature
116
125
  #
117
126
  # @see #perform!
118
127
  # @see #log
119
128
  # @see #log_exception
120
129
  #
121
- def perform(args)
130
+ def perform(args=nil)
122
131
  raise NotImplementedError, "Subclasses must implement the perform method"
123
132
  end
124
133
 
@@ -136,6 +136,16 @@ module Postburner
136
136
  # scheduler.perform
137
137
  #
138
138
  def perform
139
+ # Self-deduplicate: if another watchdog exists in the queue, exit early
140
+ # and let that one handle scheduling. This naturally resolves duplicate
141
+ # watchdogs that can occur from race conditions.
142
+ @skip_requeue = false
143
+ if another_watchdog_queued?
144
+ logger.info "[Postburner::Scheduler] Another watchdog already queued, exiting to deduplicate"
145
+ @skip_requeue = true
146
+ return
147
+ end
148
+
139
149
  logger.info "[Postburner::Scheduler] Starting scheduler run"
140
150
 
141
151
  ActiveSupport::Notifications.instrument('perform_start.scheduler.postburner', {
@@ -161,15 +171,24 @@ module Postburner
161
171
 
162
172
  # Use advisory lock to coordinate multiple workers
163
173
  ActiveSupport::Notifications.instrument('perform.scheduler.postburner', payload) do
164
- lock_acquired = Postburner::AdvisoryLock.with_lock(AdvisoryLock::SCHEDULER_LOCK_KEY, blocking: false) do
165
- process_all_schedules
166
- true
167
- end
168
-
169
- if lock_acquired
170
- logger.info "[Postburner::Scheduler] Scheduler run complete"
171
- else
172
- logger.info "[Postburner::Scheduler] Could not acquire lock, skipping"
174
+ begin
175
+ lock_acquired = Postburner::AdvisoryLock.with_lock(AdvisoryLock::SCHEDULER_LOCK_KEY, blocking: false) do
176
+ process_all_schedules
177
+ true
178
+ end
179
+
180
+ if lock_acquired
181
+ logger.info "[Postburner::Scheduler] Scheduler run complete"
182
+ else
183
+ logger.info "[Postburner::Scheduler] Could not acquire lock, skipping"
184
+ end
185
+ rescue ActiveRecord::ConnectionTimeoutError => e
186
+ # This can happen if the connection pool is exhausted
187
+ # Log cleanly and let the watchdog retry on next interval
188
+ logger.warn "[Postburner::Scheduler] Database connection pool exhausted. Skipping scheduler run (need advisory lock), will retry on next interval"
189
+ logger.debug "[Postburner::Scheduler] Check database.yml pool and max_connections i.e. pool >= needed connection count from web/job workers"
190
+ logger.debug "[Postburner::Scheduler] ActiveRecord Connection timeout: #{e.message}"
191
+ lock_acquired = false
173
192
  end
174
193
 
175
194
  # Update payload with final stats (mutates the hash subscribers receive)
@@ -180,8 +199,8 @@ module Postburner
180
199
  payload[:orphans_enqueued] = @orphans_enqueued
181
200
  end
182
201
  ensure
183
- # Always re-queue watchdog for next run
184
- requeue_watchdog
202
+ # Re-queue watchdog for next run (unless we're deduplicating)
203
+ requeue_watchdog unless @skip_requeue
185
204
  end
186
205
 
187
206
  # Class method to enqueue watchdog to Beanstalkd
@@ -321,6 +340,32 @@ module Postburner
321
340
 
322
341
  private
323
342
 
343
+ # Check if another watchdog is already queued in Beanstalkd.
344
+ #
345
+ # Used for self-deduplication: if this watchdog sees another one queued,
346
+ # it exits early and lets that one handle scheduling. This resolves
347
+ # duplicate watchdogs that can occur from race conditions.
348
+ #
349
+ # @return [Boolean] true if another watchdog exists (ready or delayed)
350
+ #
351
+ # @api private
352
+ #
353
+ def another_watchdog_queued?
354
+ Postburner.connected do |conn|
355
+ tube_name = Postburner.scheduler_tube_name
356
+ tube = conn.beanstalk.tubes[tube_name]
357
+ stats = tube.stats
358
+
359
+ # Check for ready or delayed jobs (not counting reserved, which is us)
360
+ # Note: beaneater transforms hyphenated beanstalkd stats to underscores
361
+ queued_count = stats.current_jobs_ready.to_i + stats.current_jobs_delayed.to_i
362
+ queued_count > 0
363
+ end
364
+ rescue => e
365
+ logger.debug "[Postburner::Scheduler] Could not check for duplicate watchdogs: #{e.message}"
366
+ false # Assume no duplicate if we can't check
367
+ end
368
+
324
369
  # Process all enabled schedules.
325
370
  #
326
371
  # Iterates through all enabled schedules and calls process_schedule for each.
@@ -472,6 +517,10 @@ module Postburner
472
517
 
473
518
  self.class.enqueue_watchdog
474
519
  end
520
+ rescue ActiveRecord::ConnectionTimeoutError => e
521
+ # Connection pool exhausted - workers will recreate watchdog on next timeout
522
+ logger.warn "[Postburner::Scheduler] Could not re-queue watchdog (connection pool exhausted), workers will recreate on timeout"
523
+ logger.debug "[Postburner::Scheduler] Connection timeout details: #{e.message}"
475
524
  rescue => e
476
525
  logger.error "[Postburner::Scheduler] Failed to re-queue watchdog: #{e.class} - #{e.message}"
477
526
  # This is critical - if watchdog isn't re-queued, scheduling stops
@@ -31,10 +31,9 @@ module Postburner
31
31
  # Just pass the last known id to after for the next batch.
32
32
  #
33
33
  def jobs(count=20, limit: 1000, after: nil)
34
- # Access raw hash to avoid beaneater FastStruct method definition issues in Ruby 3.4
34
+ # Note: beaneater transforms hyphenated beanstalkd stats to underscores
35
35
  stats = @tube.stats
36
- stats_hash = stats.instance_variable_get(:@hash) || {}
37
- tube_name = stats_hash['name']
36
+ tube_name = stats.name
38
37
 
39
38
  jobs = Array.new
40
39
 
@@ -48,8 +47,7 @@ module Postburner
48
47
  job = @tube.client.jobs.find(i)
49
48
  if job
50
49
  job_stats = job.stats
51
- job_stats_hash = job_stats.instance_variable_get(:@hash) || {}
52
- jobs << job if job_stats_hash['tube'] == tube_name
50
+ jobs << job if job_stats.tube == tube_name
53
51
  end
54
52
  break if jobs.length >= count
55
53
  end
@@ -1,3 +1,3 @@
1
1
  module Postburner
2
- VERSION = '1.0.0.pre.15'
2
+ VERSION = '1.0.0.pre.16'
3
3
  end
@@ -549,9 +549,10 @@ module Postburner
549
549
 
550
550
  # Handles job execution errors with retry logic.
551
551
  #
552
- # For tracked jobs and legacy Postburner::Job: Buries the job for inspection
553
- # and emits retry_stopped.job.postburner event.
554
- # For default ActiveJob: Applies exponential backoff retry with max 5 attempts.
552
+ # For tracked jobs and legacy Postburner::Job: Buries the job for inspection,
553
+ # reports to Rails.error, and emits retry_stopped.job.postburner event.
554
+ # For default ActiveJob: Applies exponential backoff retry with max 5 attempts,
555
+ # reporting to Rails.error only on final discard.
555
556
  #
556
557
  # Instruments with ActiveSupport::Notifications:
557
558
  # - retry_stopped.job.postburner: When tracked job is buried after failure
@@ -568,7 +569,22 @@ module Postburner
568
569
  payload = JSON.parse(beanstalk_job.body)
569
570
 
570
571
  if payload['tracked'] || Postburner::ActiveJob::Payload.legacy_format?(payload)
571
- logger.info "[Postburner] Burying tracked/legacy job for inspection"
572
+ # Extract job ID from payload
573
+ job_id = if Postburner::ActiveJob::Payload.legacy_format?(payload)
574
+ payload['args']&.first
575
+ else
576
+ payload['postburner_job_id']
577
+ end
578
+
579
+ logger.info "[Postburner] Burying tracked/legacy job for inspection (bkid: #{beanstalk_job.id}, job_id: #{job_id})"
580
+
581
+ # Report to Rails error reporter for integration with error tracking services
582
+ Rails.error.report(error, handled: false, context: {
583
+ job_class: payload['job_class'] || payload['class'],
584
+ job_id: job_id,
585
+ beanstalk_job_id: beanstalk_job.id,
586
+ queue_name: payload['queue_name']
587
+ })
572
588
 
573
589
  job_payload = Postburner::Instrumentation.job_payload_from_hash(payload, beanstalk_job_id: beanstalk_job.id)
574
590
  ActiveSupport::Notifications.instrument('retry_stopped.job.postburner', {
@@ -590,7 +606,8 @@ module Postburner
590
606
  # Handles retry logic for default jobs.
591
607
  #
592
608
  # Applies exponential backoff (2^retry_count seconds, max 1 hour).
593
- # After 5 failed attempts, discards the job permanently.
609
+ # After 5 failed attempts, discards the job permanently and reports
610
+ # to Rails.error for integration with error tracking services.
594
611
  #
595
612
  # Instruments with ActiveSupport::Notifications:
596
613
  # - retry.job.postburner: When job is retried
@@ -634,6 +651,15 @@ module Postburner
634
651
 
635
652
  logger.info "[Postburner] Retrying default job #{payload['job_id']}, attempt #{retry_count + 1} in #{delay}s"
636
653
  else
654
+ # Report to Rails error reporter for integration with error tracking services
655
+ Rails.error.report(error, handled: false, context: {
656
+ job_class: payload['job_class'],
657
+ job_id: payload['job_id'],
658
+ beanstalk_job_id: beanstalk_job.id,
659
+ queue_name: payload['queue_name'],
660
+ retry_count: retry_count
661
+ })
662
+
637
663
  ActiveSupport::Notifications.instrument('discard.job.postburner', {
638
664
  job: job_payload,
639
665
  beanstalk_job_id: beanstalk_job.id,
data/lib/postburner.rb CHANGED
@@ -559,19 +559,18 @@ module Postburner
559
559
  tubes_to_inspect.each do |tube|
560
560
  begin
561
561
  stats = tube.stats
562
- # Beaneater returns a StatStruct; access the underlying hash
563
- stats_hash = stats.instance_variable_get(:@hash) || {}
562
+ # Note: beaneater transforms hyphenated beanstalkd stats to underscores
564
563
 
565
564
  tube_data = {
566
565
  name: tube.name,
567
- ready: stats_hash['current_jobs_ready'] || 0,
568
- delayed: stats_hash['current_jobs_delayed'] || 0,
569
- buried: stats_hash['current_jobs_buried'] || 0,
570
- reserved: stats_hash['current_jobs_reserved'] || 0,
571
- total: (stats_hash['current_jobs_ready'] || 0) +
572
- (stats_hash['current_jobs_delayed'] || 0) +
573
- (stats_hash['current_jobs_buried'] || 0) +
574
- (stats_hash['current_jobs_reserved'] || 0)
566
+ ready: stats.current_jobs_ready || 0,
567
+ delayed: stats.current_jobs_delayed || 0,
568
+ buried: stats.current_jobs_buried || 0,
569
+ reserved: stats.current_jobs_reserved || 0,
570
+ total: (stats.current_jobs_ready || 0) +
571
+ (stats.current_jobs_delayed || 0) +
572
+ (stats.current_jobs_buried || 0) +
573
+ (stats.current_jobs_reserved || 0)
575
574
  }
576
575
  rescue Beaneater::NotFoundError
577
576
  # Tube doesn't exist yet, skip it
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: postburner
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.pre.15
4
+ version: 1.0.0.pre.16
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Smith