postburner 0.4.0 → 0.6.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6faa3078340ee2bc8d90e931ee1db203114e32ff69b288ec69340e8b86a987cd
4
- data.tar.gz: 0a2ddf78a320e9b01c26a0e3fbd3158b78d37a89bca04a5fdd8c64b70eada1a7
3
+ metadata.gz: 6d47898f4be7a93d1643ba6ff32db238b341a2ceed09bbc18de2774d3048d9cd
4
+ data.tar.gz: 934f5a102f6644e679c2bf30d4708e171f01d88b5652fe6bcef2cac8b6fe4c50
5
5
  SHA512:
6
- metadata.gz: 5253309eb7aff502623040d49e923faff7c91df6aac407ec7a4e306a8317b1363db43a4b0408eb20fbdad6ee017ad68489480ac50d7e2ac68a612a1337488457
7
- data.tar.gz: 49d0253fe3ecde9cfd4de8b48a2ce95997245bea4310f7c751ccca3685fea8a42b29310a8058f222031ee8aa91f6ec61f990ec5c330157e39853c95830236cfa
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
- case
21
- when options[:at].present?
22
- # this is rudimentary, add error handling
23
- options[:delay] ||= options[:at].to_i - Time.zone.now.to_i
24
- update_column :run_at, options[:at]
25
- options.delete(:at)
26
- when options[:delay].present?
27
- update_column :run_at, Time.zone.now + options[:delay].seconds
28
- end
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
- insert!(options)
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!('START')
118
+ self.log!("START (bkid #{self.bkid})")
76
119
 
77
- self.perform(args)
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!('DONE')
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
- self.update_columns(
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
- options[:level],
170
- message,
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 = Backburner::Worker.enqueue(Postburner::Job, self.id, options)
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.deliver(mailer, action)
11
- job = self.create!(
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: ActiveJob::Arguments.serialize(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
- def assemble(args=nil)
33
- _args = args || self.args
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
- cls = _args['mailer'].constantize
50
+ # Get the mailer class.
51
+ #
52
+ def mailer
53
+ self.args['mailer'].constantize
54
+ end
36
55
 
37
- mail = cls.
38
- with(ActiveJob::Arguments.deserialize(_args['params']).to_h).
39
- send(_args['action'])
56
+ # Get the mailer action as a symbol.
57
+ #
58
+ def action
59
+ self.args['action']&.to_sym
60
+ end
40
61
 
41
- mail
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.assemble(args).deliver_now
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
@@ -1,3 +1,3 @@
1
1
  module Postburner
2
- VERSION = '0.4.0'
2
+ VERSION = '0.6.2'
3
3
  end
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.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-10-12 00:00:00.000000000 Z
11
+ date: 2021-11-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails