good_job 1.10.0 → 1.11.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 96c16ec6a03de5eaf957327ad3f9161d19665eeef2e8bc300caa758a84cfe58f
4
- data.tar.gz: 22be95e4ae3d2dc29a8b242cb37889d21d2b19f119184a5a33d07f04e9a3b453
3
+ metadata.gz: d78c670393e4cddbedfe9512fdda95855709d830618cd63cfde7e724f26ce6c8
4
+ data.tar.gz: 494d3b0d6ec2b05d0bcc3386c14baca6071635647ed808a9b3543b5f1ebc4bfb
5
5
  SHA512:
6
- metadata.gz: a59d51fdaf7e08a551e6401bdc6fb59d56e8397bc15b7f6ead69ce5eb45da653c149ebad6583af739cdcb619c2d827fb19f50becf4e4b3780ef4d31dc9be4a50
7
- data.tar.gz: a9c3c96e76c19270b436d03fcff64e2e873cb6f14b784cbe8b2dd20e2deee455ce2ae6e1869a67b812f414880a7e264a50afd2951d80b41f4ddb9acdab8a0764
6
+ metadata.gz: bef7307febc465eab519c44a214287d5203bf0a024d83e46160dbbaf94b3a0fcd1317fbe99e446d9b0f01fb2bc8845a6dfe722fb123020aab96fe31b0c189d71
7
+ data.tar.gz: 737c03b9ad35169c9db43070abca6f644816876217fd333831db568f43eca6738dd9327eb7a403eb901ab6643e06445ba7764453d3f9b05e9e7c1159f7ba0963
data/CHANGELOG.md CHANGED
@@ -1,5 +1,60 @@
1
1
  # Changelog
2
2
 
