good_job 4.14.1 → 4.14.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 +12 -0
- data/app/charts/good_job/performance_index_chart.rb +1 -1
- data/app/charts/good_job/performance_show_chart.rb +1 -1
- data/app/charts/good_job/scheduled_by_queue_chart.rb +1 -1
- data/app/models/concerns/good_job/advisory_lockable.rb +69 -51
- data/app/models/concerns/good_job/filterable.rb +1 -1
- data/app/models/good_job/base_record.rb +7 -0
- data/config/brakeman.ignore +42 -0
- data/lib/good_job/capsule_tracker.rb +4 -3
- data/lib/good_job/notifier.rb +5 -4
- data/lib/good_job/overridable_connection.rb +6 -0
- data/lib/good_job/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0f0f62c12c0c19a4afd8f71d3dae19479ac4a7df6342ac399bf8e447fbbe8f5b
|
|
4
|
+
data.tar.gz: 75a2a8a282b0c28e370230397dfc5f1de470681b1d53cbb0dd6ee46384e87fe4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ba1a959622762c21e723f6d406d30d8fda81d736560f3c710a7c363d8206697d0eb856dbe9ee865a340ad4f3d8db5354a707f78d695d0221fe1779ba07709c7b
|
|
7
|
+
data.tar.gz: 0bb8d09ca31aefe0e6da18096da53f5fdfc7e7be820afb54791b051f7ea29464c730aefb9eaeffaeaaa5fa3a6f5e029d158651fde41bd8876fd280e3fcd4269a
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [v4.14.2](https://github.com/bensheldon/good_job/tree/v4.14.2) (2026-04-06)
|
|
4
|
+
|
|
5
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v4.14.1...v4.14.2)
|
|
6
|
+
|
|
7
|
+
**Closed issues:**
|
|
8
|
+
|
|
9
|
+
- Incompatible with permanent\_connection\_checkout = :disallowed [\#1729](https://github.com/bensheldon/good_job/issues/1729)
|
|
10
|
+
|
|
11
|
+
**Merged pull requests:**
|
|
12
|
+
|
|
13
|
+
- Replace Base.connection with lease\_connection and with\_connection throughout [\#1730](https://github.com/bensheldon/good_job/pull/1730) ([bensheldon](https://github.com/bensheldon))
|
|
14
|
+
|
|
3
15
|
## [v4.14.1](https://github.com/bensheldon/good_job/tree/v4.14.1) (2026-04-03)
|
|
4
16
|
|
|
5
17
|
[Full Changelog](https://github.com/bensheldon/good_job/compare/v4.14.0...v4.14.1)
|
|
@@ -23,7 +23,7 @@ module GoodJob
|
|
|
23
23
|
ORDER BY timestamp ASC
|
|
24
24
|
SQL
|
|
25
25
|
|
|
26
|
-
executions_data = GoodJob::Job.
|
|
26
|
+
executions_data = GoodJob::Job.connection_pool.with_connection { |conn| conn.exec_query(GoodJob::Job.pg_or_jdbc_query(sum_query), "GoodJob Performance Chart", start_end_binds) }
|
|
27
27
|
|
|
28
28
|
job_names = executions_data.reject { |d| d['sum'].nil? }.map { |d| d['job_class'] || BaseFilter::EMPTY }.uniq
|
|
29
29
|
labels = []
|
|
@@ -40,7 +40,7 @@ module GoodJob
|
|
|
40
40
|
]
|
|
41
41
|
labels = BUCKET_INTERVALS.map { |interval| GoodJob::ApplicationController.helpers.format_duration(interval) }
|
|
42
42
|
labels[-1] = I18n.t("good_job.performance.show.slow")
|
|
43
|
-
executions_data = GoodJob::Job.
|
|
43
|
+
executions_data = GoodJob::Job.connection_pool.with_connection { |conn| conn.exec_query(GoodJob::Job.pg_or_jdbc_query(sum_query), "GoodJob Performance Job Chart", binds) }
|
|
44
44
|
executions_data = executions_data.to_a.index_by { |data| data["bucket_index"] }
|
|
45
45
|
|
|
46
46
|
bucket_data = 0.upto(BUCKET_INTERVALS.count).map do |bucket_index|
|
|
@@ -28,7 +28,7 @@ module GoodJob
|
|
|
28
28
|
ORDER BY timestamp ASC
|
|
29
29
|
SQL
|
|
30
30
|
|
|
31
|
-
executions_data = GoodJob::Job.
|
|
31
|
+
executions_data = GoodJob::Job.connection_pool.with_connection { |conn| conn.exec_query(GoodJob::Job.pg_or_jdbc_query(count_query), "GoodJob Dashboard Chart", start_end_binds) }
|
|
32
32
|
|
|
33
33
|
queue_names = executions_data.reject { |d| d['count'].nil? }.map { |d| d['queue_name'] || BaseFilter::EMPTY }.uniq
|
|
34
34
|
labels = []
|
|
@@ -30,6 +30,24 @@ module GoodJob
|
|
|
30
30
|
# Default Postgres function to be used for Advisory Locks
|
|
31
31
|
class_attribute :advisory_lockable_function, default: "pg_try_advisory_lock"
|
|
32
32
|
|
|
33
|
+
# Rails < 7.2 does not have lease_connection as a class method.
|
|
34
|
+
define_singleton_method(:lease_connection) { connection } unless respond_to?(:lease_connection)
|
|
35
|
+
|
|
36
|
+
# Rails < 7.2 does not have adapter_class as a class method, and adapter
|
|
37
|
+
# quoting methods (quote_table_name, quote_column_name) are instance-only.
|
|
38
|
+
# Provide a proxy that responds to those methods by delegating to a connection.
|
|
39
|
+
unless respond_to?(:adapter_class)
|
|
40
|
+
define_singleton_method(:adapter_class) do
|
|
41
|
+
@_adapter_class ||= begin
|
|
42
|
+
pool = connection_pool
|
|
43
|
+
proxy = Object.new
|
|
44
|
+
proxy.define_singleton_method(:quote_table_name) { |name| pool.with_connection { |c| c.quote_table_name(name) } }
|
|
45
|
+
proxy.define_singleton_method(:quote_column_name) { |name| pool.with_connection { |c| c.quote_column_name(name) } }
|
|
46
|
+
proxy
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
33
51
|
# Attempt to acquire an advisory lock on the selected records and
|
|
34
52
|
# return only those records for which a lock could be acquired.
|
|
35
53
|
# @!method advisory_lock(column: _advisory_lockable_column, function: advisory_lockable_function)
|
|
@@ -39,6 +57,7 @@ module GoodJob
|
|
|
39
57
|
# @return [ActiveRecord::Relation]
|
|
40
58
|
# A relation selecting only the records that were locked.
|
|
41
59
|
scope :advisory_lock, (lambda do |column: _advisory_lockable_column, function: advisory_lockable_function, select_limit: nil|
|
|
60
|
+
lease_connection # ensure a sticky connection; advisory locks are session-scoped and must outlive this query
|
|
42
61
|
original_query = self
|
|
43
62
|
|
|
44
63
|
primary_key_for_select = primary_key.to_sym
|
|
@@ -55,7 +74,7 @@ module GoodJob
|
|
|
55
74
|
cte_type = supports_cte_materialization_specifiers? ? :MATERIALIZED : :""
|
|
56
75
|
composed_cte = Arel::Nodes::As.new(cte_table, Arel::Nodes::UnaryOperation.new(cte_type, cte_query.arel))
|
|
57
76
|
|
|
58
|
-
lock_condition = "#{function}(('x' || substr(md5(#{
|
|
77
|
+
lock_condition = "#{function}(('x' || substr(md5(#{_quoted_table_name_string} || '-' || #{adapter_class.quote_table_name(cte_table.name)}.#{adapter_class.quote_column_name(column)}::text), 1, 16))::bit(64)::bigint)"
|
|
59
78
|
query = cte_table.project(cte_table[:id])
|
|
60
79
|
.with(composed_cte)
|
|
61
80
|
.where(defined?(Arel::Nodes::BoundSqlLiteral) ? Arel::Nodes::BoundSqlLiteral.new(lock_condition, [], {}) : Arel::Nodes::SqlLiteral.new(lock_condition))
|
|
@@ -82,11 +101,12 @@ module GoodJob
|
|
|
82
101
|
# @example Get the records that have a session awaiting a lock:
|
|
83
102
|
# MyLockableRecord.joins_advisory_locks.where("pg_locks.granted = ?", false)
|
|
84
103
|
scope :joins_advisory_locks, (lambda do |column: _advisory_lockable_column|
|
|
104
|
+
quoted_column = adapter_class.quote_column_name(column)
|
|
85
105
|
joins(<<~SQL.squish)
|
|
86
106
|
LEFT JOIN pg_locks ON pg_locks.locktype = 'advisory'
|
|
87
107
|
AND pg_locks.objsubid = 1
|
|
88
|
-
AND pg_locks.classid = ('x' || substr(md5(#{
|
|
89
|
-
AND pg_locks.objid = (('x' || substr(md5(#{
|
|
108
|
+
AND pg_locks.classid = ('x' || substr(md5(#{_quoted_table_name_string} || '-' || #{quoted_table_name}.#{quoted_column}::text), 1, 16))::bit(32)::int
|
|
109
|
+
AND pg_locks.objid = (('x' || substr(md5(#{_quoted_table_name_string} || '-' || #{quoted_table_name}.#{quoted_column}::text), 1, 16))::bit(64) << 32)::bit(32)::int
|
|
90
110
|
SQL
|
|
91
111
|
end)
|
|
92
112
|
|
|
@@ -96,8 +116,8 @@ module GoodJob
|
|
|
96
116
|
# @param column [String, Symbol] column values to Advisory Lock against
|
|
97
117
|
# @return [ActiveRecord::Relation]
|
|
98
118
|
scope :includes_advisory_locks, (lambda do |column: _advisory_lockable_column|
|
|
99
|
-
owns_advisory_lock_sql = "#{
|
|
100
|
-
joins_advisory_locks(column: column).select("#{quoted_table_name}.*, #{
|
|
119
|
+
owns_advisory_lock_sql = "#{adapter_class.quote_table_name('pg_locks')}.#{adapter_class.quote_column_name('pid')} = pg_backend_pid() AS owns_advisory_lock"
|
|
120
|
+
joins_advisory_locks(column: column).select("#{quoted_table_name}.*, #{adapter_class.quote_table_name('pg_locks')}.locktype, #{owns_advisory_lock_sql}")
|
|
101
121
|
end)
|
|
102
122
|
|
|
103
123
|
# Find records that do not have an advisory lock on them.
|
|
@@ -174,18 +194,20 @@ module GoodJob
|
|
|
174
194
|
def with_advisory_lock(column: _advisory_lockable_column, function: advisory_lockable_function, unlock_session: false, select_limit: nil)
|
|
175
195
|
raise ArgumentError, "Must provide a block" unless block_given?
|
|
176
196
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
197
|
+
connection_pool.with_connection do
|
|
198
|
+
records = advisory_lock(column: column, function: function, select_limit: select_limit).to_a
|
|
199
|
+
|
|
200
|
+
begin
|
|
201
|
+
unscoped { yield(records) }
|
|
202
|
+
ensure
|
|
203
|
+
if unlock_session
|
|
204
|
+
advisory_unlock_session
|
|
205
|
+
else
|
|
206
|
+
unlock_function = advisory_unlockable_function(function)
|
|
207
|
+
if unlock_function
|
|
208
|
+
records.each do |record|
|
|
209
|
+
record.advisory_unlock(key: record.lockable_column_key(column: column), function: unlock_function)
|
|
210
|
+
end
|
|
189
211
|
end
|
|
190
212
|
end
|
|
191
213
|
end
|
|
@@ -213,15 +235,20 @@ module GoodJob
|
|
|
213
235
|
ActiveRecord::Relation::QueryAttribute.new('key', key, ActiveRecord::Type::String.new),
|
|
214
236
|
]
|
|
215
237
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
238
|
+
if block_given?
|
|
239
|
+
connection_pool.with_connection do |conn|
|
|
240
|
+
locked = conn.exec_query(pg_or_jdbc_query(query), 'GoodJob::Lockable Advisory Lock', binds).first['locked']
|
|
241
|
+
return nil unless locked
|
|
219
242
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
243
|
+
begin
|
|
244
|
+
yield
|
|
245
|
+
ensure
|
|
246
|
+
unlock_function = advisory_unlockable_function(function)
|
|
247
|
+
advisory_unlock_key(key, function: unlock_function) if unlock_function
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
else
|
|
251
|
+
lease_connection.exec_query(pg_or_jdbc_query(query), 'GoodJob::Lockable Advisory Lock', binds).first['locked']
|
|
225
252
|
end
|
|
226
253
|
end
|
|
227
254
|
|
|
@@ -241,7 +268,7 @@ module GoodJob
|
|
|
241
268
|
binds = [
|
|
242
269
|
ActiveRecord::Relation::QueryAttribute.new('key', key, ActiveRecord::Type::String.new),
|
|
243
270
|
]
|
|
244
|
-
|
|
271
|
+
lease_connection.exec_query(pg_or_jdbc_query(query), 'GoodJob::Lockable Advisory Unlock', binds).first['unlocked']
|
|
245
272
|
end
|
|
246
273
|
|
|
247
274
|
# Tests whether the provided key has an advisory lock on it.
|
|
@@ -261,7 +288,7 @@ module GoodJob
|
|
|
261
288
|
ActiveRecord::Relation::QueryAttribute.new('key', key, ActiveRecord::Type::String.new),
|
|
262
289
|
ActiveRecord::Relation::QueryAttribute.new('key', key, ActiveRecord::Type::String.new),
|
|
263
290
|
]
|
|
264
|
-
|
|
291
|
+
connection_pool.with_connection { |conn| conn.exec_query(pg_or_jdbc_query(query), 'GoodJob::Lockable Advisory Locked?', binds).any? }
|
|
265
292
|
end
|
|
266
293
|
|
|
267
294
|
# Tests whether this record is locked by the current database session.
|
|
@@ -282,17 +309,21 @@ module GoodJob
|
|
|
282
309
|
ActiveRecord::Relation::QueryAttribute.new('key', key, ActiveRecord::Type::String.new),
|
|
283
310
|
ActiveRecord::Relation::QueryAttribute.new('key', key, ActiveRecord::Type::String.new),
|
|
284
311
|
]
|
|
285
|
-
|
|
312
|
+
lease_connection.exec_query(pg_or_jdbc_query(query), 'GoodJob::Lockable Owns Advisory Lock?', binds).any?
|
|
286
313
|
end
|
|
287
314
|
|
|
288
315
|
def _advisory_lockable_column
|
|
289
316
|
advisory_lockable_column || primary_key
|
|
290
317
|
end
|
|
291
318
|
|
|
319
|
+
def _quoted_table_name_string
|
|
320
|
+
@_quoted_table_name_string ||= "'#{table_name.gsub("'", "''")}'"
|
|
321
|
+
end
|
|
322
|
+
|
|
292
323
|
def supports_cte_materialization_specifiers?
|
|
293
324
|
return @_supports_cte_materialization_specifiers if defined?(@_supports_cte_materialization_specifiers)
|
|
294
325
|
|
|
295
|
-
@_supports_cte_materialization_specifiers =
|
|
326
|
+
@_supports_cte_materialization_specifiers = connection_pool.with_connection { |conn| conn.postgresql_version >= 120000 }
|
|
296
327
|
end
|
|
297
328
|
|
|
298
329
|
# Postgres advisory unlocking function for the class
|
|
@@ -307,7 +338,7 @@ module GoodJob
|
|
|
307
338
|
# Unlocks all advisory locks active in the current database session/connection
|
|
308
339
|
# @return [void]
|
|
309
340
|
def advisory_unlock_session
|
|
310
|
-
|
|
341
|
+
lease_connection.exec_query("SELECT pg_advisory_unlock_all()::text AS unlocked", 'GoodJob::Lockable Unlock Session').first[:unlocked]
|
|
311
342
|
end
|
|
312
343
|
|
|
313
344
|
# Converts SQL query strings between PG-compatible and JDBC-compatible syntax
|
|
@@ -371,12 +402,14 @@ module GoodJob
|
|
|
371
402
|
def with_advisory_lock(key: lockable_key, function: advisory_lockable_function)
|
|
372
403
|
raise ArgumentError, "Must provide a block" unless block_given?
|
|
373
404
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
405
|
+
self.class.connection_pool.with_connection do
|
|
406
|
+
advisory_lock!(key: key, function: function)
|
|
407
|
+
begin
|
|
408
|
+
yield
|
|
409
|
+
ensure
|
|
410
|
+
unlock_function = self.class.advisory_unlockable_function(function)
|
|
411
|
+
advisory_unlock(key: key, function: unlock_function) if unlock_function
|
|
412
|
+
end
|
|
380
413
|
end
|
|
381
414
|
end
|
|
382
415
|
|
|
@@ -399,21 +432,6 @@ module GoodJob
|
|
|
399
432
|
# @return [Boolean]
|
|
400
433
|
def owns_advisory_lock?(key: lockable_key)
|
|
401
434
|
self.class.owns_advisory_lock_key?(key)
|
|
402
|
-
query = <<~SQL.squish
|
|
403
|
-
SELECT 1 AS one
|
|
404
|
-
FROM pg_locks
|
|
405
|
-
WHERE pg_locks.locktype = 'advisory'
|
|
406
|
-
AND pg_locks.objsubid = 1
|
|
407
|
-
AND pg_locks.classid = ('x' || substr(md5($1::text), 1, 16))::bit(32)::int
|
|
408
|
-
AND pg_locks.objid = (('x' || substr(md5($2::text), 1, 16))::bit(64) << 32)::bit(32)::int
|
|
409
|
-
AND pg_locks.pid = pg_backend_pid()
|
|
410
|
-
LIMIT 1
|
|
411
|
-
SQL
|
|
412
|
-
binds = [
|
|
413
|
-
ActiveRecord::Relation::QueryAttribute.new('key', key, ActiveRecord::Type::String.new),
|
|
414
|
-
ActiveRecord::Relation::QueryAttribute.new('key', key, ActiveRecord::Type::String.new),
|
|
415
|
-
]
|
|
416
|
-
self.class.connection.exec_query(pg_or_jdbc_query(query), 'GoodJob::Lockable Owns Advisory Lock?', binds).any?
|
|
417
435
|
end
|
|
418
436
|
|
|
419
437
|
# Releases all advisory locks on the record that are held by the current
|
|
@@ -52,7 +52,7 @@ module GoodJob
|
|
|
52
52
|
def database_supports_websearch_to_tsquery?
|
|
53
53
|
return @_database_supports_websearch_to_tsquery if defined?(@_database_supports_websearch_to_tsquery)
|
|
54
54
|
|
|
55
|
-
@_database_supports_websearch_to_tsquery =
|
|
55
|
+
@_database_supports_websearch_to_tsquery = connection_pool.with_connection { |conn| conn.postgresql_version >= 110000 }
|
|
56
56
|
end
|
|
57
57
|
end
|
|
58
58
|
end
|
|
@@ -44,6 +44,13 @@ module GoodJob
|
|
|
44
44
|
def self.bind_value(name, value, type_class)
|
|
45
45
|
Arel::Nodes::BindParam.new(ActiveRecord::Relation::QueryAttribute.new(name, value, type_class.new))
|
|
46
46
|
end
|
|
47
|
+
|
|
48
|
+
# Rails < 7.2 does not have lease_connection; connection is behaviorally equivalent.
|
|
49
|
+
unless respond_to?(:lease_connection)
|
|
50
|
+
def self.lease_connection
|
|
51
|
+
connection
|
|
52
|
+
end
|
|
53
|
+
end
|
|
47
54
|
end
|
|
48
55
|
end
|
|
49
56
|
|
data/config/brakeman.ignore
CHANGED
|
@@ -1,5 +1,47 @@
|
|
|
1
1
|
{
|
|
2
2
|
"ignored_warnings": [
|
|
3
|
+
{
|
|
4
|
+
"warning_type": "SQL Injection",
|
|
5
|
+
"warning_code": 0,
|
|
6
|
+
"fingerprint": "e3b77f1540f622502491e00b2825b0d5cb6cb3962017fa3ef18d4d463799f3fd",
|
|
7
|
+
"check_name": "SQL",
|
|
8
|
+
"message": "Possible SQL injection",
|
|
9
|
+
"file": "app/models/concerns/good_job/advisory_lockable.rb",
|
|
10
|
+
"line": 89,
|
|
11
|
+
"link": "https://brakemanscanner.org/docs/warning_types/sql_injection/",
|
|
12
|
+
"code": "joins(\"LEFT JOIN pg_locks ON pg_locks.locktype = 'advisory'\\n AND pg_locks.objsubid = 1\\n AND pg_locks.classid = ...#{_quoted_table_name_string}...\\n\".squish)",
|
|
13
|
+
"render_path": null,
|
|
14
|
+
"location": {
|
|
15
|
+
"type": "method",
|
|
16
|
+
"class": "GoodJob",
|
|
17
|
+
"method": null
|
|
18
|
+
},
|
|
19
|
+
"user_input": "_quoted_table_name_string",
|
|
20
|
+
"confidence": "Medium",
|
|
21
|
+
"cwe_id": [89],
|
|
22
|
+
"note": "Values are quoted identifiers and an escaped table name string; not user input."
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"warning_type": "SQL Injection",
|
|
26
|
+
"warning_code": 0,
|
|
27
|
+
"fingerprint": "ffcbcbd3d170cb7e40cc3b7054e6157983b6ccdf7eab02bc1b0f525158b35248",
|
|
28
|
+
"check_name": "SQL",
|
|
29
|
+
"message": "Possible SQL injection",
|
|
30
|
+
"file": "app/models/concerns/good_job/advisory_lockable.rb",
|
|
31
|
+
"line": 89,
|
|
32
|
+
"link": "https://brakemanscanner.org/docs/warning_types/sql_injection/",
|
|
33
|
+
"code": "joins(\"LEFT JOIN pg_locks ON pg_locks.locktype = 'advisory'\\n AND pg_locks.objsubid = 1\\n AND pg_locks.classid = ...#{_quoted_table_name_string}...\\n\".squish)",
|
|
34
|
+
"render_path": null,
|
|
35
|
+
"location": {
|
|
36
|
+
"type": "method",
|
|
37
|
+
"class": "GoodJob::AdvisoryLockable",
|
|
38
|
+
"method": null
|
|
39
|
+
},
|
|
40
|
+
"user_input": "_quoted_table_name_string",
|
|
41
|
+
"confidence": "Medium",
|
|
42
|
+
"cwe_id": [89],
|
|
43
|
+
"note": "Values are quoted identifiers and an escaped table name string; not user input."
|
|
44
|
+
},
|
|
3
45
|
{
|
|
4
46
|
"warning_type": "Dynamic Render Path",
|
|
5
47
|
"warning_code": 15,
|
|
@@ -77,11 +77,11 @@ module GoodJob # :nodoc:
|
|
|
77
77
|
@record.advisory_lock!
|
|
78
78
|
@record.update(lock_type: :advisory)
|
|
79
79
|
end
|
|
80
|
-
@advisory_locked_connection = WeakRef.new(@record.class.
|
|
80
|
+
@advisory_locked_connection = WeakRef.new(@record.class.lease_connection)
|
|
81
81
|
end
|
|
82
82
|
else
|
|
83
83
|
@record = GoodJob::Process.find_or_create_record(id: @record_id, with_advisory_lock: true)
|
|
84
|
-
@advisory_locked_connection = WeakRef.new(@record.class.
|
|
84
|
+
@advisory_locked_connection = WeakRef.new(@record.class.lease_connection)
|
|
85
85
|
create_refresh_task
|
|
86
86
|
end
|
|
87
87
|
end
|
|
@@ -155,7 +155,8 @@ module GoodJob # :nodoc:
|
|
|
155
155
|
private
|
|
156
156
|
|
|
157
157
|
def advisory_locked_connection?
|
|
158
|
-
@record&.class&.
|
|
158
|
+
conn = @record&.class&.lease_connection
|
|
159
|
+
conn && @advisory_locked_connection&.weakref_alive? && @advisory_locked_connection.eql?(conn)
|
|
159
160
|
end
|
|
160
161
|
|
|
161
162
|
def task_interval
|
data/lib/good_job/notifier.rb
CHANGED
|
@@ -54,10 +54,11 @@ module GoodJob # :nodoc:
|
|
|
54
54
|
# Send a message via Postgres NOTIFY
|
|
55
55
|
# @param message [#to_json]
|
|
56
56
|
def self.notify(message)
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
57
|
+
::GoodJob::Job.connection_pool.with_connection do |connection|
|
|
58
|
+
connection.exec_query <<~SQL.squish
|
|
59
|
+
NOTIFY #{CHANNEL}, #{connection.quote(message.to_json)}
|
|
60
|
+
SQL
|
|
61
|
+
end
|
|
61
62
|
end
|
|
62
63
|
|
|
63
64
|
# List of recipients that will receive notifications.
|
|
@@ -17,6 +17,12 @@ module GoodJob # :nodoc:
|
|
|
17
17
|
_overridden_connection || super
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
+
# Overrides lease_connection to use the assigned connection when set.
|
|
21
|
+
# @return [ActiveRecord::ConnectionAdapters::AbstractAdapter]
|
|
22
|
+
def lease_connection
|
|
23
|
+
_overridden_connection || super
|
|
24
|
+
end
|
|
25
|
+
|
|
20
26
|
# Block interface to assign the connection, yield, then unassign the connection.
|
|
21
27
|
# @param conn [ActiveRecord::ConnectionAdapters::AbstractAdapter]
|
|
22
28
|
# @return [void]
|
data/lib/good_job/version.rb
CHANGED