postburner 0.3.2 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +31 -0
- data/README.md +262 -31
- data/app/models/postburner/job.rb +100 -37
- data/app/models/postburner/mailer.rb +61 -8
- data/lib/postburner/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f0c2a65b18596dafa8809d07924e2274f7adc05ce971fea1b82143c7132f2929
|
4
|
+
data.tar.gz: e3000b8fa490aec4fb1c8b49c54fe7ee78613b3e7e3849c2270d7a7dd01adecd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 87cb7aec3924346ab43b93f5886aebfb1b138a36b199eb3e10032404a23b07774c287cb96ed8c4889feabde1106aba63d23d6429cead43f3eec8211f257d6baa
|
7
|
+
data.tar.gz: 94e32291c4f00db869a630adeea7e44abb92c4938e2314fc5e46ea487b874acc4a74b80771bd8db8944f3a7c98e06101ef90a36f8da5bf72d7fecc82fbd14c3c
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,36 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## v0.6.0 - 2021-11-01
|
4
|
+
|
5
|
+
### Added
|
6
|
+
- update exception handling to better show where the error was raised.
|
7
|
+
- update logging to match errata format.
|
8
|
+
- update documentation.
|
9
|
+
|
10
|
+
## v0.5.0 - 2021-10-27
|
11
|
+
|
12
|
+
### Added
|
13
|
+
- add mailer, action, params accessors to Mailer for easy access in subclassed jobs.
|
14
|
+
- add logging to Mailer perform.
|
15
|
+
- add save to #queue! job if unsaved
|
16
|
+
- add block with logging if queued_at isn't set or isn't prior to run time.
|
17
|
+
|
18
|
+
## v0.4.0 - 2021-10-12
|
19
|
+
|
20
|
+
### Added
|
21
|
+
- Change Job logging to persisted in perform.
|
22
|
+
- Update mailer job to use #with and support serializations.
|
23
|
+
|
24
|
+
## v0.3.3 - 2021-06-15
|
25
|
+
|
26
|
+
### Fixed
|
27
|
+
- re-raise exception after logging during perform.
|
28
|
+
- log but swallow post perform data update exceptions.
|
29
|
+
- only allow remove! if job still present.
|
30
|
+
|
31
|
+
### Added
|
32
|
+
- Job#log! and Job#log_exception! that save the data immediately.
|
33
|
+
|
3
34
|
## v0.3.2 - 2021-05-07
|
4
35
|
|
5
36
|
### Fixed
|
data/README.md
CHANGED
@@ -1,29 +1,15 @@
|
|
1
1
|
# Postburner
|
2
|
-
An ActiveRecord layer on top of Backburner for inspecting and auditing the
|
3
|
-
queue, especially for delayed jobs. It isn't meant to be
|
2
|
+
An ActiveRecord layer on top of [Backburner](https://github.com/nesquena/backburner) for inspecting and auditing the
|
3
|
+
queue, especially for delayed jobs. It isn't meant to be outperform other queues, but be safe (and inspectable).
|
4
4
|
|
5
|
-
It is meant to be complementary to Backburner. Use Backburner as the default
|
5
|
+
It is meant to be complementary to [Backburner](https://github.com/nesquena/backburner). Use Backburner as the default
|
6
6
|
ActiveJob processor for mailers, active storage, and the like. Use a
|
7
|
-
`Postburner::Job` for things that you want to track.
|
7
|
+
`Postburner::Job` for things that you want to track. See [Comparison to Backburner](#comparison-to-backburner) for more.
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
If you need something faster, but backed with ACID compliance, check out Que.
|
14
|
-
There are some additional features in Postburner such as retained jobs
|
15
|
-
after processing, stats, per job logging, etc.
|
16
|
-
|
17
|
-
Compared to plain [Backburner](https://github.com/nesquena/backburner),
|
18
|
-
Postburner adds:
|
19
|
-
- Database Jobs for inspection, linking, auditing, removal (and deletion)
|
20
|
-
- Direct access to associated [Beanstalk](https://beanstalkd.github.io/) (via [beaneater](https://github.com/beanstalkd/beaneater))
|
21
|
-
- Job Statistics (lag, attempts, logs, tracked errors)
|
22
|
-
- Convenience methods to clear tubes, stats, and connections for Beanstalk.
|
23
|
-
|
24
|
-
Otherwise, Postburner tries to be a super simple layer on `Backburner::Queue`
|
25
|
-
and `ActiveRecord`. Every tool with either of those are avilabel in
|
26
|
-
`Postburner::Job`s.
|
9
|
+
Postburner meant to be a replacement/upgrade for [Que](https://github.com/que-rb/que).
|
10
|
+
If you need something faster, check out Que - we love it! Postburner is built for a slightly different purpose: to be
|
11
|
+
simple, safe, and inspectable. All of which Que has (or can be added), but are included by default with some architecture
|
12
|
+
differences. See [Comparison to Que](#comparison-to-que) for more.
|
27
13
|
|
28
14
|
## Usage
|
29
15
|
|
@@ -33,51 +19,251 @@ class RunDonation < Postburner::Job
|
|
33
19
|
queue_priority 0 # 0 is highest priority
|
34
20
|
queue_max_job_retries 0 # don't retry
|
35
21
|
|
36
|
-
def
|
22
|
+
def perform(args)
|
37
23
|
# do long tasks here
|
38
24
|
# also have access to self.args
|
39
25
|
end
|
40
26
|
end
|
41
27
|
|
42
|
-
RunDonation
|
28
|
+
# RunDonation#create! is the `ActiveRecord` method, so it returns a model,
|
29
|
+
# you can manipulate, add columns to the table to, reference with foreign
|
30
|
+
# keys, etc.
|
31
|
+
job = RunDonation.create!(args: {donation_id: 123})
|
32
|
+
# NOTE Make sure use use an after_commit or after_save_commit, etc to avoid
|
33
|
+
# any race conditions of Postburner trying to process before a required
|
34
|
+
# database mutation is commited.
|
35
|
+
job.queue!
|
43
36
|
=> {:status=>"INSERTED", :id=>"1139"}
|
37
|
+
|
38
|
+
# queue for later with `:at`
|
44
39
|
RunDonation.create!(args: {donation_id: 123}).queue! at: Time.zone.now + 2.days
|
45
40
|
=> {:status=>"INSERTED", :id=>"1140"}
|
46
41
|
|
47
|
-
#
|
42
|
+
# queue for later with `:delay`
|
43
|
+
#
|
44
|
+
# `:delay` takes priority over `:at`takes priority over `:at` because the
|
45
|
+
# beanstalkd protocol uses uses `delay`
|
48
46
|
RunDonation.create!(args: {donation_id: 123}).queue! delay: 1.hour
|
49
47
|
=> {:status=>"INSERTED", :id=>"1141"}
|
50
48
|
```
|
51
49
|
|
50
|
+
### Mailers
|
51
|
+
|
52
|
+
```ruby
|
53
|
+
j = Postburner::Mailer.
|
54
|
+
delivery(UserMailer, :welcome)
|
55
|
+
.with(name: 'Freddy')
|
56
|
+
|
57
|
+
j.queue!
|
58
|
+
=> {:status=>"INSERTED", :id=>"1139"}
|
59
|
+
```
|
60
|
+
|
61
|
+
### [Beaneater](https://github.com/beanstalkd/beaneater) and [beanstalkd](https://raw.githubusercontent.com/beanstalkd/beanstalkd/master/doc/protocol.txt) attributes and methods
|
62
|
+
```ruby
|
63
|
+
# get the beanstalkd job id
|
64
|
+
job.bkid
|
65
|
+
=> 1104
|
66
|
+
|
67
|
+
# get the beaneater job, call any beaneater methods on this object
|
68
|
+
job.beanstalk_job
|
69
|
+
|
70
|
+
# get the beanstald stats
|
71
|
+
job.beanstalk_job.stats
|
72
|
+
|
73
|
+
# kick the beanstalk job
|
74
|
+
job.kick!
|
75
|
+
|
76
|
+
# delete beankstalkd job, retain the job model
|
77
|
+
job.delete!
|
78
|
+
|
79
|
+
# delete beankstalkd job, retain the job model, but set `removed_at` on model.
|
80
|
+
job.remove!
|
81
|
+
|
82
|
+
# or simply remove the model, which will clean up the beanstalkd job in a before_destroy hook
|
83
|
+
job.destroy # OR job.destroy!
|
84
|
+
|
85
|
+
# get a cached Backburner connection and inspect it (or even use it directly)
|
86
|
+
c = Postburner.connection
|
87
|
+
c.beanstalk.tubes.to_a
|
88
|
+
c.beanstalk.tubes.to_a.map{|t| c.tubes[t.name].peek(:buried)}
|
89
|
+
c.beanstalk.tubes['ss.development.caching'].stats
|
90
|
+
c.beanstalk.tubes['ss.development.caching'].peek(:buried).kick
|
91
|
+
c.beanstalk.tubes['ss.development.caching'].kick(3)
|
92
|
+
c.close
|
93
|
+
|
94
|
+
# automatically close
|
95
|
+
Postburner.connected do |connection|
|
96
|
+
# do stuff with connection
|
97
|
+
end
|
98
|
+
```
|
99
|
+
|
100
|
+
Read about the [beanstalkd protocol](https://raw.githubusercontent.com/beanstalkd/beanstalkd/master/doc/protocol.txt).
|
101
|
+
|
102
|
+
### Basic model fields
|
103
|
+
```ruby
|
104
|
+
# ActiveRecord primary key
|
105
|
+
job.id
|
106
|
+
|
107
|
+
# int id of the beankstalkd job
|
108
|
+
job.bkid
|
109
|
+
|
110
|
+
# string uuid
|
111
|
+
job.sid
|
112
|
+
|
113
|
+
# ActiveRecord STI job subtype
|
114
|
+
job.type
|
115
|
+
|
116
|
+
# jsonb arguments for use in job
|
117
|
+
job.args
|
118
|
+
|
119
|
+
# time job should run - not intended to be changed
|
120
|
+
# TODO run_at should be readonly after create
|
121
|
+
job.run_at
|
122
|
+
```
|
123
|
+
|
124
|
+
### Job statistics
|
125
|
+
```ruby
|
126
|
+
# when job was inserted into beankstalkd
|
127
|
+
job.queued_at
|
128
|
+
|
129
|
+
# last time attempted
|
130
|
+
job.attempting_at
|
131
|
+
|
132
|
+
# last time processing started
|
133
|
+
job.processing_at
|
134
|
+
|
135
|
+
# when completed
|
136
|
+
job.processed_at
|
137
|
+
|
138
|
+
# when removed, may be nil
|
139
|
+
job.removed_at
|
140
|
+
|
141
|
+
# lag in ms from run_at/queued_at to attempting_at
|
142
|
+
job.lag
|
143
|
+
|
144
|
+
# duration of processing in ms
|
145
|
+
job.duration
|
146
|
+
|
147
|
+
# number of attempts
|
148
|
+
job.attempt_count
|
149
|
+
|
150
|
+
# number of errors (length of errata)
|
151
|
+
job.error_count
|
152
|
+
|
153
|
+
# number of log entries (length of logs)
|
154
|
+
job.log_count
|
155
|
+
|
156
|
+
# array of attempting_at times
|
157
|
+
job.attempts
|
158
|
+
|
159
|
+
# array of errors
|
160
|
+
job.errata
|
161
|
+
|
162
|
+
# array of log messages
|
163
|
+
job.logs
|
164
|
+
```
|
165
|
+
|
166
|
+
### Job logging and exceptions
|
167
|
+
|
168
|
+
Optionally, you can:
|
169
|
+
1. Add log messages to the job during processing to `logs`
|
170
|
+
1. Add log your own exceptions to `errata`
|
171
|
+
|
172
|
+
```ruby
|
173
|
+
class RunDonation < Postburner::Job
|
174
|
+
queue 'critical'
|
175
|
+
queue_priority 0 # 0 is highest priority
|
176
|
+
queue_max_job_retries 0 # don't retry
|
177
|
+
|
178
|
+
def perform(args)
|
179
|
+
# log at task, defaults to `:info`, but `:debug`, `:warning`, `:error`
|
180
|
+
log "Log bad condition...", level: :error
|
181
|
+
|
182
|
+
begin
|
183
|
+
# danger
|
184
|
+
rescue Exception => e
|
185
|
+
log_exception e
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
```
|
190
|
+
|
191
|
+
### Optionally, mount the engine
|
192
|
+
|
193
|
+
```ruby
|
194
|
+
mount Postburner::Engine => "/postburner"
|
195
|
+
|
196
|
+
# mount only for development inspection
|
197
|
+
mount Postburner::Engine => "/postburner" if Rails.env.development?
|
198
|
+
```
|
199
|
+
|
200
|
+
[Open the controller](https://guides.rubyonrails.org/engines.html#overriding-models-and-controllers)
|
201
|
+
to add your own authentication or changes - or just create your own routes, controllers, and views.
|
202
|
+
|
203
|
+
[Override the views](https://guides.rubyonrails.org/engines.html#overriding-views) to make them
|
204
|
+
prettier - or follow the suggestion above and use your own.
|
205
|
+
|
52
206
|
## Installation
|
53
207
|
|
208
|
+
First [install beanstalkd](https://beanstalkd.github.io/download.html). On Debian-based systems, that goes like this:
|
209
|
+
```bash
|
210
|
+
sudo apt-get install beanstalkd
|
211
|
+
```
|
212
|
+
|
213
|
+
Then add to your Gemfile.
|
54
214
|
```ruby
|
55
|
-
# in Gemfile
|
56
215
|
gem 'postburner'
|
57
216
|
```
|
58
217
|
|
218
|
+
Install...
|
59
219
|
```bash
|
60
220
|
$ bundle
|
221
|
+
```
|
61
222
|
|
223
|
+
After bundling, install the migration.
|
224
|
+
```
|
62
225
|
# install migration, possible to edit and add attributes or indexes as needed.
|
63
226
|
$ bundle exec rails g postburner:install
|
64
227
|
```
|
65
228
|
|
66
|
-
|
229
|
+
Inspect `XXX_create_postburner_jobs.rb`. (The template is [here](-/blob/master/lib/generators/postburner/install/templates/migrations/create_postburner_jobs.rb.erb)).The required attributes are in there, but add more if you would like to use them,
|
230
|
+
or do it in a separate migration. By default, several indexes are added
|
67
231
|
|
68
|
-
|
232
|
+
Because `Postburner` is built on `Backburner`, add a `config/initializers/backburner.rb` with option found [here](https://github.com/nesquena/backburner#configuration).
|
233
|
+
```ruby
|
234
|
+
Backburner.configure do |config|
|
235
|
+
config.beanstalk_url = "beanstalk://127.0.0.1"
|
236
|
+
config.tube_namespace = "some.app.production"
|
237
|
+
config.namespace_separator = "."
|
238
|
+
config.on_error = lambda { |e| puts e }
|
239
|
+
config.max_job_retries = 3 # default 0 retries
|
240
|
+
config.retry_delay = 2 # default 5 seconds
|
241
|
+
config.retry_delay_proc = lambda { |min_retry_delay, num_retries| min_retry_delay + (num_retries ** 3) }
|
242
|
+
config.default_priority = 65536
|
243
|
+
config.respond_timeout = 120
|
244
|
+
config.default_worker = Backburner::Workers::Simple
|
245
|
+
config.logger = Logger.new(STDOUT)
|
246
|
+
config.primary_queue = "backburner-jobs"
|
247
|
+
config.priority_labels = { :custom => 50, :useless => 1000 }
|
248
|
+
config.reserve_timeout = nil
|
249
|
+
config.job_serializer_proc = lambda { |body| JSON.dump(body) }
|
250
|
+
config.job_parser_proc = lambda { |body| JSON.parse(body) }
|
251
|
+
end
|
252
|
+
```
|
253
|
+
|
254
|
+
Finally, set `Backburner` for `ActiveJob`
|
69
255
|
```
|
70
256
|
# config/application.rb
|
71
257
|
config.active_job.queue_adapter = :backburner
|
72
258
|
```
|
73
259
|
|
74
260
|
Postburner may later provide an adapter, but we recommend using `Postburner::Job` classes
|
75
|
-
|
261
|
+
directly.
|
76
262
|
|
77
263
|
Add jobs to `app/jobs/`. There currently is no generator.
|
264
|
+
|
78
265
|
```ruby
|
79
266
|
# app/jobs/run_donation.rb
|
80
|
-
|
81
267
|
class RunDonation < Postburner::Job
|
82
268
|
queue 'critical'
|
83
269
|
queue_priority 0 # 0 is highest priority
|
@@ -88,6 +274,34 @@ class RunDonation < Postburner::Job
|
|
88
274
|
# also have access to self.args
|
89
275
|
end
|
90
276
|
end
|
277
|
+
```
|
278
|
+
|
279
|
+
### Comparison to Backburner
|
280
|
+
|
281
|
+
Compared to plain [Backburner](https://github.com/nesquena/backburner),
|
282
|
+
Postburner adds:
|
283
|
+
- Database Jobs for inspection, linking, auditing, removal (and deletion)
|
284
|
+
- Direct access to associated [Beanstalk](https://beanstalkd.github.io/) (via [beaneater](https://github.com/beanstalkd/beaneater))
|
285
|
+
- Job Statistics (lag, attempts, logs, tracked errors)
|
286
|
+
- Convenience methods to clear tubes, stats, and connections for Beanstalk.
|
287
|
+
|
288
|
+
Otherwise, Postburner tries to be a super simple layer on `Backburner::Queue`
|
289
|
+
and `ActiveRecord`. Every tool with either of those are available in
|
290
|
+
`Postburner::Job`s.
|
291
|
+
|
292
|
+
Comes with a mountable interface that can be password protected with whatever
|
293
|
+
authentication you use in your project.
|
294
|
+
|
295
|
+
### Comparison to Que
|
296
|
+
|
297
|
+
Postburner meant to be a replacement/upgrade for [Que](https://github.com/que-rb/que).
|
298
|
+
However, if you need something faster and backed with ACID compliance, check out Que.
|
299
|
+
|
300
|
+
Postburner has some additional features such as retained jobs after processing,
|
301
|
+
stats, per job logging, etc.
|
302
|
+
|
303
|
+
Postburner is meant to be simpler than `Que`. Que is incredible, but jobs should
|
304
|
+
be simple so the logic and history can be transparent.
|
91
305
|
|
92
306
|
## Contributing
|
93
307
|
Submit a pull request. Follow conventions of the project. Be nice.
|
@@ -102,7 +316,6 @@ Submit a pull request. Follow conventions of the project. Be nice.
|
|
102
316
|
- Job generator
|
103
317
|
- Build file in app/jobs
|
104
318
|
- Inherit from Postburner::Job
|
105
|
-
- Job generator
|
106
319
|
- Add before/after/around hooks
|
107
320
|
- Add destroy, and remove actions on show page
|
108
321
|
- Clear tubes.
|
@@ -112,5 +325,23 @@ Submit a pull request. Follow conventions of the project. Be nice.
|
|
112
325
|
- Add logging with Job.args in backburner logs
|
113
326
|
- MAYBE - ActiveJob adapter
|
114
327
|
|
328
|
+
|
329
|
+
### Running in Development
|
330
|
+
|
331
|
+
```
|
332
|
+
cd test/dummy
|
333
|
+
bundle exec backburner
|
334
|
+
```
|
335
|
+
|
336
|
+
### Releasing
|
337
|
+
|
338
|
+
1. `gem bump -v minor -t`
|
339
|
+
Where <minor> can be: major|minor|patch|pre|release or a version number
|
340
|
+
2. Edit `CHANGELOG.md` with details from `git log --oneline`
|
341
|
+
3. `git commit --amend`
|
342
|
+
4. `gem release -k nac -p`
|
343
|
+
Where <nac> is an authorized key with push capabilities.
|
344
|
+
|
345
|
+
|
115
346
|
## License
|
116
347
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
@@ -1,4 +1,17 @@
|
|
1
1
|
module Postburner
|
2
|
+
# Must implement a perform method, if an exception is raised the job
|
3
|
+
# doesn't complete.
|
4
|
+
#
|
5
|
+
# Job won't run unless queued_at is set, and is set to a time prior to
|
6
|
+
# the time the job runs.
|
7
|
+
#
|
8
|
+
# TODO Mailer uses ActiveJob::Arguments... probably should use that here
|
9
|
+
# as well. Decided how to migrate existing jobs or allow both - Opt to
|
10
|
+
# allow both: rescue from ActiveJob::DeserializationError and use the
|
11
|
+
# plain hash, probably log it too.
|
12
|
+
#
|
13
|
+
# Add `cancelled_at` that blocks jobs performing if present.
|
14
|
+
#
|
2
15
|
class Job < ApplicationRecord
|
3
16
|
include Backburner::Queue
|
4
17
|
|
@@ -17,6 +30,8 @@ module Postburner
|
|
17
30
|
def queue!(options={})
|
18
31
|
return if self.queued_at.present? && self.bkid.present?
|
19
32
|
|
33
|
+
self.save!
|
34
|
+
|
20
35
|
case
|
21
36
|
when options[:at].present?
|
22
37
|
# this is rudimentary, add error handling
|
@@ -33,14 +48,15 @@ module Postburner
|
|
33
48
|
# tube: backburner.worker.queue.backburner-jobs
|
34
49
|
#
|
35
50
|
def self.perform(id, _={})
|
51
|
+
job = nil
|
36
52
|
begin
|
37
53
|
job = self.find(id)
|
38
|
-
job.perform!(job.args)
|
39
54
|
rescue ActiveRecord::RecordNotFound => e
|
40
55
|
Rails.logger.warn <<-MSG
|
41
56
|
[Postburner::Job] [#{id}] Not Found.
|
42
57
|
MSG
|
43
58
|
end
|
59
|
+
job&.perform!(job.args)
|
44
60
|
end
|
45
61
|
|
46
62
|
def perform!(args={})
|
@@ -55,52 +71,63 @@ module Postburner
|
|
55
71
|
)
|
56
72
|
|
57
73
|
begin
|
74
|
+
if self.queued_at.nil?
|
75
|
+
self.log! "Not Queued", level: :error
|
76
|
+
return
|
77
|
+
end
|
78
|
+
|
79
|
+
if self.queued_at > Time.zone.now
|
80
|
+
self.log! "Future Queued", level: :error
|
81
|
+
return
|
82
|
+
end
|
83
|
+
|
58
84
|
if self.processed_at.present?
|
59
|
-
self.log "Already Processed", level: :error
|
60
|
-
update_column :logs, self.logs
|
85
|
+
self.log! "Already Processed", level: :error
|
61
86
|
self.delete!
|
62
87
|
return
|
63
88
|
end
|
64
89
|
|
65
90
|
if self.removed_at.present?
|
66
|
-
self.log "Removed", level: :error
|
67
|
-
update_column :logs, self.logs
|
91
|
+
self.log! "Removed", level: :error
|
68
92
|
return
|
69
93
|
end
|
70
94
|
|
71
95
|
if self.run_at && self.run_at > Time.zone.now
|
72
96
|
response = self.insert! delay: self.run_at - Time.zone.now
|
73
|
-
self.log "PREMATURE; RE-INSERTED: #{response}"
|
74
|
-
update_column :logs, self.logs
|
97
|
+
self.log! "PREMATURE; RE-INSERTED: #{response}"
|
75
98
|
return
|
76
99
|
end
|
77
100
|
|
78
|
-
self.log('START')
|
101
|
+
self.log!('START')
|
79
102
|
|
80
|
-
|
103
|
+
begin
|
104
|
+
self.perform(args)
|
105
|
+
rescue Exception => exception
|
106
|
+
self.persist_metadata!
|
107
|
+
self.log! '[Postburner] Exception raised during perform prevented completion.'
|
108
|
+
raise exception
|
109
|
+
end
|
81
110
|
|
82
|
-
self.log('DONE')
|
111
|
+
self.log!('DONE')
|
112
|
+
|
113
|
+
begin
|
114
|
+
now = Time.zone.now
|
115
|
+
_duration = (now - self.processing_at) * 1000 rescue nil
|
116
|
+
persist_metadata!(
|
117
|
+
processed_at: now,
|
118
|
+
duration: _duration,
|
119
|
+
)
|
120
|
+
rescue Exception => e
|
121
|
+
self.log_exception!(e)
|
122
|
+
self.log! '[Postburner] Could not set data after processing.'
|
123
|
+
# TODO README doesn't retry if Postburner is to blame
|
124
|
+
end
|
83
125
|
|
84
|
-
rescue Exception =>
|
85
|
-
self.log_exception(
|
126
|
+
rescue Exception => exception
|
127
|
+
self.log_exception!(exception)
|
128
|
+
raise exception
|
86
129
|
end
|
87
130
|
|
88
|
-
begin
|
89
|
-
now = Time.zone.now
|
90
|
-
_duration = (now - self.processing_at) * 1000 rescue nil
|
91
|
-
self.update_columns(
|
92
|
-
processed_at: now,
|
93
|
-
duration: _duration,
|
94
|
-
errata: self.errata,
|
95
|
-
error_count: self.errata.length,
|
96
|
-
logs: self.logs,
|
97
|
-
log_count: self.logs.length,
|
98
|
-
)
|
99
|
-
rescue Exception => e
|
100
|
-
raise e if Rails.env.development? || Rails.env.production?
|
101
|
-
self.log_exception(e)
|
102
|
-
update_column :errata, self.errata
|
103
|
-
end
|
104
131
|
end
|
105
132
|
|
106
133
|
def delete!
|
@@ -122,9 +149,12 @@ module Postburner
|
|
122
149
|
end
|
123
150
|
|
124
151
|
def remove!
|
125
|
-
|
126
|
-
|
127
|
-
|
152
|
+
if self.beanstalk_job
|
153
|
+
self.delete!
|
154
|
+
self.update_column(:removed_at, Time.zone.now)
|
155
|
+
else
|
156
|
+
false
|
157
|
+
end
|
128
158
|
end
|
129
159
|
|
130
160
|
def beanstalk_job
|
@@ -152,28 +182,61 @@ module Postburner
|
|
152
182
|
]
|
153
183
|
end
|
154
184
|
|
185
|
+
def log_exception!(exception)
|
186
|
+
self.log_exception(exception)
|
187
|
+
self.update_column :errata, self.errata
|
188
|
+
end
|
189
|
+
|
155
190
|
def log(message, options={})
|
156
191
|
options[:level] ||= :info
|
157
192
|
options[:level] = :error unless LOG_LEVELS.member?(options[:level])
|
158
193
|
|
159
194
|
self.logs << [
|
160
|
-
Time.zone.now,
|
161
|
-
|
162
|
-
|
195
|
+
Time.zone.now, # time
|
196
|
+
{
|
197
|
+
level: options[:level], # level
|
198
|
+
message: message, # message
|
199
|
+
elapsed: self.elapsed_ms, # ms from start
|
200
|
+
}
|
163
201
|
]
|
164
202
|
end
|
165
203
|
|
204
|
+
# ms from attempting_at
|
205
|
+
#
|
206
|
+
def elapsed_ms
|
207
|
+
return unless self.attempting_at
|
208
|
+
(Time.zone.now - self.attempting_at) * 1000
|
209
|
+
end
|
210
|
+
|
211
|
+
def log!(message, options={})
|
212
|
+
self.log(message, options)
|
213
|
+
self.update_column :logs, self.logs
|
214
|
+
end
|
215
|
+
|
166
216
|
private
|
167
217
|
|
218
|
+
def persist_metadata!(data={})
|
219
|
+
self.update_columns({
|
220
|
+
errata: self.errata,
|
221
|
+
error_count: self.errata.length,
|
222
|
+
logs: self.logs,
|
223
|
+
log_count: self.logs.length,
|
224
|
+
}.merge(data))
|
225
|
+
end
|
226
|
+
|
227
|
+
|
168
228
|
def insert!(options={})
|
169
|
-
response = Backburner::Worker.enqueue(
|
229
|
+
response = Backburner::Worker.enqueue(
|
230
|
+
Postburner::Job,
|
231
|
+
self.id,
|
232
|
+
options
|
233
|
+
)
|
170
234
|
|
171
235
|
self.log("QUEUED: #{response}")
|
172
236
|
|
173
|
-
|
237
|
+
persist_metadata!(
|
174
238
|
queued_at: Time.zone.now,
|
175
239
|
bkid: response[:id],
|
176
|
-
logs: self.logs,
|
177
240
|
)
|
178
241
|
|
179
242
|
response
|
@@ -1,14 +1,14 @@
|
|
1
1
|
module Postburner
|
2
2
|
# Send a mailer, tracked.
|
3
3
|
#
|
4
|
-
# Postburner::Mailer.deliver(UserMailer, :welcome
|
5
|
-
# Postburner::Mailer.deliver(UserMailer, :welcome
|
6
|
-
# Postburner::Mailer.deliver(UserMailer, :welcome
|
4
|
+
# Postburner::Mailer.deliver(UserMailer, :welcome).with(user_id: 1)
|
5
|
+
# Postburner::Mailer.deliver(UserMailer, :welcome).with(user_id: 1).queue! at: Time.zone.now + 1.day
|
6
|
+
# Postburner::Mailer.deliver(UserMailer, :welcome).with(user_id: 1).queue! delay: 5.minutes
|
7
7
|
class Mailer < Job
|
8
8
|
#queue 'mailers'
|
9
9
|
|
10
|
-
def self.
|
11
|
-
job = self.
|
10
|
+
def self.delivery(mailer, action)
|
11
|
+
job = self.new(
|
12
12
|
args: {
|
13
13
|
mailer: mailer.to_s,
|
14
14
|
action: action.to_s,
|
@@ -17,9 +17,62 @@ module Postburner
|
|
17
17
|
job
|
18
18
|
end
|
19
19
|
|
20
|
-
def
|
21
|
-
|
22
|
-
|
20
|
+
def self.delivery!(mailer, action)
|
21
|
+
job = self.delivery(mailer, action)
|
22
|
+
job.save!
|
23
|
+
job
|
24
|
+
end
|
25
|
+
|
26
|
+
# Similar to ActionMailer #with - set the parameters
|
27
|
+
#
|
28
|
+
def with(params={})
|
29
|
+
self.args.merge!(
|
30
|
+
'params' => ActiveJob::Arguments.serialize(params)
|
31
|
+
)
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
def with!(params={})
|
36
|
+
self.with(params)
|
37
|
+
self.save!
|
38
|
+
self
|
39
|
+
end
|
40
|
+
|
41
|
+
# Build the mail but don't send.
|
42
|
+
#
|
43
|
+
# Optional `args` argument for testing convenience.
|
44
|
+
#
|
45
|
+
def assemble
|
46
|
+
mail = self.mailer.with(self.params).send(self.action)
|
47
|
+
mail
|
48
|
+
end
|
49
|
+
|
50
|
+
# Get the mailer class.
|
51
|
+
#
|
52
|
+
def mailer
|
53
|
+
self.args['mailer'].constantize
|
54
|
+
end
|
55
|
+
|
56
|
+
# Get the mailer action as a symbol.
|
57
|
+
#
|
58
|
+
def action
|
59
|
+
self.args['action']&.to_sym
|
60
|
+
end
|
61
|
+
|
62
|
+
# Get the deserialized params.
|
63
|
+
#
|
64
|
+
def params
|
65
|
+
ActiveJob::Arguments.deserialize(self.args['params']).to_h
|
66
|
+
end
|
67
|
+
|
68
|
+
def perform(args)
|
69
|
+
self.log! "Building"
|
70
|
+
mail = self.assemble
|
71
|
+
|
72
|
+
self.log! "Delivering"
|
73
|
+
mail.deliver_now
|
74
|
+
|
75
|
+
self.log! "Delivered"
|
23
76
|
end
|
24
77
|
end
|
25
78
|
end
|
data/lib/postburner/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: postburner
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matt Smith
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-11-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -128,7 +128,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
128
128
|
- !ruby/object:Gem::Version
|
129
129
|
version: '0'
|
130
130
|
requirements: []
|
131
|
-
rubygems_version: 3.1.
|
131
|
+
rubygems_version: 3.1.6
|
132
132
|
signing_key:
|
133
133
|
specification_version: 4
|
134
134
|
summary: Postgres backed beanstalkd queue via backburner
|