postburner 1.0.0.pre.3 → 1.0.0.pre.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +61 -67
- data/app/concerns/postburner/commands.rb +143 -0
- data/app/concerns/postburner/execution.rb +190 -0
- data/app/concerns/postburner/insertion.rb +170 -0
- data/app/concerns/postburner/logging.rb +181 -0
- data/{lib/postburner/queue_config.rb → app/concerns/postburner/properties.rb} +65 -4
- data/app/concerns/postburner/statistics.rb +125 -0
- data/app/models/postburner/job.rb +14 -756
- data/app/views/postburner/jobs/show.html.haml +2 -2
- data/lib/postburner/strategies/queue.rb +17 -7
- data/lib/postburner/version.rb +1 -1
- data/lib/postburner.rb +0 -1
- metadata +7 -2
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Postburner
|
|
4
|
+
# Concern providing queue insertion methods for Postburner jobs.
|
|
5
|
+
#
|
|
6
|
+
# Handles enqueuing jobs to Beanstalkd (or test strategies), including
|
|
7
|
+
# scheduling, re-queueing, and the internal insertion mechanics via
|
|
8
|
+
# after_save_commit callbacks.
|
|
9
|
+
#
|
|
10
|
+
# @example Basic queueing
|
|
11
|
+
# class MyJob < Postburner::Job
|
|
12
|
+
# def perform(args)
|
|
13
|
+
# # ... work ...
|
|
14
|
+
# end
|
|
15
|
+
# end
|
|
16
|
+
#
|
|
17
|
+
# job = MyJob.create!(args: { foo: 'bar' })
|
|
18
|
+
# job.queue!
|
|
19
|
+
#
|
|
20
|
+
# @example Scheduled queueing
|
|
21
|
+
# job.queue!(delay: 1.hour)
|
|
22
|
+
# job.queue!(at: 2.days.from_now)
|
|
23
|
+
#
|
|
24
|
+
module Insertion
|
|
25
|
+
extend ActiveSupport::Concern
|
|
26
|
+
|
|
27
|
+
# Enqueues the job to Beanstalkd for processing.
|
|
28
|
+
#
|
|
29
|
+
# Sets queued_at timestamp and optionally run_at for scheduled execution.
|
|
30
|
+
# Triggers enqueue callbacks and inserts job into Beanstalkd via after_save_commit hook.
|
|
31
|
+
# In test mode, executes immediately instead of queueing to Beanstalkd.
|
|
32
|
+
#
|
|
33
|
+
# @param options [Hash] Queue options
|
|
34
|
+
# @option options [Time, ActiveSupport::Duration] :at Absolute time to run the job
|
|
35
|
+
# @option options [Integer, ActiveSupport::Duration] :delay Seconds to delay execution
|
|
36
|
+
# @option options [Integer] :pri Beanstalkd priority (lower = higher priority)
|
|
37
|
+
# @option options [Integer] :ttr Time-to-run in seconds before job times out
|
|
38
|
+
#
|
|
39
|
+
# @return [void]
|
|
40
|
+
#
|
|
41
|
+
# @raise [ActiveRecord::RecordInvalid] if job is not valid
|
|
42
|
+
# @raise [AlreadyProcessed] if job was already processed
|
|
43
|
+
#
|
|
44
|
+
# @example Queue immediately
|
|
45
|
+
# job.queue!
|
|
46
|
+
#
|
|
47
|
+
# @example Queue with delay
|
|
48
|
+
# job.queue!(delay: 1.hour)
|
|
49
|
+
#
|
|
50
|
+
# @example Queue at specific time
|
|
51
|
+
# job.queue!(at: Time.zone.now + 2.days)
|
|
52
|
+
#
|
|
53
|
+
# @example Queue with priority
|
|
54
|
+
# job.queue!(pri: 0, delay: 30.minutes)
|
|
55
|
+
#
|
|
56
|
+
# @see #requeue!
|
|
57
|
+
# @see Postburner.queue_strategy
|
|
58
|
+
#
|
|
59
|
+
def queue!(options={})
|
|
60
|
+
return if self.queued_at.present? && self.bkid.present?
|
|
61
|
+
raise ActiveRecord::RecordInvalid, "Can't queue unless valid." unless self.valid?
|
|
62
|
+
raise AlreadyProcessed, "Processed at #{self.processed_at}" if self.processed_at
|
|
63
|
+
|
|
64
|
+
at = options.delete(:at)
|
|
65
|
+
now = Time.zone.now
|
|
66
|
+
|
|
67
|
+
self.queued_at = now
|
|
68
|
+
self.run_at = case
|
|
69
|
+
when at.present?
|
|
70
|
+
# this is rudimentary, add error handling
|
|
71
|
+
options[:delay] ||= at.to_i - now.to_i
|
|
72
|
+
at
|
|
73
|
+
when options[:delay].present?
|
|
74
|
+
now + options[:delay].seconds
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
@_insert_options = options
|
|
78
|
+
|
|
79
|
+
run_callbacks :enqueue do
|
|
80
|
+
self.save!
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Re-queues an existing job by removing it from Beanstalkd and queueing again.
|
|
85
|
+
#
|
|
86
|
+
# Calls {#delete!} to remove from Beanstalkd, resets queuing metadata,
|
|
87
|
+
# then calls {#queue!} with new options.
|
|
88
|
+
#
|
|
89
|
+
# @param options [Hash] Queue options (same as {#queue!})
|
|
90
|
+
# @option options [Time, ActiveSupport::Duration] :at Absolute time to run the job
|
|
91
|
+
# @option options [Integer, ActiveSupport::Duration] :delay Seconds to delay execution
|
|
92
|
+
# @option options [Integer] :pri Beanstalkd priority
|
|
93
|
+
# @option options [Integer] :ttr Time-to-run in seconds
|
|
94
|
+
#
|
|
95
|
+
# @return [void]
|
|
96
|
+
#
|
|
97
|
+
# @raise [ActiveRecord::RecordInvalid] if job is not valid
|
|
98
|
+
# @raise [Beaneater::NotConnected] if Beanstalkd connection fails
|
|
99
|
+
#
|
|
100
|
+
# @example Requeue with different delay
|
|
101
|
+
# job.requeue!(delay: 5.minutes)
|
|
102
|
+
#
|
|
103
|
+
# @see #queue!
|
|
104
|
+
# @see #delete!
|
|
105
|
+
#
|
|
106
|
+
def requeue!(options={})
|
|
107
|
+
self.delete!
|
|
108
|
+
self.bkid, self.queued_at = nil, nil
|
|
109
|
+
|
|
110
|
+
self.queue! options
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Checks if job is flagged for insertion into Beanstalkd.
|
|
114
|
+
#
|
|
115
|
+
# Set internally by {#queue!} to trigger insertion via after_save_commit hook.
|
|
116
|
+
#
|
|
117
|
+
# @return [Boolean] true if job will be inserted on save
|
|
118
|
+
# @api private
|
|
119
|
+
#
|
|
120
|
+
def will_insert?
|
|
121
|
+
@_insert_options.is_a? Hash
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
private
|
|
125
|
+
|
|
126
|
+
# After-save-commit callback that inserts job into queue if flagged.
|
|
127
|
+
#
|
|
128
|
+
# Checks {#will_insert?} and calls {#insert!} if true. Triggered by
|
|
129
|
+
# after_save_commit callback after {#queue!} saves the record.
|
|
130
|
+
#
|
|
131
|
+
# @return [void]
|
|
132
|
+
#
|
|
133
|
+
# @api private
|
|
134
|
+
#
|
|
135
|
+
def insert_if_queued!
|
|
136
|
+
#debugger
|
|
137
|
+
return unless self.will_insert?
|
|
138
|
+
insert!(@_insert_options)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Inserts job into queue via current queue strategy.
|
|
142
|
+
#
|
|
143
|
+
# Delegates to Postburner.queue_strategy.insert which handles actual
|
|
144
|
+
# queueing (Beanstalkd in production, inline execution in test mode).
|
|
145
|
+
# Updates bkid if the strategy returns a Beanstalkd job ID.
|
|
146
|
+
#
|
|
147
|
+
# @param options [Hash] Queue options (delay, pri, ttr, etc.)
|
|
148
|
+
#
|
|
149
|
+
# @return [Hash, nil] Queue strategy response
|
|
150
|
+
#
|
|
151
|
+
# @api private
|
|
152
|
+
#
|
|
153
|
+
def insert!(options={})
|
|
154
|
+
response = Postburner.queue_strategy.insert(self, options)
|
|
155
|
+
#debugger
|
|
156
|
+
|
|
157
|
+
# Response must be a hash with an :id key (value can be nil)
|
|
158
|
+
# Backburner returns symbol keys
|
|
159
|
+
unless response.is_a?(Hash) && response.key?(:id)
|
|
160
|
+
raise MalformedResponse, "Missing :id key in response: #{response.inspect}"
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
persist_metadata!(bkid: response[:id])
|
|
164
|
+
|
|
165
|
+
self.log("QUEUED: #{response}") if response
|
|
166
|
+
|
|
167
|
+
response
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Postburner
|
|
4
|
+
# Concern providing logging and exception tracking for Postburner jobs.
|
|
5
|
+
#
|
|
6
|
+
# Provides methods for adding timestamped log entries and tracking exceptions
|
|
7
|
+
# in the job's PostgreSQL audit trail. All logs are stored in JSONB arrays
|
|
8
|
+
# with timestamps, levels, and elapsed time tracking.
|
|
9
|
+
#
|
|
10
|
+
# @example Basic logging
|
|
11
|
+
# class MyJob < Postburner::Job
|
|
12
|
+
# def perform(args)
|
|
13
|
+
# log "Starting processing"
|
|
14
|
+
# # ... work ...
|
|
15
|
+
# log! "Critical checkpoint reached"
|
|
16
|
+
# end
|
|
17
|
+
# end
|
|
18
|
+
#
|
|
19
|
+
# @example Exception tracking
|
|
20
|
+
# class MyJob < Postburner::Job
|
|
21
|
+
# def perform(args)
|
|
22
|
+
# process_data(args)
|
|
23
|
+
# rescue => e
|
|
24
|
+
# log_exception!(e)
|
|
25
|
+
# raise
|
|
26
|
+
# end
|
|
27
|
+
# end
|
|
28
|
+
#
|
|
29
|
+
module Logging
|
|
30
|
+
extend ActiveSupport::Concern
|
|
31
|
+
|
|
32
|
+
included do
|
|
33
|
+
# Valid log levels in order of severity
|
|
34
|
+
LOG_LEVELS = [
|
|
35
|
+
:debug,
|
|
36
|
+
:info,
|
|
37
|
+
:warning,
|
|
38
|
+
:error
|
|
39
|
+
].freeze
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Tracks an exception in the job's errata array.
|
|
43
|
+
#
|
|
44
|
+
# Appends exception details to the in-memory errata array with timestamp,
|
|
45
|
+
# class, message, and backtrace. Does NOT persist to database immediately.
|
|
46
|
+
# Use {#log_exception!} to persist immediately.
|
|
47
|
+
#
|
|
48
|
+
# @param exception [Exception] The exception to track
|
|
49
|
+
#
|
|
50
|
+
# @return [Array<Array>] Updated errata array
|
|
51
|
+
#
|
|
52
|
+
# @example
|
|
53
|
+
# begin
|
|
54
|
+
# # ... risky operation ...
|
|
55
|
+
# rescue => e
|
|
56
|
+
# log_exception(e)
|
|
57
|
+
# raise
|
|
58
|
+
# end
|
|
59
|
+
#
|
|
60
|
+
# @see #log_exception!
|
|
61
|
+
#
|
|
62
|
+
def log_exception(exception)
|
|
63
|
+
self.errata << [
|
|
64
|
+
Time.zone.now,
|
|
65
|
+
{
|
|
66
|
+
bkid: self.bkid,
|
|
67
|
+
class: exception.class,
|
|
68
|
+
message: exception.message,
|
|
69
|
+
backtrace: exception.backtrace,
|
|
70
|
+
}
|
|
71
|
+
]
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Tracks an exception and immediately persists to database.
|
|
75
|
+
#
|
|
76
|
+
# Calls {#log_exception} to append exception details, then persists
|
|
77
|
+
# both errata and error_count to database via {#persist_metadata!}.
|
|
78
|
+
#
|
|
79
|
+
# @param exception [Exception] The exception to track
|
|
80
|
+
#
|
|
81
|
+
# @return [void]
|
|
82
|
+
#
|
|
83
|
+
# @example
|
|
84
|
+
# begin
|
|
85
|
+
# # ... risky operation ...
|
|
86
|
+
# rescue => e
|
|
87
|
+
# log_exception!(e)
|
|
88
|
+
# raise
|
|
89
|
+
# end
|
|
90
|
+
#
|
|
91
|
+
# @see #log_exception
|
|
92
|
+
#
|
|
93
|
+
def log_exception!(exception)
|
|
94
|
+
self.log_exception(exception)
|
|
95
|
+
self.persist_metadata!
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Appends a log message to the job's logs array.
|
|
99
|
+
#
|
|
100
|
+
# Adds timestamped log entry to in-memory logs array with bkid, level,
|
|
101
|
+
# message, and elapsed time. Does NOT persist to database immediately.
|
|
102
|
+
# Use {#log!} to persist immediately.
|
|
103
|
+
#
|
|
104
|
+
# @param message [String] Log message to append
|
|
105
|
+
# @param options [Hash] Log options
|
|
106
|
+
# @option options [Symbol] :level Log level (:debug, :info, :warning, :error) - defaults to :info
|
|
107
|
+
#
|
|
108
|
+
# @return [Array<Array>] Updated logs array
|
|
109
|
+
#
|
|
110
|
+
# @note Invalid log levels are coerced to :error
|
|
111
|
+
#
|
|
112
|
+
# @example
|
|
113
|
+
# log "Processing started"
|
|
114
|
+
# log "Warning: rate limit approaching", level: :warning
|
|
115
|
+
# log "Critical error occurred", level: :error
|
|
116
|
+
#
|
|
117
|
+
# @see #log!
|
|
118
|
+
# @see #elapsed_ms
|
|
119
|
+
# @see LOG_LEVELS
|
|
120
|
+
#
|
|
121
|
+
def log(message, options={})
|
|
122
|
+
options[:level] ||= :info
|
|
123
|
+
options[:level] = :error unless LOG_LEVELS.member?(options[:level])
|
|
124
|
+
|
|
125
|
+
self.logs << [
|
|
126
|
+
Time.zone.now, # time
|
|
127
|
+
{
|
|
128
|
+
bkid: self.bkid,
|
|
129
|
+
level: options[:level], # level
|
|
130
|
+
message: message, # message
|
|
131
|
+
elapsed: self.elapsed_ms, # ms from start
|
|
132
|
+
}
|
|
133
|
+
]
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Appends a log message and immediately persists to database.
|
|
137
|
+
#
|
|
138
|
+
# Calls {#log} to append message, then persists logs array to database.
|
|
139
|
+
# Use this for important log messages that should be saved immediately.
|
|
140
|
+
#
|
|
141
|
+
# @param message [String] Log message to append
|
|
142
|
+
# @param options [Hash] Log options
|
|
143
|
+
# @option options [Symbol] :level Log level (:debug, :info, :warning, :error)
|
|
144
|
+
#
|
|
145
|
+
# @return [void]
|
|
146
|
+
#
|
|
147
|
+
# @example
|
|
148
|
+
# log! "Job started"
|
|
149
|
+
# log! "Payment processed successfully", level: :info
|
|
150
|
+
# log! "Retrying failed request", level: :warning
|
|
151
|
+
#
|
|
152
|
+
# @see #log
|
|
153
|
+
#
|
|
154
|
+
def log!(message, options={})
|
|
155
|
+
self.log(message, options)
|
|
156
|
+
self.update_column :logs, self.logs
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
private
|
|
160
|
+
|
|
161
|
+
# Persists job metadata (errata, logs, and counts) to database.
|
|
162
|
+
#
|
|
163
|
+
# Updates errata, error_count, logs, and log_count columns, plus any
|
|
164
|
+
# additional data passed in. Used internally for atomic metadata updates.
|
|
165
|
+
#
|
|
166
|
+
# @param data [Hash] Additional columns to update
|
|
167
|
+
#
|
|
168
|
+
# @return [void]
|
|
169
|
+
#
|
|
170
|
+
# @api private
|
|
171
|
+
#
|
|
172
|
+
def persist_metadata!(data={})
|
|
173
|
+
self.update_columns({
|
|
174
|
+
errata: self.errata,
|
|
175
|
+
error_count: self.errata.length,
|
|
176
|
+
logs: self.logs,
|
|
177
|
+
log_count: self.logs.length,
|
|
178
|
+
}.merge(data))
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
end
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Postburner
|
|
4
|
-
# Queue
|
|
4
|
+
# Queue properties module for Postburner::Job classes.
|
|
5
5
|
#
|
|
6
6
|
# Provides DSL methods for configuring queue behavior (name, priority, TTR, retries).
|
|
7
|
-
#
|
|
8
|
-
# with ActiveSupport::Callbacks.
|
|
7
|
+
# Defines configurable properties for job queue management.
|
|
9
8
|
#
|
|
10
9
|
# @example Basic usage
|
|
11
10
|
# class ProcessPayment < Postburner::Job
|
|
@@ -19,10 +18,13 @@ module Postburner
|
|
|
19
18
|
# end
|
|
20
19
|
# end
|
|
21
20
|
#
|
|
22
|
-
module
|
|
21
|
+
module Properties
|
|
23
22
|
extend ActiveSupport::Concern
|
|
24
23
|
|
|
25
24
|
included do
|
|
25
|
+
# Instance-level queue configuration (overrides class-level defaults)
|
|
26
|
+
attr_writer :priority, :ttr
|
|
27
|
+
|
|
26
28
|
class_attribute :postburner_queue_name, default: 'default'
|
|
27
29
|
class_attribute :postburner_priority, default: nil
|
|
28
30
|
class_attribute :postburner_ttr, default: nil
|
|
@@ -150,5 +152,64 @@ module Postburner
|
|
|
150
152
|
end
|
|
151
153
|
end
|
|
152
154
|
end
|
|
155
|
+
|
|
156
|
+
# Returns the queue name for this job instance.
|
|
157
|
+
#
|
|
158
|
+
# Checks instance-level override first, then falls back to class-level configuration.
|
|
159
|
+
#
|
|
160
|
+
# @return [String] Queue name
|
|
161
|
+
#
|
|
162
|
+
# @example Class-level configuration
|
|
163
|
+
# class MyJob < Postburner::Job
|
|
164
|
+
# queue 'critical'
|
|
165
|
+
# end
|
|
166
|
+
# job = MyJob.create!(args: {})
|
|
167
|
+
# job.queue_name # => 'critical'
|
|
168
|
+
#
|
|
169
|
+
def queue_name
|
|
170
|
+
self.class.queue
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# Returns the priority for this job instance.
|
|
174
|
+
#
|
|
175
|
+
# Checks instance-level override first, then falls back to class-level configuration.
|
|
176
|
+
#
|
|
177
|
+
# @return [Integer, nil] Priority (lower = higher priority)
|
|
178
|
+
#
|
|
179
|
+
# @example Instance-level override
|
|
180
|
+
# job = MyJob.create!(args: {}, priority: 1500)
|
|
181
|
+
# job.priority # => 1500
|
|
182
|
+
#
|
|
183
|
+
def priority
|
|
184
|
+
@priority || self.class.priority
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
# Returns the TTR (time-to-run) for this job instance.
|
|
188
|
+
#
|
|
189
|
+
# Checks instance-level override first, then falls back to class-level configuration.
|
|
190
|
+
#
|
|
191
|
+
# @return [Integer, nil] TTR in seconds
|
|
192
|
+
#
|
|
193
|
+
# @example Instance-level override
|
|
194
|
+
# job = MyJob.create!(args: {}, ttr: 600)
|
|
195
|
+
# job.ttr # => 600
|
|
196
|
+
#
|
|
197
|
+
def ttr
|
|
198
|
+
@ttr || self.class.ttr
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
# Returns the full tube name with environment prefix.
|
|
202
|
+
#
|
|
203
|
+
# Expands the queue name to include the environment prefix
|
|
204
|
+
# (e.g., 'critical' becomes 'postburner.development.critical').
|
|
205
|
+
#
|
|
206
|
+
# @return [String] Full tube name with environment prefix
|
|
207
|
+
#
|
|
208
|
+
# @example
|
|
209
|
+
# job.expanded_tube_name # => 'postburner.development.critical'
|
|
210
|
+
#
|
|
211
|
+
def expanded_tube_name
|
|
212
|
+
Postburner.configuration.expand_tube_name(queue_name)
|
|
213
|
+
end
|
|
153
214
|
end
|
|
154
215
|
end
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Postburner
|
|
4
|
+
# Concern providing statistics and metrics methods for Postburner jobs.
|
|
5
|
+
#
|
|
6
|
+
# Provides methods for calculating timing metrics, retrieving job statistics,
|
|
7
|
+
# and determining intended execution times for scheduled jobs.
|
|
8
|
+
#
|
|
9
|
+
# @example Get job statistics
|
|
10
|
+
# job.stats
|
|
11
|
+
# # => { id: 5, bkid: 1, queue: "default", ... }
|
|
12
|
+
#
|
|
13
|
+
# @example Check elapsed time
|
|
14
|
+
# job.elapsed_ms # => 1234.567
|
|
15
|
+
#
|
|
16
|
+
module Statistics
|
|
17
|
+
extend ActiveSupport::Concern
|
|
18
|
+
|
|
19
|
+
# Returns job statistics including Beanstalkd job state.
|
|
20
|
+
#
|
|
21
|
+
# Fetches current job state from Beanstalkd and returns combined statistics
|
|
22
|
+
# about the job's PostgreSQL record and its current Beanstalkd status.
|
|
23
|
+
#
|
|
24
|
+
# @return [Hash] Statistics hash with the following keys:
|
|
25
|
+
# - id: PostgreSQL job ID
|
|
26
|
+
# - bkid: Beanstalkd job ID
|
|
27
|
+
# - queue: Queue name configured for this job class (e.g., 'sleep-jobs')
|
|
28
|
+
# - tube: Derived tube name with environment prefix (e.g., 'postburner.development.sleep-jobs')
|
|
29
|
+
# - watched: Boolean indicating if tube is in configured watch list
|
|
30
|
+
# - beanstalk: Hash of Beanstalkd job statistics:
|
|
31
|
+
# - id: Beanstalkd job ID
|
|
32
|
+
# - tube: Tube name where job resides
|
|
33
|
+
# - state: Job state (ready, reserved, delayed, buried)
|
|
34
|
+
# - pri: Priority (0 = highest, 4294967295 = lowest)
|
|
35
|
+
# - age: Seconds since job was created
|
|
36
|
+
# - delay: Seconds remaining before job becomes ready (0 if ready now)
|
|
37
|
+
# - ttr: Time-to-run in seconds (time allowed for job processing)
|
|
38
|
+
# - time_left: Seconds remaining before job times out (0 if not reserved)
|
|
39
|
+
# - file: Binlog file number containing job
|
|
40
|
+
# - reserves: Number of times job has been reserved
|
|
41
|
+
# - timeouts: Number of times job has timed out during processing
|
|
42
|
+
# - releases: Number of times job has been released back to ready
|
|
43
|
+
# - buries: Number of times job has been buried
|
|
44
|
+
# - kicks: Number of times job has been kicked from buried/delayed
|
|
45
|
+
#
|
|
46
|
+
# @raise [Beaneater::NotFoundError] if job no longer exists in Beanstalkd
|
|
47
|
+
#
|
|
48
|
+
# @example
|
|
49
|
+
# job.stats
|
|
50
|
+
# # => {
|
|
51
|
+
# # id: 5,
|
|
52
|
+
# # bkid: 1,
|
|
53
|
+
# # queue: "sleep-jobs",
|
|
54
|
+
# # tube: "postburner.development.sleep-jobs",
|
|
55
|
+
# # watched: false,
|
|
56
|
+
# # beanstalk: {
|
|
57
|
+
# # id: 1,
|
|
58
|
+
# # tube: "postburner.development.sleep-jobs",
|
|
59
|
+
# # state: "ready",
|
|
60
|
+
# # pri: 50,
|
|
61
|
+
# # age: 1391,
|
|
62
|
+
# # delay: 0,
|
|
63
|
+
# # ttr: 120,
|
|
64
|
+
# # time_left: 0,
|
|
65
|
+
# # file: 0,
|
|
66
|
+
# # reserves: 0,
|
|
67
|
+
# # timeouts: 0,
|
|
68
|
+
# # releases: 0,
|
|
69
|
+
# # buries: 0,
|
|
70
|
+
# # kicks: 0
|
|
71
|
+
# # }
|
|
72
|
+
# # }
|
|
73
|
+
#
|
|
74
|
+
def stats
|
|
75
|
+
# Get configured watched tubes (expanded with environment prefix)
|
|
76
|
+
watched = Postburner.watched_tube_names.include?(self.expanded_tube_name)
|
|
77
|
+
|
|
78
|
+
{
|
|
79
|
+
id: self.id,
|
|
80
|
+
bkid: self.bkid,
|
|
81
|
+
queue: queue_name,
|
|
82
|
+
tube: expanded_tube_name,
|
|
83
|
+
watched: watched,
|
|
84
|
+
beanstalk: self.bk.stats.to_h.symbolize_keys,
|
|
85
|
+
}
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Returns elapsed time in milliseconds since job execution started.
|
|
89
|
+
#
|
|
90
|
+
# Calculates milliseconds between attempting_at and current time.
|
|
91
|
+
# Used to track how long a job has been executing.
|
|
92
|
+
#
|
|
93
|
+
# @return [Float, nil] Milliseconds since attempting_at, or nil if not yet attempting
|
|
94
|
+
#
|
|
95
|
+
# @example
|
|
96
|
+
# elapsed_ms # => 1234.567 (job has been running for ~1.2 seconds)
|
|
97
|
+
#
|
|
98
|
+
# @see #log
|
|
99
|
+
#
|
|
100
|
+
def elapsed_ms
|
|
101
|
+
return unless self.attempting_at
|
|
102
|
+
(Time.zone.now - self.attempting_at) * 1000
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Returns the intended execution time for the job.
|
|
106
|
+
#
|
|
107
|
+
# Returns run_at if scheduled, otherwise returns queued_at.
|
|
108
|
+
# Used for calculating lag (delay between intended and actual execution).
|
|
109
|
+
#
|
|
110
|
+
# @return [Time] Intended execution timestamp
|
|
111
|
+
#
|
|
112
|
+
# @example
|
|
113
|
+
# job.queue!(delay: 1.hour)
|
|
114
|
+
# job.intended_at # => 1 hour from now (run_at)
|
|
115
|
+
#
|
|
116
|
+
# job.queue!
|
|
117
|
+
# job.intended_at # => now (queued_at)
|
|
118
|
+
#
|
|
119
|
+
# @see #lag
|
|
120
|
+
#
|
|
121
|
+
def intended_at
|
|
122
|
+
self.run_at ? self.run_at : self.queued_at
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|