3
+ ## [v1.11.2](https://github.com/bensheldon/good_job/tree/v1.11.2) (2021-07-20)
4
+
5
+ [Full Changelog](https://github.com/bensheldon/good_job/compare/v1.11.1...v1.11.2)
6
+
7
+ **Fixed bugs:**
8
+
9
+ - Notifier waits to retry listening when database is unavailable [\#301](https://github.com/bensheldon/good_job/pull/301) ([bensheldon](https://github.com/bensheldon))
10
+
11
+ **Closed issues:**
12
+
13
+ - Handle database connection drops [\#296](https://github.com/bensheldon/good_job/issues/296)
14
+ - Using the `async` worker results in `ActiveModel::UnknownAttributeError unknown attribute 'create_with_advisory_lock' for GoodJob::Job`. [\#290](https://github.com/bensheldon/good_job/issues/290)
15
+
16
+ **Merged pull requests:**
17
+
18
+ - Rename development and test databases to be `good_job` [\#300](https://github.com/bensheldon/good_job/pull/300) ([bensheldon](https://github.com/bensheldon))
19
+ - Move generators spec into top-level spec directory; update dependencies [\#299](https://github.com/bensheldon/good_job/pull/299) ([bensheldon](https://github.com/bensheldon))
20
+
21
+ ## [v1.11.1](https://github.com/bensheldon/good_job/tree/v1.11.1) (2021-07-07)
22
+
23
+ [Full Changelog](https://github.com/bensheldon/good_job/compare/v1.11.0...v1.11.1)
24
+
25
+ **Closed issues:**
26
+
27
+ - Database connection required while loading the code on 1.10.x [\#291](https://github.com/bensheldon/good_job/issues/291)
28
+
29
+ **Merged pull requests:**
30
+
31
+ - Defer accessing ActiveRecord `primary_key` in Lockable [\#293](https://github.com/bensheldon/good_job/pull/293) ([bensheldon](https://github.com/bensheldon))
32
+
33
+ ## [v1.11.0](https://github.com/bensheldon/good_job/tree/v1.11.0) (2021-07-07)
34
+
35
+ [Full Changelog](https://github.com/bensheldon/good_job/compare/v1.10.1...v1.11.0)
36
+
37
+ **Implemented enhancements:**
38
+
39
+ - Add concurrency extension for ActiveJob [\#281](https://github.com/bensheldon/good_job/pull/281) ([bensheldon](https://github.com/bensheldon))
40
+
41
+ **Closed issues:**
42
+
43
+ - Investigate GoodJob concurrency [\#289](https://github.com/bensheldon/good_job/issues/289)
44
+ - Problem with migrating database on 1.10.0 [\#287](https://github.com/bensheldon/good_job/issues/287)
45
+ - Support migration --database option for install task? [\#267](https://github.com/bensheldon/good_job/issues/267)
46
+ - Add GoodJob to Ruby Toolbox [\#243](https://github.com/bensheldon/good_job/issues/243)
47
+ - Custom advisory locks to prevent certain jobs from being worked on concurrently? [\#206](https://github.com/bensheldon/good_job/issues/206)
48
+
49
+ ## [v1.10.1](https://github.com/bensheldon/good_job/tree/v1.10.1) (2021-06-30)
50
+
51
+ [Full Changelog](https://github.com/bensheldon/good_job/compare/v1.10.0...v1.10.1)
52
+
53
+ **Merged pull requests:**
54
+
55
+ - Remove `FOR UPDATE SKIP LOCKED` from job locking sql statement [\#288](https://github.com/bensheldon/good_job/pull/288) ([bensheldon](https://github.com/bensheldon))
56
+ - Update GH Test Matrix with latest JRuby 9.2.19.0 [\#283](https://github.com/bensheldon/good_job/pull/283) ([tedhexaflow](https://github.com/tedhexaflow))
57
+
3
58
  ## [v1.10.0](https://github.com/bensheldon/good_job/tree/v1.10.0) (2021-06-29)
4
59
 
5
60
  [Full Changelog](https://github.com/bensheldon/good_job/compare/v1.9.6...v1.10.0)
data/README.md CHANGED
@@ -38,6 +38,7 @@ For more of the story of GoodJob, read the [introductory blog post](https://isla
38
38
  - [Configuration options](#configuration-options)
39
39
  - [Global options](#global-options)
40
40
  - [Dashboard](#dashboard)
41
+ - [ActiveJob Concurrency](#activejob-concurrency)
41
42
  - [Updating](#updating)
42
43
  - [Go deeper](#go-deeper)
43
44
  - [Exceptions, retries, and reliability](#exceptions-retries-and-reliability)
@@ -319,6 +320,35 @@ GoodJob includes a Dashboard as a mountable `Rails::Engine`.
319
320
  end
320
321
  ```
321
322
 
323
+ ### ActiveJob Concurrency
324
+
325
+ GoodJob can extend ActiveJob to provide limits on concurrently running jobs, either at time of _enqueue_ or at _perform_.
326
+
327
+ **Note:** Limiting concurrency at _enqueue_ requires Rails 6.0+ because Rails 5.2 does not support `throw :abort` in ActiveJob callbacks.
328
+
329
+ ```ruby
330
+ class MyJob < ApplicationJob
331
+ include GoodJob::ActiveJobExtensions::Concurrency
332
+
333
+ good_job_control_concurrency_with(
334
+ # Maximum number of jobs with the concurrency key to be concurrently enqueued
335
+ enqueue_limit: 2,
336
+
337
+ # Maximum number of jobs with the concurrency key to be concurrently performed
338
+ perform_limit: 1,
339
+
340
+ # A unique key to be globally locked against.
341
+ # Can be String or Lambda/Proc that is invoked in the context of the job.
342
+ # Note: Arguments passed to #perform_later must be accessed through `arguments` method.
343
+ key: -> { "Unique-#{arguments.first}" } # MyJob.perform_later("Alice") => "Unique-Alice"
344
+ )
345
+
346
+ def perform(first_name)
347
+ # do work
348
+ end
349
+ end
350
+ ```
351
+
322
352
  ### Updating
323
353
 
324
354
  GoodJob follows semantic versioning, though updates may be encouraged through deprecation warnings in minor versions.
@@ -0,0 +1,4 @@
1
+ module GoodJob
2
+ module ActiveJobExtensions
3
+ end
4
+ end
@@ -0,0 +1,68 @@
1
+ module GoodJob
2
+ module ActiveJobExtensions
3
+ module Concurrency
4
+ extend ActiveSupport::Concern
5
+
6
+ ConcurrencyExceededError = Class.new(StandardError)
7
+
8
+ included do
9
+ class_attribute :good_job_concurrency_config, instance_accessor: false, default: {}
10
+
11
+ before_enqueue do |job|
12
+ # Always allow jobs to be retried because the current job's execution will complete momentarily
13
+ next if CurrentExecution.active_job_id == job.job_id
14
+
15
+ limit = job.class.good_job_concurrency_config.fetch(:enqueue_limit, Float::INFINITY)
16
+ next if limit.blank? || (0...Float::INFINITY).exclude?(limit)
17
+
18
+ key = job.good_job_concurrency_key
19
+ next if key.blank?
20
+
21
+ GoodJob::Job.new.with_advisory_lock(key: key, function: "pg_advisory_lock") do
22
+ # TODO: Why is `unscoped` necessary? Nested scope is bleeding into subsequent query?
23
+ enqueue_concurrency = GoodJob::Job.unscoped.where(concurrency_key: key).unfinished.count
24
+ # The job has not yet been enqueued, so check if adding it will go over the limit
25
+ throw :abort if enqueue_concurrency + 1 > limit
26
+ end
27
+ end
28
+
29
+ retry_on(
30
+ GoodJob::ActiveJobExtensions::Concurrency::ConcurrencyExceededError,
31
+ attempts: Float::INFINITY,
32
+ wait: :exponentially_longer
33
+ )
34
+
35
+ before_perform do |job|
36
+ limit = job.class.good_job_concurrency_config.fetch(:perform_limit, Float::INFINITY)
37
+ next if limit.blank? || (0...Float::INFINITY).exclude?(limit)
38
+
39
+ key = job.good_job_concurrency_key
40
+ next if key.blank?
41
+
42
+ GoodJob::Job.new.with_advisory_lock(key: key, function: "pg_advisory_lock") do
43
+ perform_concurrency = GoodJob::Job.unscoped.where(concurrency_key: key).advisory_locked.count
44
+ # The current job has already been locked and will appear in the previous query
45
+ raise GoodJob::ActiveJobExtensions::Concurrency::ConcurrencyExceededError if perform_concurrency > limit
46
+ end
47
+ end
48
+ end
49
+
50
+ class_methods do
51
+ def good_job_control_concurrency_with(config)
52
+ self.good_job_concurrency_config = config
53
+ end
54
+ end
55
+
56
+ def good_job_concurrency_key
57
+ key = self.class.good_job_concurrency_config[:key]
58
+ return if key.blank?
59
+
60
+ if key.respond_to? :call
61
+ instance_exec(&key)
62
+ else
63
+ key
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -4,11 +4,11 @@ module GoodJob
4
4
  # Thread-local attributes for passing values from Instrumentation.
5
5
  # (Cannot use ActiveSupport::CurrentAttributes because ActiveJob resets it)
6
6
  module CurrentExecution
7
- # @!attribute [rw] error_on_retry
7
+ # @!attribute [rw] active_job_id
8
8
  # @!scope class
9
- # Error captured by retry_on
10
- # @return [Exception, nil]
11
- thread_mattr_accessor :error_on_retry
9
+ # ActiveJob ID
10
+ # @return [String, nil]
11
+ thread_mattr_accessor :active_job_id
12
12
 
13
13
  # @!attribute [rw] error_on_discard
14
14
  # @!scope class
@@ -16,11 +16,18 @@ module GoodJob
16
16
  # @return [Exception, nil]
17
17
  thread_mattr_accessor :error_on_discard
18
18
 
19
+ # @!attribute [rw] error_on_retry
20
+ # @!scope class
21
+ # Error captured by retry_on
22
+ # @return [Exception, nil]
23
+ thread_mattr_accessor :error_on_retry
24
+
19
25
  # Resets attributes
20
26
  # @return [void]
21
27
  def self.reset
22
- self.error_on_retry = nil
28
+ self.active_job_id = nil
23
29
  self.error_on_discard = nil
30
+ self.error_on_retry = nil
24
31
  end
25
32
 
26
33
  # @return [Integer] Current process ID
data/lib/good_job/job.rb CHANGED
@@ -15,6 +15,7 @@ module GoodJob
15
15
  DEFAULT_PRIORITY = 0
16
16
 
17
17
  self.table_name = 'good_jobs'.freeze
18
+ self.advisory_lockable_column = 'id'.freeze
18
19
 
19
20
  attr_readonly :serialized_params
20
21
 
@@ -219,6 +220,21 @@ module GoodJob
219
220
  DEPRECATION
220
221
  end
221
222
 
223
+ if column_names.include?('concurrency_key')
224
+ good_job_args[:concurrency_key] = active_job.good_job_concurrency_key if active_job.respond_to?(:good_job_concurrency_key)
225
+ else
226
+ ActiveSupport::Deprecation.warn(<<~DEPRECATION)
227
+ GoodJob has pending database migrations. To create the migration files, run:
228
+
229
+ rails generate good_job:update
230
+
231
+ To apply the migration files, run:
232
+
233
+ rails db:migrate
234
+
235
+ DEPRECATION
236
+ end
237
+
222
238
  good_job = GoodJob::Job.new(**good_job_args)
223
239
 
224
240
  instrument_payload[:good_job] = good_job
@@ -264,6 +280,10 @@ module GoodJob
264
280
  self.class.unscoped.unfinished.owns_advisory_locked.exists?(id: id)
265
281
  end
266
282
 
283
+ def active_job_id
284
+ super || serialized_params['job_id']
285
+ end
286
+
267
287
  private
268
288
 
269
289
  # @return [ExecutionResult]
@@ -273,6 +293,7 @@ module GoodJob
273
293
  )
274
294
 
275
295
  GoodJob::CurrentExecution.reset
296
+ GoodJob::CurrentExecution.active_job_id = active_job_id
276
297
  ActiveSupport::Notifications.instrument("perform_job.good_job", { good_job: self, process_id: GoodJob::CurrentExecution.process_id, thread_name: GoodJob::CurrentExecution.thread_name }) do
277
298
  value = ActiveJob::Base.execute(params)
278
299
 
@@ -23,20 +23,20 @@ module GoodJob
23
23
 
24
24
  included do
25
25
  # Default column to be used when creating Advisory Locks
26
- cattr_accessor(:advisory_lockable_column, instance_accessor: false) { primary_key }
26
+ class_attribute :advisory_lockable_column, instance_accessor: false, default: Concurrent::Delay.new { primary_key }
27
27
 
28
28
  # Default Postgres function to be used for Advisory Locks
29
- cattr_accessor(:advisory_lockable_function) { "pg_try_advisory_lock" }
29
+ class_attribute :advisory_lockable_function, default: "pg_try_advisory_lock"
30
30
 
31
31
  # Attempt to acquire an advisory lock on the selected records and
32
32
  # return only those records for which a lock could be acquired.
33
- # @!method advisory_lock(column: advisory_lockable_column, function: advisory_lockable_function)
33
+ # @!method advisory_lock(column: _advisory_lockable_column, function: advisory_lockable_function)
34
34
  # @!scope class
35
35
  # @param column [String, Symbol] column values to Advisory Lock against
36
36
  # @param function [String, Symbol] Postgres Advisory Lock function name to use
37
37
  # @return [ActiveRecord::Relation]
38
38
  # A relation selecting only the records that were locked.
39
- scope :advisory_lock, (lambda do |column: advisory_lockable_column, function: advisory_lockable_function|
39
+ scope :advisory_lock, (lambda do |column: _advisory_lockable_column, function: advisory_lockable_function|
40
40
  original_query = self
41
41
 
42
42
  cte_table = Arel::Table.new(:rows)
@@ -48,15 +48,9 @@ module GoodJob
48
48
  end
49
49
 
50
50
  composed_cte = Arel::Nodes::As.new(cte_table, Arel::Nodes::SqlLiteral.new([cte_type, "(", cte_query.to_sql, ")"].join(' ')))
51
-
52
- # In addition to an advisory lock, there is also a FOR UPDATE SKIP LOCKED
53
- # because this causes the query to skip jobs that were completed (and deleted)
54
- # by another session in the time since the table snapshot was taken.
55
- # In rare cases under high concurrency levels, leaving this out can result in double executions.
56
51
  query = cte_table.project(cte_table[:id])
57
52
  .with(composed_cte)
58
53
  .where(Arel.sql(sanitize_sql_for_conditions(["#{function}(('x' || substr(md5(:table_name || #{connection.quote_table_name(cte_table.name)}.#{connection.quote_column_name(column)}::text), 1, 16))::bit(64)::bigint)", { table_name: table_name }])))
59
- .lock(Arel.sql("FOR UPDATE SKIP LOCKED"))
60
54
 
61
55
  limit = original_query.arel.ast.limit
62
56
  query.limit = limit.value if limit.present?
@@ -70,13 +64,13 @@ module GoodJob
70
64
  #
71
65
  # For details on +pg_locks+, see
72
66
  # {https://www.postgresql.org/docs/current/view-pg-locks.html}.
73
- # @!method joins_advisory_locks(column: advisory_lockable_column)
67
+ # @!method joins_advisory_locks(column: _advisory_lockable_column)
74
68
  # @!scope class
75
69
  # @param column [String, Symbol] column values to Advisory Lock against
76
70
  # @return [ActiveRecord::Relation]
77
71
  # @example Get the records that have a session awaiting a lock:
78
72
  # MyLockableRecord.joins_advisory_locks.where("pg_locks.granted = ?", false)
79
- scope :joins_advisory_locks, (lambda do |column: advisory_lockable_column|
73
+ scope :joins_advisory_locks, (lambda do |column: _advisory_lockable_column|
80
74
  join_sql = <<~SQL.squish
81
75
  LEFT JOIN pg_locks ON pg_locks.locktype = 'advisory'
82
76
  AND pg_locks.objsubid = 1
@@ -88,26 +82,26 @@ module GoodJob
88
82
  end)
89
83
 
90
84
  # Find records that do not have an advisory lock on them.
91
- # @!method advisory_unlocked(column: advisory_lockable_column)
85
+ # @!method advisory_unlocked(column: _advisory_lockable_column)
92
86
  # @!scope class
93
87
  # @param column [String, Symbol] column values to Advisory Lock against
94
88
  # @return [ActiveRecord::Relation]
95
- scope :advisory_unlocked, ->(column: advisory_lockable_column) { joins_advisory_locks(column: column).where(pg_locks: { locktype: nil }) }
89
+ scope :advisory_unlocked, ->(column: _advisory_lockable_column) { joins_advisory_locks(column: column).where(pg_locks: { locktype: nil }) }
96
90
 
97
91
  # Find records that have an advisory lock on them.
98
- # @!method advisory_locked(column: advisory_lockable_column)
92
+ # @!method advisory_locked(column: _advisory_lockable_column)
99
93
  # @!scope class
100
94
  # @param column [String, Symbol] column values to Advisory Lock against
101
95
  # @return [ActiveRecord::Relation]
102
- scope :advisory_locked, ->(column: advisory_lockable_column) { joins_advisory_locks(column: column).where.not(pg_locks: { locktype: nil }) }
96
+ scope :advisory_locked, ->(column: _advisory_lockable_column) { joins_advisory_locks(column: column).where.not(pg_locks: { locktype: nil }) }
103
97
 
104
98
  # Find records with advisory locks owned by the current Postgres
105
99
  # session/connection.
106
- # @!method advisory_locked(column: advisory_lockable_column)
100
+ # @!method advisory_locked(column: _advisory_lockable_column)
107
101
  # @!scope class
108
102
  # @param column [String, Symbol] column values to Advisory Lock against
109
103
  # @return [ActiveRecord::Relation]
110
- scope :owns_advisory_locked, ->(column: advisory_lockable_column) { joins_advisory_locks(column: column).where('"pg_locks"."pid" = pg_backend_pid()') }
104
+ scope :owns_advisory_locked, ->(column: _advisory_lockable_column) { joins_advisory_locks(column: column).where('"pg_locks"."pid" = pg_backend_pid()') }
111
105
 
112
106
  # Whether an advisory lock should be acquired in the same transaction
113
107
  # that created the record.
@@ -149,7 +143,7 @@ module GoodJob
149
143
  # MyLockableRecord.order(created_at: :asc).limit(2).with_advisory_lock do |record|
150
144
  # do_something_with record
151
145
  # end
152
- def with_advisory_lock(column: advisory_lockable_column, function: advisory_lockable_function, unlock_session: false)
146
+ def with_advisory_lock(column: _advisory_lockable_column, function: advisory_lockable_function, unlock_session: false)
153
147
  raise ArgumentError, "Must provide a block" unless block_given?
154
148
 
155
149
  records = advisory_lock(column: column, function: function).to_a
@@ -160,13 +154,19 @@ module GoodJob
160
154
  advisory_unlock_session
161
155
  else
162
156
  records.each do |record|
163
- key = [table_name, record[advisory_lockable_column]].join
157
+ key = [table_name, record[_advisory_lockable_column]].join
164
158
  record.advisory_unlock(key: key, function: advisory_unlockable_function(function))
165
159
  end
166
160
  end
167
161
  end
168
162
  end
169
163
 
164
+ # Allow advisory_lockable_column to be a `Concurrent::Delay`
165
+ def _advisory_lockable_column
166
+ column = advisory_lockable_column
167
+ column.respond_to?(:value) ? column.value : column
168
+ end
169
+
170
170
  def supports_cte_materialization_specifiers?
171
171
  return @_supports_cte_materialization_specifiers if defined?(@_supports_cte_materialization_specifiers)
172
172
 
@@ -207,9 +207,16 @@ module GoodJob
207
207
  # @param function [String, Symbol] Postgres Advisory Lock function name to use
208
208
  # @return [Boolean] whether the lock was acquired.
209
209
  def advisory_lock(key: lockable_key, function: advisory_lockable_function)
210
- query = <<~SQL.squish
211
- SELECT #{function}(('x'||substr(md5($1::text), 1, 16))::bit(64)::bigint) AS locked
212
- SQL
210
+ query = if function.include? "_try_"
211
+ <<~SQL.squish
212
+ SELECT #{function}(('x'||substr(md5($1::text), 1, 16))::bit(64)::bigint) AS locked
213
+ SQL
214
+ else
215
+ <<~SQL.squish
216
+ SELECT #{function}(('x'||substr(md5($1::text), 1, 16))::bit(64)::bigint)::text AS locked
217
+ SQL
218
+ end
219
+
213
220
  binds = [[nil, key]]
214
221
  self.class.connection.exec_query(pg_or_jdbc_query(query), 'GoodJob::Lockable Advisory Lock', binds).first['locked']
215
222
  end
@@ -307,7 +314,7 @@ module GoodJob
307
314
  # Default Advisory Lock key
308
315
  # @return [String]
309
316
  def lockable_key
310
- [self.class.table_name, self[self.class.advisory_lockable_column]].join
317
+ [self.class.table_name, self[self.class._advisory_lockable_column]].join
311
318
  end
312
319
 
313
320
  delegate :pg_or_jdbc_query, to: :class
@@ -24,6 +24,8 @@ module GoodJob # :nodoc:
24
24
  max_queue: 1,
25
25
  fallback_policy: :discard,
26
26
  }.freeze
27
+ # Seconds to wait if database cannot be connected to
28
+ RECONNECT_INTERVAL = 5
27
29
  # Seconds to block while LISTENing for a message
28
30
  WAIT_INTERVAL = 1
29
31
 
@@ -114,7 +116,13 @@ module GoodJob # :nodoc:
114
116
  ActiveSupport::Notifications.instrument("notifier_notify_error.good_job", { error: thread_error })
115
117
  end
116
118
 
117
- listen unless shutdown?
119
+ return if shutdown?
120
+
121
+ if thread_error.is_a?(ActiveRecord::ConnectionNotEstablished) || thread_error.is_a?(ActiveRecord::StatementInvalid)
122
+ listen(delay: RECONNECT_INTERVAL)
123
+ else
124
+ listen
125
+ end
118
126
  end
119
127
 
120
128
  private
@@ -125,8 +133,8 @@ module GoodJob # :nodoc:
125
133
  @executor = Concurrent::ThreadPoolExecutor.new(EXECUTOR_OPTIONS)
126
134
  end
127
135
 
128
- def listen
129
- future = Concurrent::Future.new(args: [@recipients, executor, @listening], executor: @executor) do |thr_recipients, thr_executor, thr_listening|
136
+ def listen(delay: 0)
137
+ future = Concurrent::ScheduledTask.new(delay, args: [@recipients, executor, @listening], executor: @executor) do |thr_recipients, thr_executor, thr_listening|
130
138
  with_listen_connection do |conn|
131
139
  ActiveSupport::Notifications.instrument("notifier_listen.good_job") do
132
140
  conn.async_exec("LISTEN #{CHANNEL}").clear
@@ -1,4 +1,4 @@
1
1
  module GoodJob
2
2
  # GoodJob gem version.
3
- VERSION = '1.10.0'.freeze
3
+ VERSION = '1.11.2'.freeze
4
4
  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: 1.10.0
4
+ version: 1.11.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Sheldon
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-06-29 00:00:00.000000000 Z
11
+ date: 2021-07-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activejob
@@ -362,6 +362,8 @@ files:
362
362
  - lib/generators/good_job/templates/update/migrations/03_add_active_job_id_index_and_concurrency_key_index_to_good_jobs.rb
363
363
  - lib/generators/good_job/update_generator.rb
364
364
  - lib/good_job.rb
365
+ - lib/good_job/active_job_extensions.rb
366
+ - lib/good_job/active_job_extensions/concurrency.rb
365
367
  - lib/good_job/adapter.rb
366
368
  - lib/good_job/cli.rb
367
369
  - lib/good_job/configuration.rb