postburner 1.0.0.pre.12 → 1.0.0.pre.14
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 +177 -82
- data/app/concerns/postburner/insertion.rb +20 -1
- data/app/models/postburner/schedule.rb +69 -0
- data/app/models/postburner/schedule_execution.rb +19 -0
- data/bin/postburner +13 -5
- data/lib/generators/postburner/install/templates/config/postburner.yml +24 -139
- data/lib/postburner/active_job/adapter.rb +98 -17
- data/lib/postburner/configuration.rb +5 -0
- data/lib/postburner/instrumentation.rb +196 -0
- data/lib/postburner/scheduler.rb +74 -20
- data/lib/postburner/strategies/{nice_queue.rb → default_queue.rb} +10 -10
- data/lib/postburner/strategies/{test_queue.rb → inline_test_queue.rb} +10 -7
- data/lib/postburner/strategies/{queue.rb → strict_queue.rb} +12 -12
- data/lib/postburner/strategies/{immediate_test_queue.rb → time_travel_test_queue.rb} +15 -12
- data/lib/postburner/test_helpers.rb +133 -0
- data/lib/postburner/version.rb +1 -1
- data/lib/postburner/worker.rb +26 -12
- data/lib/postburner.rb +51 -49
- metadata +7 -5
|
@@ -1,153 +1,38 @@
|
|
|
1
|
-
# Postburner Configuration
|
|
1
|
+
# Postburner Configuration
|
|
2
2
|
#
|
|
3
|
-
#
|
|
3
|
+
# See docs/configuration.md for advanced options including:
|
|
4
|
+
# - Multiple workers with different concurrency profiles
|
|
5
|
+
# - Fork/thread tuning for production deployments
|
|
6
|
+
# - Scheduler configuration
|
|
4
7
|
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
# Each worker can have different fork/thread settings and process different queues.
|
|
12
|
-
# This enables running different queue groups in separate OS processes with distinct
|
|
13
|
-
# concurrency profiles.
|
|
14
|
-
#
|
|
15
|
-
# ## Puma-Style Architecture
|
|
16
|
-
#
|
|
17
|
-
# - **forks: 0** = Single process with thread pool (development/staging)
|
|
18
|
-
# - **forks: 1+** = Multiple processes with thread pools (production)
|
|
19
|
-
#
|
|
20
|
-
# Scale by adjusting forks and threads per worker:
|
|
21
|
-
# - Development: forks=0, threads=1 (single-threaded, easiest debugging)
|
|
22
|
-
# - Staging: forks=0, threads=10 (multi-threaded, moderate load)
|
|
23
|
-
# - Production: forks=4, threads=10 (40 concurrent jobs per worker)
|
|
8
|
+
# Start the worker with:
|
|
9
|
+
# bin/postburner
|
|
10
|
+
# bin/postburner --worker default
|
|
11
|
+
# rake postburner:work
|
|
12
|
+
# rake postburner:work WORKER=default
|
|
24
13
|
#
|
|
14
|
+
# With a single worker defined, it's auto-selected. Add more workers under
|
|
15
|
+
# `workers:` for different queue sets (see docs/configuration.md).
|
|
25
16
|
|
|
26
|
-
|
|
27
|
-
# Beanstalkd connection URL
|
|
28
|
-
# Override with ENV['BEANSTALK_URL'] if set
|
|
17
|
+
shared: &shared
|
|
29
18
|
beanstalk_url: <%= ENV['BEANSTALK_URL'] || 'beanstalk://localhost:11300' %>
|
|
30
|
-
|
|
31
|
-
# Scheduler configuration
|
|
32
|
-
# The scheduler runs as a lightweight "watchdog" job that checks for due
|
|
33
|
-
# schedule executions and enqueues them. All workers automatically watch
|
|
34
|
-
# the scheduler queue and will process the watchdog when it becomes due.
|
|
35
|
-
default_scheduler_interval: 300 # Check for due schedules every 5 minutes (300 seconds)
|
|
36
|
-
default_scheduler_priority: 100 # Scheduler watchdog priority (lower = higher priority)
|
|
37
|
-
|
|
38
|
-
development:
|
|
39
|
-
<<: *default
|
|
40
|
-
|
|
41
19
|
workers:
|
|
42
20
|
default:
|
|
43
|
-
# Single-threaded, single process (simplest for debugging)
|
|
44
|
-
# If not specified, uses env-level defaults
|
|
45
21
|
queues:
|
|
46
22
|
- default
|
|
47
23
|
- mailers
|
|
48
24
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
workers:
|
|
53
|
-
default:
|
|
54
|
-
# Test mode uses inline strategies automatically
|
|
55
|
-
queues:
|
|
56
|
-
- default
|
|
57
|
-
|
|
58
|
-
staging: # <- environment config
|
|
59
|
-
<<: *default
|
|
60
|
-
|
|
61
|
-
# Env-level defaults (use default_ prefix)
|
|
62
|
-
default_threads: 10
|
|
63
|
-
default_gc_limit: 5000
|
|
64
|
-
|
|
65
|
-
workers: # <- worker config overrides
|
|
66
|
-
default:
|
|
67
|
-
# Multi-threaded, single process (moderate concurrency)
|
|
68
|
-
# Uses env-level defaults: default_threads=10, default_gc_limit=5000
|
|
69
|
-
queues:
|
|
70
|
-
- critical
|
|
71
|
-
- default
|
|
72
|
-
- mailers
|
|
25
|
+
# Single process, single thread (forks: 0, threads: 1)
|
|
26
|
+
development:
|
|
27
|
+
<<: *shared
|
|
73
28
|
|
|
74
|
-
|
|
75
|
-
|
|
29
|
+
# Inline execution (jobs run immediately in-process)
|
|
30
|
+
test:
|
|
31
|
+
<<: *shared
|
|
76
32
|
|
|
77
|
-
|
|
33
|
+
# Multi-process, multi-thread (forks: 2, threads: 4)
|
|
34
|
+
production:
|
|
35
|
+
<<: *shared
|
|
78
36
|
default_forks: 2
|
|
79
|
-
default_threads:
|
|
80
|
-
default_gc_limit:
|
|
81
|
-
|
|
82
|
-
# Example 1: Single worker using env defaults
|
|
83
|
-
# Run: bin/postburner
|
|
84
|
-
#
|
|
85
|
-
# workers:
|
|
86
|
-
# default:
|
|
87
|
-
# # Uses default_forks=2, default_threads=10
|
|
88
|
-
# queues:
|
|
89
|
-
# - critical
|
|
90
|
-
# - default
|
|
91
|
-
# - mailers
|
|
92
|
-
# - imports
|
|
93
|
-
|
|
94
|
-
# Example 2: Multiple workers with different concurrency profiles
|
|
95
|
-
# Run separate processes:
|
|
96
|
-
# bin/postburner --worker imports (4 forks, 1 thread each)
|
|
97
|
-
# bin/postburner --worker general (2 forks, 100 threads each)
|
|
98
|
-
#
|
|
99
|
-
workers: # <- worker config, i.e. overrides, NOT environment config
|
|
100
|
-
# Heavy, memory-intensive jobs - more processes, fewer threads
|
|
101
|
-
imports:
|
|
102
|
-
forks: 4 # Overrides default_forks
|
|
103
|
-
threads: 1 # Overrides default_threads
|
|
104
|
-
gc_limit: 500 # Overrides default_gc_limit
|
|
105
|
-
queues:
|
|
106
|
-
- imports
|
|
107
|
-
- data_processing
|
|
108
|
-
|
|
109
|
-
# General jobs - uses env defaults (forks=2, threads=10)
|
|
110
|
-
# Override threads for higher concurrency
|
|
111
|
-
general:
|
|
112
|
-
threads: 100 # Overrides default_threads (forks uses default_forks=2)
|
|
113
|
-
queues:
|
|
114
|
-
- default
|
|
115
|
-
- mailers
|
|
116
|
-
- notifications
|
|
117
|
-
|
|
118
|
-
# Example 3: Fine-grained control with multiple specialized workers
|
|
119
|
-
# Run separate processes:
|
|
120
|
-
# bin/postburner --worker critical
|
|
121
|
-
# bin/postburner --worker default
|
|
122
|
-
# bin/postburner --worker mailers
|
|
123
|
-
#
|
|
124
|
-
# workers:
|
|
125
|
-
# critical:
|
|
126
|
-
# forks: 1
|
|
127
|
-
# threads: 1
|
|
128
|
-
# gc_limit: 100
|
|
129
|
-
# queues:
|
|
130
|
-
# - critical
|
|
131
|
-
#
|
|
132
|
-
# default:
|
|
133
|
-
# forks: 4
|
|
134
|
-
# threads: 10
|
|
135
|
-
# queues:
|
|
136
|
-
# - default
|
|
137
|
-
#
|
|
138
|
-
# mailers:
|
|
139
|
-
# forks: 2
|
|
140
|
-
# threads: 5
|
|
141
|
-
# queues:
|
|
142
|
-
# - mailers
|
|
143
|
-
|
|
144
|
-
# Env-Level Defaults (can be overridden per worker):
|
|
145
|
-
#
|
|
146
|
-
# default_queue: default # Default queue name (optional)
|
|
147
|
-
# default_priority: 65536 # Lower = higher priority (optional, 0 is highest)
|
|
148
|
-
# default_ttr: 300 # Time-to-run in seconds (optional)
|
|
149
|
-
# default_threads: 1 # Thread count per fork (optional, defaults to 1)
|
|
150
|
-
# default_forks: 0 # Fork count (optional, defaults to 0 = single process)
|
|
151
|
-
# default_gc_limit: nil # Exit after N jobs for restart (optional, nil = no limit)
|
|
152
|
-
# default_scheduler_interval: 300 # Scheduler check interval in seconds (optional, default: 300)
|
|
153
|
-
# default_scheduler_priority: 100 # Scheduler watchdog priority (optional, default: 100)
|
|
37
|
+
default_threads: 4
|
|
38
|
+
default_gc_limit: 64
|
|
@@ -106,11 +106,56 @@ module ActiveJob
|
|
|
106
106
|
job.class.included_modules.include?(Postburner::Tracked)
|
|
107
107
|
end
|
|
108
108
|
|
|
109
|
+
# Resolves Beanstalkd options (priority and TTR) for a job.
|
|
110
|
+
#
|
|
111
|
+
# Uses a priority cascade:
|
|
112
|
+
# 1. job.priority (from .set(priority: n))
|
|
113
|
+
# 2. enqueue_options hook (for third-party jobs like Turbo)
|
|
114
|
+
# 3. Class-level configuration (postburner_ttr, priority DSL)
|
|
115
|
+
# 4. Global defaults from configuration
|
|
116
|
+
#
|
|
117
|
+
# @param job [ActiveJob::Base] The job instance
|
|
118
|
+
#
|
|
119
|
+
# @return [Hash] Hash with :pri and :ttr keys
|
|
120
|
+
#
|
|
121
|
+
def job_options(job)
|
|
122
|
+
hook_options = resolve_enqueue_options(job)
|
|
123
|
+
|
|
124
|
+
# Priority cascade: job.priority > hook > class-level > default
|
|
125
|
+
pri = job.priority ||
|
|
126
|
+
hook_options[:priority] ||
|
|
127
|
+
Postburner.configuration.default_priority
|
|
128
|
+
|
|
129
|
+
# TTR cascade: class-level > hook > default
|
|
130
|
+
ttr = (job.class.respond_to?(:postburner_ttr) && job.class.postburner_ttr) ||
|
|
131
|
+
hook_options[:ttr] ||
|
|
132
|
+
Postburner.configuration.default_ttr
|
|
133
|
+
|
|
134
|
+
{ pri: pri, ttr: ttr }
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Calls the enqueue_options hook if configured.
|
|
138
|
+
#
|
|
139
|
+
# @param job [ActiveJob::Base] The job instance
|
|
140
|
+
#
|
|
141
|
+
# @return [Hash] Options hash with :priority and/or :ttr, or empty hash
|
|
142
|
+
#
|
|
143
|
+
def resolve_enqueue_options(job)
|
|
144
|
+
hook = Postburner.configuration.enqueue_options
|
|
145
|
+
return {} unless hook.respond_to?(:call)
|
|
146
|
+
|
|
147
|
+
hook.call(job) || {}
|
|
148
|
+
end
|
|
149
|
+
|
|
109
150
|
# Enqueues a tracked job (with PostgreSQL audit trail).
|
|
110
151
|
#
|
|
111
152
|
# Creates a Postburner::TrackedJob record, then queues minimal payload
|
|
112
153
|
# to Beanstalkd with just the job ID reference.
|
|
113
154
|
#
|
|
155
|
+
# Instruments with ActiveSupport::Notifications:
|
|
156
|
+
# - enqueue.job.postburner: When job is queued immediately
|
|
157
|
+
# - enqueue_at.job.postburner: When job is queued with delay
|
|
158
|
+
#
|
|
114
159
|
# @param job [ActiveJob::Base] The job instance
|
|
115
160
|
# @param timestamp [Time, nil] When to execute the job
|
|
116
161
|
#
|
|
@@ -128,25 +173,41 @@ module ActiveJob
|
|
|
128
173
|
delay = timestamp ? [timestamp.to_i - Time.current.to_i, 0].max : 0
|
|
129
174
|
|
|
130
175
|
# Queue to Beanstalkd with minimal payload
|
|
176
|
+
bkid = nil
|
|
131
177
|
Postburner.connected do |conn|
|
|
132
178
|
tube_name = expand_tube_name(job.queue_name)
|
|
133
|
-
|
|
134
|
-
# Get priority and TTR
|
|
135
|
-
# Priority order: job.priority (from .set or class.priority) > default
|
|
136
|
-
pri = job.priority || Postburner.configuration.default_priority
|
|
137
|
-
ttr = job.class.respond_to?(:postburner_ttr) && job.class.postburner_ttr ||
|
|
138
|
-
Postburner.configuration.default_ttr
|
|
179
|
+
opts = job_options(job)
|
|
139
180
|
|
|
140
181
|
bkid = conn.tubes[tube_name].put(
|
|
141
182
|
Postburner::ActiveJob::Payload.tracked_payload(job, tracked_job.id),
|
|
142
|
-
pri: pri,
|
|
183
|
+
pri: opts[:pri],
|
|
143
184
|
delay: delay,
|
|
144
|
-
ttr: ttr
|
|
185
|
+
ttr: opts[:ttr]
|
|
145
186
|
)
|
|
146
187
|
|
|
147
188
|
# Update tracked_job with Beanstalkd ID
|
|
148
189
|
tracked_job.update_column(:bkid, bkid)
|
|
149
190
|
end
|
|
191
|
+
|
|
192
|
+
# Instrument enqueue event
|
|
193
|
+
job_payload = Postburner::Instrumentation.job_payload_from_activejob(
|
|
194
|
+
job,
|
|
195
|
+
tracked: true,
|
|
196
|
+
postburner_job_id: tracked_job.id,
|
|
197
|
+
beanstalk_job_id: bkid
|
|
198
|
+
)
|
|
199
|
+
scheduled_at = timestamp ? Time.zone.at(timestamp) : nil
|
|
200
|
+
|
|
201
|
+
if scheduled_at && scheduled_at > Time.current
|
|
202
|
+
ActiveSupport::Notifications.instrument('enqueue_at.job.postburner', {
|
|
203
|
+
job: job_payload,
|
|
204
|
+
scheduled_at: scheduled_at
|
|
205
|
+
})
|
|
206
|
+
else
|
|
207
|
+
ActiveSupport::Notifications.instrument('enqueue.job.postburner', {
|
|
208
|
+
job: job_payload
|
|
209
|
+
})
|
|
210
|
+
end
|
|
150
211
|
end
|
|
151
212
|
|
|
152
213
|
# Enqueues a default job (Beanstalkd only, no PostgreSQL).
|
|
@@ -154,6 +215,10 @@ module ActiveJob
|
|
|
154
215
|
# Queues full job data to Beanstalkd for fast execution without
|
|
155
216
|
# PostgreSQL overhead.
|
|
156
217
|
#
|
|
218
|
+
# Instruments with ActiveSupport::Notifications:
|
|
219
|
+
# - enqueue.job.postburner: When job is queued immediately
|
|
220
|
+
# - enqueue_at.job.postburner: When job is queued with delay
|
|
221
|
+
#
|
|
157
222
|
# @param job [ActiveJob::Base] The job instance
|
|
158
223
|
# @param timestamp [Time, nil] When to execute the job
|
|
159
224
|
#
|
|
@@ -161,23 +226,39 @@ module ActiveJob
|
|
|
161
226
|
#
|
|
162
227
|
def enqueue_default(job, timestamp)
|
|
163
228
|
delay = timestamp ? [timestamp.to_i - Time.current.to_i, 0].max : 0
|
|
229
|
+
bkid = nil
|
|
164
230
|
|
|
165
231
|
Postburner.connected do |conn|
|
|
166
232
|
tube_name = expand_tube_name(job.queue_name)
|
|
233
|
+
opts = job_options(job)
|
|
167
234
|
|
|
168
|
-
|
|
169
|
-
# Priority order: job.priority (from .set or class.priority) > default
|
|
170
|
-
pri = job.priority || Postburner.configuration.default_priority
|
|
171
|
-
ttr = job.class.respond_to?(:postburner_ttr) && job.class.postburner_ttr ||
|
|
172
|
-
Postburner.configuration.default_ttr
|
|
173
|
-
|
|
174
|
-
conn.tubes[tube_name].put(
|
|
235
|
+
bkid = conn.tubes[tube_name].put(
|
|
175
236
|
Postburner::ActiveJob::Payload.default_payload(job),
|
|
176
|
-
pri: pri,
|
|
237
|
+
pri: opts[:pri],
|
|
177
238
|
delay: delay,
|
|
178
|
-
ttr: ttr
|
|
239
|
+
ttr: opts[:ttr]
|
|
179
240
|
)
|
|
180
241
|
end
|
|
242
|
+
|
|
243
|
+
# Instrument enqueue event
|
|
244
|
+
job_payload = Postburner::Instrumentation.job_payload_from_activejob(
|
|
245
|
+
job,
|
|
246
|
+
tracked: false,
|
|
247
|
+
postburner_job_id: nil,
|
|
248
|
+
beanstalk_job_id: bkid
|
|
249
|
+
)
|
|
250
|
+
scheduled_at = timestamp ? Time.zone.at(timestamp) : nil
|
|
251
|
+
|
|
252
|
+
if scheduled_at && scheduled_at > Time.current
|
|
253
|
+
ActiveSupport::Notifications.instrument('enqueue_at.job.postburner', {
|
|
254
|
+
job: job_payload,
|
|
255
|
+
scheduled_at: scheduled_at
|
|
256
|
+
})
|
|
257
|
+
else
|
|
258
|
+
ActiveSupport::Notifications.instrument('enqueue.job.postburner', {
|
|
259
|
+
job: job_payload
|
|
260
|
+
})
|
|
261
|
+
end
|
|
181
262
|
end
|
|
182
263
|
|
|
183
264
|
# Expands queue name to full tube name with environment prefix.
|
|
@@ -23,6 +23,7 @@ module Postburner
|
|
|
23
23
|
# Global settings
|
|
24
24
|
attr_accessor :beanstalk_url, :logger, :default_queue, :default_priority, :default_ttr
|
|
25
25
|
attr_accessor :default_scheduler_interval, :default_scheduler_priority
|
|
26
|
+
attr_accessor :enqueue_options
|
|
26
27
|
|
|
27
28
|
# Worker-specific settings (loaded for a single worker)
|
|
28
29
|
attr_accessor :worker_config
|
|
@@ -35,6 +36,9 @@ module Postburner
|
|
|
35
36
|
# @option options [Integer] :default_ttr Default time-to-run in seconds (default: 300)
|
|
36
37
|
# @option options [Integer] :default_scheduler_interval Scheduler check interval in seconds (default: 300)
|
|
37
38
|
# @option options [Integer] :default_scheduler_priority Scheduler job priority (default: 100)
|
|
39
|
+
# @option options [Proc] :enqueue_options Proc that receives job and returns options hash
|
|
40
|
+
# with :priority and/or :ttr keys. Called during enqueue to customize job options.
|
|
41
|
+
# Priority cascade: job.priority > hook > class-level > default
|
|
38
42
|
# @option options [Hash] :worker_config Worker configuration hash with keys:
|
|
39
43
|
# - :name [String] Worker name
|
|
40
44
|
# - :queues [Array<String>] Queue/tube names to process
|
|
@@ -52,6 +56,7 @@ module Postburner
|
|
|
52
56
|
@default_ttr = options[:default_ttr] || 300
|
|
53
57
|
@default_scheduler_interval = options[:default_scheduler_interval] || 300
|
|
54
58
|
@default_scheduler_priority = options[:default_scheduler_priority] || 100
|
|
59
|
+
@enqueue_options = options[:enqueue_options]
|
|
55
60
|
@worker_config = options[:worker_config] || {
|
|
56
61
|
name: 'default',
|
|
57
62
|
queues: ['default'],
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Postburner
|
|
4
|
+
# Instrumentation helpers for ActiveSupport::Notifications events.
|
|
5
|
+
#
|
|
6
|
+
# Provides standardized payload builders for all Postburner instrumentation events.
|
|
7
|
+
# Event naming follows the pattern: `verb.noun.postburner`
|
|
8
|
+
#
|
|
9
|
+
# ## Event Categories
|
|
10
|
+
#
|
|
11
|
+
# ### Job Events
|
|
12
|
+
# - `perform_start.job.postburner` - Before job execution
|
|
13
|
+
# - `perform.job.postburner` - Around job execution (includes duration)
|
|
14
|
+
# - `retry.job.postburner` - Default job retried
|
|
15
|
+
# - `discard.job.postburner` - Default job exhausts retries
|
|
16
|
+
# - `enqueue.job.postburner` - Job queued (immediate)
|
|
17
|
+
# - `enqueue_at.job.postburner` - Job queued with delay
|
|
18
|
+
# - `retry_stopped.job.postburner` - Tracked job buried after failures
|
|
19
|
+
#
|
|
20
|
+
# ### Schedule Events
|
|
21
|
+
# - `create.schedule.postburner` - Schedule created
|
|
22
|
+
# - `update.schedule.postburner` - Schedule updated
|
|
23
|
+
# - `audit.schedule.postburner` - Scheduler audits a schedule
|
|
24
|
+
#
|
|
25
|
+
# ### Schedule Execution Events
|
|
26
|
+
# - `create.schedule_execution.postburner` - Execution created
|
|
27
|
+
# - `enqueue.schedule_execution.postburner` - Execution enqueued to Beanstalkd
|
|
28
|
+
# - `skip.schedule_execution.postburner` - Execution skipped
|
|
29
|
+
#
|
|
30
|
+
# ### Scheduler Watchdog Events
|
|
31
|
+
# - `perform_start.scheduler.postburner` - Watchdog run begins
|
|
32
|
+
# - `perform.scheduler.postburner` - Around watchdog run (summary)
|
|
33
|
+
#
|
|
34
|
+
# @example Subscribing to job events
|
|
35
|
+
# ActiveSupport::Notifications.subscribe('perform.job.postburner') do |name, start, finish, id, payload|
|
|
36
|
+
# duration = (finish - start) * 1000
|
|
37
|
+
# Rails.logger.info "[Postburner] #{payload[:job][:class]} completed in #{duration.round(2)}ms"
|
|
38
|
+
# end
|
|
39
|
+
#
|
|
40
|
+
# @example Subscribing to schedule events
|
|
41
|
+
# ActiveSupport::Notifications.subscribe('create.schedule.postburner') do |*args|
|
|
42
|
+
# payload = args.last
|
|
43
|
+
# schedule = payload[:schedule]
|
|
44
|
+
# Rails.logger.info "[Postburner] Schedule '#{schedule[:name]}' created"
|
|
45
|
+
# end
|
|
46
|
+
#
|
|
47
|
+
module Instrumentation
|
|
48
|
+
module_function
|
|
49
|
+
|
|
50
|
+
# Build a job payload hash for instrumentation.
|
|
51
|
+
#
|
|
52
|
+
# @param job [Postburner::Job, Hash] Job instance or parsed payload hash
|
|
53
|
+
# @param beanstalk_job_id [Integer, nil] Beanstalkd job ID
|
|
54
|
+
# @return [Hash] Standardized job payload
|
|
55
|
+
#
|
|
56
|
+
def job_payload(job, beanstalk_job_id: nil)
|
|
57
|
+
if job.is_a?(Postburner::Job)
|
|
58
|
+
job_payload_from_model(job, beanstalk_job_id: beanstalk_job_id)
|
|
59
|
+
elsif job.is_a?(Hash)
|
|
60
|
+
job_payload_from_hash(job, beanstalk_job_id: beanstalk_job_id)
|
|
61
|
+
else
|
|
62
|
+
raise ArgumentError, "Expected Postburner::Job or Hash, got #{job.class}"
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Build job payload from a Postburner::Job model.
|
|
67
|
+
#
|
|
68
|
+
# @param job [Postburner::Job] Job model instance
|
|
69
|
+
# @param beanstalk_job_id [Integer, nil] Beanstalkd job ID
|
|
70
|
+
# @return [Hash] Standardized job payload
|
|
71
|
+
#
|
|
72
|
+
def job_payload_from_model(job, beanstalk_job_id: nil)
|
|
73
|
+
{
|
|
74
|
+
class: job.class.name,
|
|
75
|
+
id: job.id,
|
|
76
|
+
job_id: job.respond_to?(:args) && job.args.is_a?(Hash) ? job.args['job_id'] : nil,
|
|
77
|
+
arguments: job.args,
|
|
78
|
+
queue_name: job.respond_to?(:queue_name) ? job.queue_name : job.class.try(:postburner_queue),
|
|
79
|
+
beanstalk_job_id: beanstalk_job_id || job.bkid,
|
|
80
|
+
tracked: true
|
|
81
|
+
}
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Build job payload from a parsed Beanstalkd payload hash.
|
|
85
|
+
#
|
|
86
|
+
# Handles both ActiveJob format and legacy Postburner::Job format.
|
|
87
|
+
#
|
|
88
|
+
# @param payload [Hash] Parsed JSON payload from Beanstalkd
|
|
89
|
+
# @param beanstalk_job_id [Integer, nil] Beanstalkd job ID
|
|
90
|
+
# @return [Hash] Standardized job payload
|
|
91
|
+
#
|
|
92
|
+
def job_payload_from_hash(payload, beanstalk_job_id: nil)
|
|
93
|
+
if Postburner::ActiveJob::Payload.legacy_format?(payload)
|
|
94
|
+
# Legacy format: { "class" => "JobClass", "args" => [id] }
|
|
95
|
+
{
|
|
96
|
+
class: payload['class'],
|
|
97
|
+
id: payload['args']&.first,
|
|
98
|
+
job_id: nil,
|
|
99
|
+
arguments: payload['args'],
|
|
100
|
+
queue_name: nil,
|
|
101
|
+
beanstalk_job_id: beanstalk_job_id,
|
|
102
|
+
tracked: true
|
|
103
|
+
}
|
|
104
|
+
else
|
|
105
|
+
# ActiveJob format
|
|
106
|
+
tracked = payload['tracked'] == true
|
|
107
|
+
{
|
|
108
|
+
class: payload['job_class'],
|
|
109
|
+
id: tracked ? payload['postburner_job_id'] : nil,
|
|
110
|
+
job_id: payload['job_id'],
|
|
111
|
+
arguments: payload['arguments'],
|
|
112
|
+
queue_name: payload['queue_name'],
|
|
113
|
+
beanstalk_job_id: beanstalk_job_id,
|
|
114
|
+
tracked: tracked
|
|
115
|
+
}
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Build job payload from an ActiveJob instance.
|
|
120
|
+
#
|
|
121
|
+
# @param job [ActiveJob::Base] ActiveJob instance
|
|
122
|
+
# @param tracked [Boolean] Whether job is tracked in PostgreSQL
|
|
123
|
+
# @param postburner_job_id [Integer, nil] Postburner::TrackedJob ID if tracked
|
|
124
|
+
# @param beanstalk_job_id [Integer, nil] Beanstalkd job ID
|
|
125
|
+
# @return [Hash] Standardized job payload
|
|
126
|
+
#
|
|
127
|
+
def job_payload_from_activejob(job, tracked:, postburner_job_id: nil, beanstalk_job_id: nil)
|
|
128
|
+
{
|
|
129
|
+
class: job.class.name,
|
|
130
|
+
id: postburner_job_id,
|
|
131
|
+
job_id: job.job_id,
|
|
132
|
+
arguments: job.arguments,
|
|
133
|
+
queue_name: job.queue_name,
|
|
134
|
+
beanstalk_job_id: beanstalk_job_id,
|
|
135
|
+
tracked: tracked
|
|
136
|
+
}
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Build a schedule payload hash for instrumentation.
|
|
140
|
+
#
|
|
141
|
+
# @param schedule [Postburner::Schedule] Schedule model instance
|
|
142
|
+
# @return [Hash] Standardized schedule payload
|
|
143
|
+
#
|
|
144
|
+
def schedule_payload(schedule)
|
|
145
|
+
{
|
|
146
|
+
id: schedule.id,
|
|
147
|
+
name: schedule.name,
|
|
148
|
+
job_class: schedule.job_class,
|
|
149
|
+
enabled: schedule.enabled,
|
|
150
|
+
interval: schedule.interval,
|
|
151
|
+
interval_unit: schedule.interval_unit,
|
|
152
|
+
cron: schedule.cron,
|
|
153
|
+
timezone: schedule.timezone
|
|
154
|
+
}
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Build a schedule execution payload hash for instrumentation.
|
|
158
|
+
#
|
|
159
|
+
# @param execution [Postburner::ScheduleExecution] Execution model instance
|
|
160
|
+
# @return [Hash] Standardized execution payload
|
|
161
|
+
#
|
|
162
|
+
def execution_payload(execution)
|
|
163
|
+
{
|
|
164
|
+
id: execution.id,
|
|
165
|
+
schedule_id: execution.schedule_id,
|
|
166
|
+
run_at: execution.run_at,
|
|
167
|
+
next_run_at: execution.next_run_at,
|
|
168
|
+
status: execution.status,
|
|
169
|
+
beanstalk_job_id: execution.beanstalk_job_id,
|
|
170
|
+
job_id: execution.job_id
|
|
171
|
+
}
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# Build changes hash from ActiveRecord model, excluding specified attributes.
|
|
175
|
+
#
|
|
176
|
+
# @param model [ActiveRecord::Base] Model with changes
|
|
177
|
+
# @param exclude [Array<String, Symbol>] Attributes to exclude
|
|
178
|
+
# @return [Hash] Changes hash in { attribute: [old, new] } format
|
|
179
|
+
#
|
|
180
|
+
def changes_payload(model, exclude: [])
|
|
181
|
+
exclude = exclude.map(&:to_s)
|
|
182
|
+
model.saved_changes.except(*exclude, 'updated_at', 'created_at')
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# Instrument an event with ActiveSupport::Notifications.
|
|
186
|
+
#
|
|
187
|
+
# @param event [String] Event name (e.g., 'perform.job.postburner')
|
|
188
|
+
# @param payload [Hash] Event payload
|
|
189
|
+
# @yield Block to execute within instrumentation (for timing)
|
|
190
|
+
# @return [Object] Result of block if given
|
|
191
|
+
#
|
|
192
|
+
def instrument(event, payload = {}, &block)
|
|
193
|
+
ActiveSupport::Notifications.instrument(event, payload, &block)
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
end
|