good_job 2.9.4 → 2.9.5
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 +20 -0
- data/README.md +3 -5
- data/lib/good_job/active_job_extensions/concurrency.rb +2 -2
- data/lib/good_job/execution.rb +3 -1
- data/lib/good_job/lockable.rb +56 -26
- data/lib/good_job/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: c65c653e99d224dcc5803bd851fbc8891ecafd9634c68e4b0feaee08eafc1ed0
|
|
4
|
+
data.tar.gz: 6b6d8a61555bfacdf6ba82e69afa48a3d05c1c9d9239770cea86878067151727
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
|
403
|
-
|
|
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.
|
|
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.
|
|
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
|
data/lib/good_job/execution.rb
CHANGED
data/lib/good_job/lockable.rb
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
270
|
-
|
|
271
|
-
|
|
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.
|
data/lib/good_job/version.rb
CHANGED
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
|
+
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-
|
|
11
|
+
date: 2022-02-07 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activejob
|