good_job 2.9.4 → 2.9.5

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: b97fd131b5bf8c808b9b40b42e0daad06c3e657e9f9f093bda45ef7b1ad003cd
4
- data.tar.gz: 4b988574b3e6c5d6877f835c7b9a2956099737a508b824da53e3e61005f4fcc7
3
+ metadata.gz: c65c653e99d224dcc5803bd851fbc8891ecafd9634c68e4b0feaee08eafc1ed0
4
+ data.tar.gz: 6b6d8a61555bfacdf6ba82e69afa48a3d05c1c9d9239770cea86878067151727
5
5
  SHA512:
6
- metadata.gz: caf7be40dd4b22cd6461e5dc8f83349ea377cdf7edfd0082d85ec35a9636ee58d1e4d890ba7d6aae9cb81e1357c1e3d709501ff68d173f3443d399903f4185ac
7
- data.tar.gz: 913bcd9082c2a66d48e85af5d8f8bab86fdf355636a59fe1a4328f77f0fc952b13ee722d9b867238bd4da6dd3301820f2f5dd1407758f7cf23ec7152edd149f7
6
+ metadata.gz: 9135ae904e9031c97d9029af23d15b504b03383fbff4c43634416199a130d04ddafbd121ababe7f9675d997d1b5be8db37d098e060f4a51193035abe087052c8
7
+ data.tar.gz: 30235ec67f81832b6c9b7401a01c7e167d239fef9d38b5be4d83d56c98cb061bb8737432092bce7e18e7136f67c4df14dc917f83c227f3d0e83b578b9fe58468
data/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # Changelog
2
2
 
