postburner 0.4.0 → 0.6.2
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 +26 -0
- data/README.md +11 -0
- data/app/models/postburner/job.rb +113 -35
- data/app/models/postburner/mailer.rb +41 -11
- data/lib/postburner/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6d47898f4be7a93d1643ba6ff32db238b341a2ceed09bbc18de2774d3048d9cd
|
4
|
+
data.tar.gz: 934f5a102f6644e679c2bf30d4708e171f01d88b5652fe6bcef2cac8b6fe4c50
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cb29ebe178b4675552a441c335fd95867a9a26c0a15235c42d5dff0f8ab72c40c417c728bc171ecf461ef3e78c9672364b89fae3b91419dceac59f80ad4c9986
|
7
|
+
data.tar.gz: 807cb26488522a5a3becdc53fbc43b44a9123d1355ee31d2b79dc2d1d479ce714a6b3a5f20c60b74e2f2fc22753736e6fde9822fe59e83feab69024e34e60deb
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,31 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## v0.6.2 - 2021-11-01
|
4
|
+
|
5
|
+
### Fixed
|
6
|
+
- add #requeue method.
|
7
|
+
- check if AlreadyProcessed.
|
8
|
+
|
9
|
+
## v0.6.1 - 2021-11-01
|
10
|
+
|
11
|
+
### Updated
|
12
|
+
- update run_at logic to support only inserting after save commit.
|
13
|
+
|
14
|
+
## v0.6.0 - 2021-11-01
|
15
|
+
|
16
|
+
### Added
|
17
|
+
- update exception handling to better show where the error was raised.
|
18
|
+
- update logging to match errata format.
|
19
|
+
- update documentation.
|
20
|
+
|
21
|
+
## v0.5.0 - 2021-10-27
|
22
|
+
|
23
|
+
### Added
|
24
|
+
- add mailer, action, params accessors to Mailer for easy access in subclassed jobs.
|
25
|
+
- add logging to Mailer perform.
|
26
|
+
- add save to #queue! job if unsaved
|
27
|
+
- add block with logging if queued_at isn't set or isn't prior to run time.
|
28
|
+
|
3
29
|
## v0.4.0 - 2021-10-12
|
4
30
|
|
5
31
|
### Added
|
data/README.md
CHANGED
@@ -47,6 +47,17 @@ RunDonation.create!(args: {donation_id: 123}).queue! delay: 1.hour
|
|
47
47
|
=> {:status=>"INSERTED", :id=>"1141"}
|
48
48
|
```
|
49
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
|
+
|
50
61
|
### [Beaneater](https://github.com/beanstalkd/beaneater) and [beanstalkd](https://raw.githubusercontent.com/beanstalkd/beanstalkd/master/doc/protocol.txt) attributes and methods
|
51
62
|
```ruby
|
52
63
|
# get the beanstalkd job id
|
@@ -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
|
|
@@ -11,36 +24,56 @@ module Postburner
|
|
11
24
|
|
12
25
|
before_validation :ensure_sid!
|
13
26
|
before_destroy :delete!
|
27
|
+
after_save_commit :insert_if_queued!
|
14
28
|
|
15
29
|
validates :sid, presence: {strict: true}
|
16
30
|
|
17
31
|
def queue!(options={})
|
18
32
|
return if self.queued_at.present? && self.bkid.present?
|
33
|
+
raise ActiveRecord::RecordInvalid, "Can't queue unless valid." unless self.valid?
|
34
|
+
raise AlreadyProcessed, "Processed at #{self.processed_at}" if self.processed_at
|
19
35
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
36
|
+
at = options.delete(:at)
|
37
|
+
now = Time.zone.now
|
38
|
+
|
39
|
+
self.queued_at = now
|
40
|
+
self.run_at = case
|
41
|
+
when at.present?
|
42
|
+
# this is rudimentary, add error handling
|
43
|
+
options[:delay] ||= at.to_i - now.to_i
|
44
|
+
at
|
45
|
+
when options[:delay].present?
|
46
|
+
now + options[:delay].seconds
|
47
|
+
end
|
48
|
+
|
49
|
+
@_insert_options = options
|
50
|
+
|
51
|
+
self.save!
|
52
|
+
end
|
53
|
+
|
54
|
+
def requeue!(options={})
|
55
|
+
self.delete!
|
56
|
+
self.bkid, self.queued_at = nil, nil
|
29
57
|
|
30
|
-
|
58
|
+
self.queue! options
|
59
|
+
end
|
60
|
+
|
61
|
+
def will_insert?
|
62
|
+
@_insert_options.is_a? Hash
|
31
63
|
end
|
32
64
|
|
33
65
|
# tube: backburner.worker.queue.backburner-jobs
|
34
66
|
#
|
35
67
|
def self.perform(id, _={})
|
68
|
+
job = nil
|
36
69
|
begin
|
37
70
|
job = self.find(id)
|
38
|
-
job.perform!(job.args)
|
39
71
|
rescue ActiveRecord::RecordNotFound => e
|
40
72
|
Rails.logger.warn <<-MSG
|
41
73
|
[Postburner::Job] [#{id}] Not Found.
|
42
74
|
MSG
|
43
75
|
end
|
76
|
+
job&.perform!(job.args)
|
44
77
|
end
|
45
78
|
|
46
79
|
def perform!(args={})
|
@@ -55,6 +88,16 @@ module Postburner
|
|
55
88
|
)
|
56
89
|
|
57
90
|
begin
|
91
|
+
if self.queued_at.nil?
|
92
|
+
self.log! "Not Queued", level: :error
|
93
|
+
return
|
94
|
+
end
|
95
|
+
|
96
|
+
if self.queued_at > Time.zone.now
|
97
|
+
self.log! "Future Queued", level: :error
|
98
|
+
return
|
99
|
+
end
|
100
|
+
|
58
101
|
if self.processed_at.present?
|
59
102
|
self.log! "Already Processed", level: :error
|
60
103
|
self.delete!
|
@@ -72,22 +115,24 @@ module Postburner
|
|
72
115
|
return
|
73
116
|
end
|
74
117
|
|
75
|
-
self.log!(
|
118
|
+
self.log!("START (bkid #{self.bkid})")
|
76
119
|
|
77
|
-
|
120
|
+
begin
|
121
|
+
self.perform(args)
|
122
|
+
rescue Exception => exception
|
123
|
+
self.persist_metadata!
|
124
|
+
self.log! '[Postburner] Exception raised during perform prevented completion.'
|
125
|
+
raise exception
|
126
|
+
end
|
78
127
|
|
79
|
-
self.log!(
|
128
|
+
self.log!("DONE (bkid #{self.bkid})")
|
80
129
|
|
81
130
|
begin
|
82
131
|
now = Time.zone.now
|
83
132
|
_duration = (now - self.processing_at) * 1000 rescue nil
|
84
|
-
|
133
|
+
persist_metadata!(
|
85
134
|
processed_at: now,
|
86
135
|
duration: _duration,
|
87
|
-
errata: self.errata,
|
88
|
-
error_count: self.errata.length,
|
89
|
-
logs: self.logs,
|
90
|
-
log_count: self.logs.length,
|
91
136
|
)
|
92
137
|
rescue Exception => e
|
93
138
|
self.log_exception!(e)
|
@@ -97,7 +142,6 @@ module Postburner
|
|
97
142
|
|
98
143
|
rescue Exception => exception
|
99
144
|
self.log_exception!(exception)
|
100
|
-
self.log! '[Postburner] Exception raised during perform prevented completion.'
|
101
145
|
raise exception
|
102
146
|
end
|
103
147
|
|
@@ -148,6 +192,7 @@ module Postburner
|
|
148
192
|
self.errata << [
|
149
193
|
Time.zone.now,
|
150
194
|
{
|
195
|
+
bkid: self.bkid,
|
151
196
|
class: exception.class,
|
152
197
|
message: exception.message,
|
153
198
|
backtrace: exception.backtrace,
|
@@ -165,30 +210,67 @@ module Postburner
|
|
165
210
|
options[:level] = :error unless LOG_LEVELS.member?(options[:level])
|
166
211
|
|
167
212
|
self.logs << [
|
168
|
-
Time.zone.now,
|
169
|
-
|
170
|
-
|
213
|
+
Time.zone.now, # time
|
214
|
+
{
|
215
|
+
bkid: self.bkid,
|
216
|
+
level: options[:level], # level
|
217
|
+
message: message, # message
|
218
|
+
elapsed: self.elapsed_ms, # ms from start
|
219
|
+
}
|
171
220
|
]
|
172
221
|
end
|
173
222
|
|
223
|
+
# ms from attempting_at
|
224
|
+
#
|
225
|
+
def elapsed_ms
|
226
|
+
return unless self.attempting_at
|
227
|
+
(Time.zone.now - self.attempting_at) * 1000
|
228
|
+
end
|
229
|
+
|
174
230
|
def log!(message, options={})
|
175
231
|
self.log(message, options)
|
176
232
|
self.update_column :logs, self.logs
|
177
233
|
end
|
178
234
|
|
235
|
+
def intended_at
|
236
|
+
self.run_at ? self.run_at : self.queued_at
|
237
|
+
end
|
238
|
+
|
239
|
+
class AlreadyProcessed < StandardError; end
|
240
|
+
|
179
241
|
private
|
180
242
|
|
243
|
+
def persist_metadata!(data={})
|
244
|
+
self.update_columns({
|
245
|
+
errata: self.errata,
|
246
|
+
error_count: self.errata.length,
|
247
|
+
logs: self.logs,
|
248
|
+
log_count: self.logs.length,
|
249
|
+
}.merge(data))
|
250
|
+
end
|
251
|
+
|
252
|
+
def insert_if_queued!
|
253
|
+
return unless self.will_insert?
|
254
|
+
insert!(@_insert_options)
|
255
|
+
end
|
256
|
+
|
181
257
|
def insert!(options={})
|
182
|
-
response =
|
258
|
+
response = nil
|
259
|
+
|
260
|
+
Job.transaction do
|
261
|
+
response = Backburner::Worker.enqueue(
|
262
|
+
Postburner::Job,
|
263
|
+
self.id,
|
264
|
+
options
|
265
|
+
)
|
266
|
+
|
267
|
+
persist_metadata!(
|
268
|
+
bkid: response[:id],
|
269
|
+
)
|
270
|
+
end
|
183
271
|
|
184
272
|
self.log("QUEUED: #{response}")
|
185
273
|
|
186
|
-
update_columns(
|
187
|
-
queued_at: Time.zone.now,
|
188
|
-
bkid: response[:id],
|
189
|
-
logs: self.logs,
|
190
|
-
)
|
191
|
-
|
192
274
|
response
|
193
275
|
end
|
194
276
|
|
@@ -196,14 +278,10 @@ module Postburner
|
|
196
278
|
now = Time.zone.now
|
197
279
|
self.attempts << now
|
198
280
|
self.attempting_at ||= now
|
199
|
-
self.lag ||= (self.attempting_at - self.intended_at) * 1000
|
281
|
+
self.lag ||= (self.attempting_at - self.intended_at) * 1000 rescue nil
|
200
282
|
now
|
201
283
|
end
|
202
284
|
|
203
|
-
def intended_at
|
204
|
-
self.run_at ? self.run_at : self.queued_at
|
205
|
-
end
|
206
|
-
|
207
285
|
def ensure_sid!
|
208
286
|
self.sid ||= SecureRandom.uuid
|
209
287
|
end
|
@@ -7,8 +7,8 @@ module Postburner
|
|
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,32 +17,62 @@ module Postburner
|
|
17
17
|
job
|
18
18
|
end
|
19
19
|
|
20
|
+
def self.delivery!(mailer, action)
|
21
|
+
job = self.delivery(mailer, action)
|
22
|
+
job.save!
|
23
|
+
job
|
24
|
+
end
|
25
|
+
|
20
26
|
# Similar to ActionMailer #with - set the parameters
|
21
27
|
#
|
22
28
|
def with(params={})
|
23
29
|
self.args.merge!(
|
24
|
-
params
|
30
|
+
'params' => ActiveJob::Arguments.serialize(params)
|
25
31
|
)
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
def with!(params={})
|
36
|
+
self.with(params)
|
26
37
|
self.save!
|
27
38
|
self
|
28
39
|
end
|
29
40
|
|
30
41
|
# Build the mail but don't send.
|
31
42
|
#
|
32
|
-
|
33
|
-
|
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
|
34
49
|
|
35
|
-
|
50
|
+
# Get the mailer class.
|
51
|
+
#
|
52
|
+
def mailer
|
53
|
+
self.args['mailer'].constantize
|
54
|
+
end
|
36
55
|
|
37
|
-
|
38
|
-
|
39
|
-
|
56
|
+
# Get the mailer action as a symbol.
|
57
|
+
#
|
58
|
+
def action
|
59
|
+
self.args['action']&.to_sym
|
60
|
+
end
|
40
61
|
|
41
|
-
|
62
|
+
# Get the deserialized params.
|
63
|
+
#
|
64
|
+
def params
|
65
|
+
ActiveJob::Arguments.deserialize(self.args['params']).to_h
|
42
66
|
end
|
43
67
|
|
44
68
|
def perform(args)
|
45
|
-
self.
|
69
|
+
self.log! "Building"
|
70
|
+
mail = self.assemble
|
71
|
+
|
72
|
+
self.log! "Delivering"
|
73
|
+
mail.deliver_now
|
74
|
+
|
75
|
+
self.log! "Delivered"
|
46
76
|
end
|
47
77
|
end
|
48
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.2
|
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
|