3
+ ## [v2.9.5](https://github.com/bensheldon/good_job/tree/v2.9.5) (2022-02-07)
4
+
5
+ [Full Changelog](https://github.com/bensheldon/good_job/compare/v2.9.4...v2.9.5)
6
+
7
+ **Fixed bugs:**
8
+
9
+ - Transactions in "aborting" threads do not commit; causes GoodJob::Process record not destroyed on exit [\#489](https://github.com/bensheldon/good_job/issues/489)
10
+ - Deserialize ActiveJob arguments when manually retrying a job [\#513](https://github.com/bensheldon/good_job/pull/513) ([bensheldon](https://github.com/bensheldon))
11
+
12
+ **Closed issues:**
13
+
14
+ - Concurrency key proc is missing `arguments` when retrying a discarded job. [\#512](https://github.com/bensheldon/good_job/issues/512)
15
+ - Cron Schedule not visible in dashboard [\#496](https://github.com/bensheldon/good_job/issues/496)
16
+
17
+ **Merged pull requests:**
18
+
19
+ - Rename methods to `advisory_lock_key` and allow it to take a block instead of `with_advisory_lock` [\#511](https://github.com/bensheldon/good_job/pull/511) ([bensheldon](https://github.com/bensheldon))
20
+ - README: Limiting concurrency - fetch symbol instead of string [\#510](https://github.com/bensheldon/good_job/pull/510) ([BenSto](https://github.com/BenSto))
21
+ - Add arbitrary lock on class level too [\#499](https://github.com/bensheldon/good_job/pull/499) ([pandwoter](https://github.com/pandwoter))
22
+
3
23
  ## [v2.9.4](https://github.com/bensheldon/good_job/tree/v2.9.4) (2022-01-31)
4
24
 
5
25
  [Full Changelog](https://github.com/bensheldon/good_job/compare/v2.9.3...v2.9.4)
data/README.md CHANGED
@@ -399,11 +399,9 @@ class MyJob < ApplicationJob
399
399
 
400
400
  # A unique key to be globally locked against.
401
401
  # Can be String or Lambda/Proc that is invoked in the context of the job.
402
- # Note: Arguments passed to #perform_later must be accessed through `arguments` method.
403
- key: -> { "Unique-#{arguments.first}" } # MyJob.perform_later("Alice") => "Unique-Alice"
404
-
405
- # If the method uses named parameters, they can be accessed like so:
406
- # key: -> { "Unique-#{arguments.first['name']}" } # MyJob.perform_later(name: "Alice")
402
+ # Note: Arguments passed to #perform_later can be accessed through ActiveJob's `arguments` method
403
+ # which is an array containing positional arguments and, optionally, a kwarg hash.
404
+ key: -> { "Unique-#{arguments.first}-#{arguments.last[:version]}" } # MyJob.perform_later("Alice", version: 'v2') => "Unique-Alice-v2"
407
405
  )
408
406
 
409
407
  def perform(first_name)
@@ -30,7 +30,7 @@ module GoodJob
30
30
  key = job.good_job_concurrency_key
31
31
  next(block.call) if key.blank?
32
32
 
33
- GoodJob::Execution.new.with_advisory_lock(key: key, function: "pg_advisory_lock") do
33
+ GoodJob::Execution.advisory_lock_key(key, function: "pg_advisory_lock") do
34
34
  enqueue_concurrency = if enqueue_limit
35
35
  GoodJob::Execution.where(concurrency_key: key).unfinished.advisory_unlocked.count
36
36
  else
@@ -61,7 +61,7 @@ module GoodJob
61
61
  key = job.good_job_concurrency_key
62
62
  next if key.blank?
63
63
 
64
- GoodJob::Execution.new.with_advisory_lock(key: key, function: "pg_advisory_lock") do
64
+ GoodJob::Execution.advisory_lock_key(key, function: "pg_advisory_lock") do
65
65
  allowed_active_job_ids = GoodJob::Execution.where(concurrency_key: key).advisory_locked.order(Arel.sql("COALESCE(performed_at, scheduled_at, created_at) ASC")).limit(perform_limit).pluck(:active_job_id)
66
66
  # The current job has already been locked and will appear in the previous query
67
67
  raise GoodJob::ActiveJobExtensions::Concurrency::ConcurrencyExceededError unless allowed_active_job_ids.include? job.job_id
@@ -267,7 +267,9 @@ module GoodJob
267
267
  end
268
268
 
269
269
  def active_job
270
- ActiveJob::Base.deserialize(active_job_data)
270
+ ActiveJob::Base.deserialize(active_job_data).tap do |aj|
271
+ aj.send(:deserialize_arguments_if_needed)
272
+ end
271
273
  end
272
274
 
273
275
  private
@@ -148,6 +148,7 @@ module GoodJob
148
148
  raise ArgumentError, "Must provide a block" unless block_given?
149
149
 
150
150
  records = advisory_lock(column: column, function: function).to_a
151
+
151
152
  begin
152
153
  unscoped { yield(records) }
153
154
  ensure
@@ -161,6 +162,53 @@ module GoodJob
161
162
  end
162
163
  end
163
164
 
165
+ # Acquires an advisory lock on this record if it is not already locked by
166
+ # another database session. Be careful to ensure you release the lock when
167
+ # you are done with {#advisory_unlock_key} to release all remaining locks.
168
+ # @param key [String, Symbol] Key to Advisory Lock against
169
+ # @param function [String, Symbol] Postgres Advisory Lock function name to use
170
+ # @return [Boolean] whether the lock was acquired.
171
+ def advisory_lock_key(key, function: advisory_lockable_function)
172
+ query = if function.include? "_try_"
173
+ <<~SQL.squish
174
+ SELECT #{function}(('x'||substr(md5($1::text), 1, 16))::bit(64)::bigint) AS locked
175
+ SQL
176
+ else
177
+ <<~SQL.squish
178
+ SELECT #{function}(('x'||substr(md5($1::text), 1, 16))::bit(64)::bigint)::text AS locked
179
+ SQL
180
+ end
181
+
182
+ binds = [
183
+ ActiveRecord::Relation::QueryAttribute.new('key', key, ActiveRecord::Type::String.new),
184
+ ]
185
+ locked = connection.exec_query(pg_or_jdbc_query(query), 'GoodJob::Lockable Advisory Lock', binds).first['locked']
186
+ return locked unless block_given?
187
+ return nil unless locked
188
+
189
+ begin
190
+ yield
191
+ ensure
192
+ advisory_unlock_key(key, function: advisory_unlockable_function(function))
193
+ end
194
+ end
195
+
196
+ # Releases an advisory lock on this record if it is locked by this database
197
+ # session. Note that advisory locks stack, so you must call
198
+ # {#advisory_unlock} and {#advisory_lock} the same number of times.
199
+ # @param key [String, Symbol] Key to lock against
200
+ # @param function [String, Symbol] Postgres Advisory Lock function name to use
201
+ # @return [Boolean] whether the lock was released.
202
+ def advisory_unlock_key(key, function: advisory_unlockable_function)
203
+ query = <<~SQL.squish
204
+ SELECT #{function}(('x'||substr(md5($1::text), 1, 16))::bit(64)::bigint) AS unlocked
205
+ SQL
206
+ binds = [
207
+ ActiveRecord::Relation::QueryAttribute.new('key', key, ActiveRecord::Type::String.new),
208
+ ]
209
+ connection.exec_query(pg_or_jdbc_query(query), 'GoodJob::Lockable Advisory Unlock', binds).first['unlocked']
210
+ end
211
+
164
212
  def _advisory_lockable_column
165
213
  advisory_lockable_column || primary_key
166
214
  end
@@ -205,20 +253,7 @@ module GoodJob
205
253
  # @param function [String, Symbol] Postgres Advisory Lock function name to use
206
254
  # @return [Boolean] whether the lock was acquired.
207
255
  def advisory_lock(key: lockable_key, function: advisory_lockable_function)
208
- query = if function.include? "_try_"
209
- <<~SQL.squish
210
- SELECT #{function}(('x'||substr(md5($1::text), 1, 16))::bit(64)::bigint) AS locked
211
- SQL
212
- else
213
- <<~SQL.squish
214
- SELECT #{function}(('x'||substr(md5($1::text), 1, 16))::bit(64)::bigint)::text AS locked
215
- SQL
216
- end
217
-
218
- binds = [
219
- ActiveRecord::Relation::QueryAttribute.new('key', key, ActiveRecord::Type::String.new),
220
- ]
221
- self.class.connection.exec_query(pg_or_jdbc_query(query), 'GoodJob::Lockable Advisory Lock', binds).first['locked']
256
+ self.class.advisory_lock_key(key, function: function)
222
257
  end
223
258
 
224
259
  # Releases an advisory lock on this record if it is locked by this database
@@ -228,13 +263,7 @@ module GoodJob
228
263
  # @param function [String, Symbol] Postgres Advisory Lock function name to use
229
264
  # @return [Boolean] whether the lock was released.
230
265
  def advisory_unlock(key: lockable_key, function: self.class.advisory_unlockable_function(advisory_lockable_function))
231
- query = <<~SQL.squish
232
- SELECT #{function}(('x'||substr(md5($1::text), 1, 16))::bit(64)::bigint) AS unlocked
233
- SQL
234
- binds = [
235
- ActiveRecord::Relation::QueryAttribute.new('key', key, ActiveRecord::Type::String.new),
236
- ]
237
- self.class.connection.exec_query(pg_or_jdbc_query(query), 'GoodJob::Lockable Advisory Unlock', binds).first['unlocked']
266
+ self.class.advisory_unlock_key(key, function: function)
238
267
  end
239
268
 
240
269
  # Acquires an advisory lock on this record or raises
@@ -245,8 +274,7 @@ module GoodJob
245
274
  # @raise [RecordAlreadyAdvisoryLockedError]
246
275
  # @return [Boolean] +true+
247
276
  def advisory_lock!(key: lockable_key, function: advisory_lockable_function)
248
- result = advisory_lock(key: key, function: function)
249
- result || raise(RecordAlreadyAdvisoryLockedError)
277
+ self.class.advisory_lock_key(key, function: function) || raise(RecordAlreadyAdvisoryLockedError)
250
278
  end
251
279
 
252
280
  # Acquires an advisory lock on this record and safely releases it after the
@@ -266,9 +294,11 @@ module GoodJob
266
294
  raise ArgumentError, "Must provide a block" unless block_given?
267
295
 
268
296
  advisory_lock!(key: key, function: function)
269
- yield
270
- ensure
271
- advisory_unlock(key: key, function: self.class.advisory_unlockable_function(function)) unless $ERROR_INFO.is_a? RecordAlreadyAdvisoryLockedError
297
+ begin
298
+ yield
299
+ ensure
300
+ advisory_unlock(key: key, function: self.class.advisory_unlockable_function(function))
301
+ end
272
302
  end
273
303
 
274
304
  # Tests whether this record has an advisory lock on it.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  module GoodJob
3
3
  # GoodJob gem version.
4
- VERSION = '2.9.4'
4
+ VERSION = '2.9.5'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: good_job
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.9.4
4
+ version: 2.9.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Sheldon
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-01-31 00:00:00.000000000 Z
11
+ date: 2022-02-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activejob