good_job 4.2.1 → 4.3.0

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: f21cb4322491fa83f330857033b5c1c0524509d57a93d795d3e4c7ce064e676c
4
- data.tar.gz: '05082cbbd0fd4a26587ac53a58d7042c546390505e68fb978ef675bdfe6a0fa6'
3
+ metadata.gz: ec5d4c89f52824a8023774ca2179f67579c21ae7772737a367ee912bc58db9bd
4
+ data.tar.gz: f8526687220d9bd8ec7222f897f2302ab7d42f8883627c43195390b7ab9cf0be
5
5
  SHA512:
6
- metadata.gz: 825bed536a7ee957773981cc926f04dad400e0f2a770e5749a18511927b3c09a4130370a5ee0d88e984bf8ed3a09cce53bd0285bc1066c1e4a837835ee44ab21
7
- data.tar.gz: 04371b7bb7fb075dc5d11817ff942443609bfd33cd7112ba35c9617447ae520035d7fcfafff127e170b77dba4d258af5ebc54448c9d20c07cd1516d584076fdb
6
+ metadata.gz: 3760a509091f29ca67d90366b06940cb4b12428bbe99a5fd451315ec9895291d2083168603f1f81a3b87ccf1a6df9bcf657d9c89a811768436fdd6a80aa60737
7
+ data.tar.gz: 9ca52b976c7399f386e141c7ea6d54bfbf3b1b8036ef6f1ac37cc24dede46966ab7644c1c8a5bcde491c3a768862fd1edbd66b7054e06a0e6955e5f7b3d8245f
data/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # Changelog
2
2
 
3
+ ## [v4.3.0](https://github.com/bensheldon/good_job/tree/v4.3.0) (2024-09-14)
4
+
5
+ [Full Changelog](https://github.com/bensheldon/good_job/compare/v4.2.1...v4.3.0)
6
+
7
+ **Fixed bugs:**
8
+
9
+ - Don't shadow gem loading errors during autoloading with the inline adapter [\#1486](https://github.com/bensheldon/good_job/pull/1486) ([Earlopain](https://github.com/Earlopain))
10
+ - Ensure before actions run on root route [\#1482](https://github.com/bensheldon/good_job/pull/1482) ([ebiven](https://github.com/ebiven))
11
+
12
+ **Closed issues:**
13
+
14
+ - Undefined method `unhandled_error` for nil [\#1485](https://github.com/bensheldon/good_job/issues/1485)
15
+ - bin/rails g good\_job:install fails [\#1481](https://github.com/bensheldon/good_job/issues/1481)
16
+ - Rails 7.2.1 breaks enqueuing without enqueue\_after\_transaction\_commit? method on queue adapter [\#1477](https://github.com/bensheldon/good_job/issues/1477)
17
+
18
+ **Merged pull requests:**
19
+
20
+ - Fix documentation for 2nd Batch callback parameter: consistently call it `context` [\#1476](https://github.com/bensheldon/good_job/pull/1476) ([martijnversluis](https://github.com/martijnversluis))
21
+ - Redefine `Batch#finished_at` to mean all callback jobs have finished too; add `Batch#jobs_finished_at` to allow not deleting batches until all their callback jobs complete [\#1454](https://github.com/bensheldon/good_job/pull/1454) ([bensheldon](https://github.com/bensheldon))
22
+
3
23
  ## [v4.2.1](https://github.com/bensheldon/good_job/tree/v4.2.1) (2024-08-29)
4
24
 
5
25
  [Full Changelog](https://github.com/bensheldon/good_job/compare/v4.2.0...v4.2.1)
data/README.md CHANGED
@@ -681,15 +681,15 @@ Batches track a set of jobs, and enqueue an optional callback job when all of th
681
681
  OtherJob.perform_later
682
682
  end
683
683
 
684
- # When these jobs have finished, it will enqueue your `MyBatchCallbackJob.perform_later(batch, options)`
684
+ # When these jobs have finished, it will enqueue your `MyBatchCallbackJob.perform_later(batch, context)`
685
685
  class MyBatchCallbackJob < ApplicationJob
686
- # Callback jobs must accept a `batch` and `options` argument
687
- def perform(batch, params)
686
+ # Callback jobs must accept a `batch` and `context` argument
687
+ def perform(batch, context)
688
688
  # The batch object will contain the Batch's properties, which are mutable
689
689
  batch.properties[:user] # => <User id: 1, ...>
690
690
 
691
- # Params is a hash containing additional context (more may be added in the future)
692
- params[:event] # => :finish, :success, :discard
691
+ # Context is a hash containing additional context (more may be added in the future)
692
+ context[:event] # => :finish, :success, :discard
693
693
  end
694
694
  end
695
695
  ```
@@ -757,19 +757,19 @@ Batch callbacks are Active Job jobs that are enqueued at certain events during t
757
757
  - `:success` - Enqueued only when all jobs in the batch have finished and succeeded.
758
758
  - `:discard` - Enqueued immediately the first time a job in the batch is discarded.
759
759
 
760
- Callback jobs must accept a `batch` and `params` argument in their `perform` method:
760
+ Callback jobs must accept a `batch` and `context` argument in their `perform` method:
761
761
 
762
762
  ```ruby
763
763
  class MyBatchCallbackJob < ApplicationJob
764
- def perform(batch, params)
764
+ def perform(batch, context)
765
765
  # The batch object will contain the Batch's properties
766
766
  batch.properties[:user] # => <User id: 1, ...>
767
767
  # Batches are mutable
768
768
  batch.properties[:user] = User.find(2)
769
769
  batch.save
770
770
 
771
- # Params is a hash containing additional context (more may be added in the future)
772
- params[:event] # => :finish, :success, :discard
771
+ # Context is a hash containing additional context (more may be added in the future)
772
+ context[:event] # => :finish, :success, :discard
773
773
  end
774
774
  end
775
775
  ```
@@ -811,7 +811,7 @@ class WorkJob < ApplicationJob
811
811
  end
812
812
 
813
813
  class BatchJob < ApplicationJob
814
- def perform(batch, options)
814
+ def perform(batch, context)
815
815
  if batch.properties[:stage].nil?
816
816
  batch.enqueue(stage: 1) do
817
817
  WorkJob.perform_later('a')
@@ -94,6 +94,12 @@ module GoodJob
94
94
  redirect_to jobs_path, notice: t(".notice")
95
95
  end
96
96
 
97
+ def redirect_to_index
98
+ # Redirect to the jobs page, maintaining query parameters. This is
99
+ # necessary to support the `?poll=1` parameter that enables live polling.
100
+ redirect_to jobs_path(request.query_parameters)
101
+ end
102
+
97
103
  private
98
104
 
99
105
  def redirect_on_error(exception)
@@ -26,10 +26,12 @@ module GoodJob
26
26
  :enqueued_at,
27
27
  :finished_at,
28
28
  :discarded_at,
29
+ :jobs_finished_at,
29
30
  :enqueued?,
30
31
  :finished?,
31
32
  :succeeded?,
32
33
  :discarded?,
34
+ :jobs_finished?,
33
35
  :description,
34
36
  :description=,
35
37
  :on_finish,
@@ -95,8 +97,12 @@ module GoodJob
95
97
  record.transaction do
96
98
  record.with_advisory_lock(function: "pg_advisory_xact_lock") do
97
99
  record.enqueued_at_will_change!
100
+ record.jobs_finished_at_will_change! if GoodJob::BatchRecord.jobs_finished_at_migrated?
98
101
  record.finished_at_will_change!
99
- record.update!(enqueued_at: nil, finished_at: nil)
102
+
103
+ update_attributes = { discarded_at: nil, finished_at: nil }
104
+ update_attributes[:jobs_finished_at] = nil if GoodJob::BatchRecord.jobs_finished_at_migrated?
105
+ record.update!(**update_attributes)
100
106
  end
101
107
  end
102
108
  end
@@ -142,7 +148,9 @@ module GoodJob
142
148
  buffer = GoodJob::Adapter::InlineBuffer.capture do
143
149
  record.transaction do
144
150
  record.with_advisory_lock(function: "pg_advisory_xact_lock") do
145
- record.update!(discarded_at: nil, finished_at: nil)
151
+ update_attributes = { discarded_at: nil, finished_at: nil }
152
+ update_attributes[:jobs_finished_at] = nil if GoodJob::BatchRecord.jobs_finished_at_migrated?
153
+ record.update!(update_attributes)
146
154
  record.jobs.discarded.each(&:retry_job)
147
155
  record._continue_discard_or_finish(lock: false)
148
156
  end
@@ -38,6 +38,10 @@ module GoodJob
38
38
  query
39
39
  end)
40
40
 
41
+ def self.jobs_finished_at_migrated?
42
+ column_names.include?('jobs_finished_at')
43
+ end
44
+
41
45
  # Whether the batch has finished and no jobs were discarded
42
46
  # @return [Boolean]
43
47
  def succeeded?
@@ -52,22 +56,26 @@ module GoodJob
52
56
  attributes.except('serialized_properties').merge(properties: properties)
53
57
  end
54
58
 
55
- def _continue_discard_or_finish(execution = nil, lock: true)
56
- execution_discarded = execution && execution.finished_at.present? && execution.error.present?
59
+ def _continue_discard_or_finish(job = nil, lock: true)
60
+ job_discarded = job && job.finished_at.present? && job.error.present?
57
61
  buffer = GoodJob::Adapter::InlineBuffer.capture do
58
62
  advisory_lock_maybe(lock) do
59
63
  Batch.within_thread(batch_id: nil, batch_callback_id: id) do
60
64
  reload
61
- if execution_discarded && !discarded_at
65
+
66
+ if job_discarded && !discarded_at
62
67
  update(discarded_at: Time.current)
63
68
  on_discard.constantize.set(priority: callback_priority, queue: callback_queue_name).perform_later(to_batch, { event: :discard }) if on_discard.present?
64
69
  end
65
70
 
66
- if enqueued_at && !finished_at && jobs.where(finished_at: nil).count.zero?
67
- update(finished_at: Time.current)
71
+ if enqueued_at && !(self.class.jobs_finished_at_migrated? ? jobs_finished_at : finished_at) && jobs.where(finished_at: nil).count.zero?
72
+ self.class.jobs_finished_at_migrated? ? update(jobs_finished_at: Time.current) : update(finished_at: Time.current)
73
+
68
74
  on_success.constantize.set(priority: callback_priority, queue: callback_queue_name).perform_later(to_batch, { event: :success }) if !discarded_at && on_success.present?
69
75
  on_finish.constantize.set(priority: callback_priority, queue: callback_queue_name).perform_later(to_batch, { event: :finish }) if on_finish.present?
70
76
  end
77
+
78
+ update(finished_at: Time.current) if !finished_at && self.class.jobs_finished_at_migrated? && jobs_finished? && callback_jobs.where(finished_at: nil).count.zero?
71
79
  end
72
80
  end
73
81
  end
@@ -97,6 +105,14 @@ module GoodJob
97
105
  self.serialized_properties = value
98
106
  end
99
107
 
108
+ def jobs_finished?
109
+ self.class.jobs_finished_at_migrated? ? jobs_finished_at : finished_at
110
+ end
111
+
112
+ def jobs_finished_at
113
+ self.class.jobs_finished_at_migrated? ? self[:jobs_finished_at] : self[:finished_at]
114
+ end
115
+
100
116
  private
101
117
 
102
118
  def advisory_lock_maybe(value, &block)
@@ -40,6 +40,7 @@ module GoodJob
40
40
  set_callback :perform_unlocked, :after, :continue_discard_or_finish_batch
41
41
 
42
42
  belongs_to :batch, class_name: 'GoodJob::BatchRecord', inverse_of: :jobs, optional: true
43
+ belongs_to :callback_batch, class_name: 'GoodJob::BatchRecord', foreign_key: :batch_callback_id, inverse_of: :callback_jobs, optional: true
43
44
  belongs_to :locked_by_process, class_name: "GoodJob::Process", foreign_key: :locked_by_id, inverse_of: :locked_jobs, optional: true
44
45
  has_many :executions, class_name: 'GoodJob::Execution', foreign_key: 'active_job_id', primary_key: "id", inverse_of: :job, dependent: :delete_all
45
46
 
@@ -743,6 +744,7 @@ module GoodJob
743
744
 
744
745
  def continue_discard_or_finish_batch
745
746
  batch._continue_discard_or_finish(self) if batch.present?
747
+ callback_batch._continue_discard_or_finish if callback_batch.present?
746
748
  end
747
749
 
748
750
  def active_job_data
data/config/routes.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  GoodJob::Engine.routes.draw do
4
- root to: redirect(path: 'jobs')
4
+ root 'jobs#redirect_to_index'
5
5
 
6
6
  resources :jobs, only: %i[index show destroy] do
7
7
  collection do
@@ -46,6 +46,7 @@ class CreateGoodJobs < ActiveRecord::Migration<%= migration_version %>
46
46
  t.datetime :enqueued_at
47
47
  t.datetime :discarded_at
48
48
  t.datetime :finished_at
49
+ t.datetime :jobs_finished_at
49
50
  end
50
51
 
51
52
  create_table :good_job_executions, id: :uuid do |t|
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddJobsFinishedAtToGoodJobBatches < ActiveRecord::Migration<%= migration_version %>
4
+ def change
5
+ reversible do |dir|
6
+ dir.up do
7
+ # Ensure this incremental update migration is idempotent
8
+ # with monolithic install migration.
9
+ return if connection.column_exists?(:good_job_batches, :jobs_finished_at)
10
+ end
11
+ end
12
+
13
+ change_table :good_job_batches do |t|
14
+ t.datetime :jobs_finished_at
15
+ end
16
+ end
17
+ end
@@ -254,7 +254,7 @@ module GoodJob
254
254
  job.advisory_unlock
255
255
  job.run_callbacks(:perform_unlocked)
256
256
 
257
- raise result.unhandled_error if result.unhandled_error
257
+ raise result.unhandled_error if result&.unhandled_error
258
258
  end
259
259
  end
260
260
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module GoodJob
4
4
  # GoodJob gem version.
5
- VERSION = '4.2.1'
5
+ VERSION = '4.3.0'
6
6
 
7
7
  # GoodJob version as Gem::Version object
8
8
  GEM_VERSION = Gem::Version.new(VERSION)
data/lib/good_job.rb CHANGED
@@ -171,10 +171,10 @@ module GoodJob
171
171
  _shutdown_all(Capsule.instances, :restart, timeout: timeout)
172
172
  end
173
173
 
174
- # Sends +#shutdown+ or +#restart+ to executable objects ({GoodJob::Notifier}, {GoodJob::Poller}, {GoodJob::Scheduler}, {GoodJob::MultiScheduler}, {GoodJob::CronManager})
174
+ # Sends +#shutdown+ or +#restart+ to executable objects ({GoodJob::Notifier}, {GoodJob::Poller}, {GoodJob::Scheduler}, {GoodJob::MultiScheduler}, {GoodJob::CronManager}, {GoodJob::SharedExecutor})
175
175
  # @param executables [Array<Notifier, Poller, Scheduler, MultiScheduler, CronManager, SharedExecutor>] Objects to shut down.
176
- # @param method_name [:symbol] Method to call, e.g. +:shutdown+ or +:restart+.
177
- # @param timeout [nil,Numeric]
176
+ # @param method_name [Symbol] Method to call, e.g. +:shutdown+ or +:restart+.
177
+ # @param timeout [nil, Numeric] Seconds to wait for actively executing jobs to finish.
178
178
  # @param after [Array<Notifier, Poller, Scheduler, MultiScheduler, CronManager, SharedExecutor>] Objects to shut down after initial executables shut down.
179
179
  # @return [void]
180
180
  def self._shutdown_all(executables, method_name = :shutdown, timeout: -1, after: [])
@@ -290,7 +290,7 @@ module GoodJob
290
290
  # For use in tests/CI to validate GoodJob is up-to-date.
291
291
  # @return [Boolean]
292
292
  def self.migrated?
293
- true
293
+ GoodJob::BatchRecord.jobs_finished_at_migrated?
294
294
  end
295
295
  end
296
296
 
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: 4.2.1
4
+ version: 4.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Sheldon
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-08-29 00:00:00.000000000 Z
11
+ date: 2024-09-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activejob
@@ -340,6 +340,7 @@ files:
340
340
  - lib/generators/good_job/install_generator.rb
341
341
  - lib/generators/good_job/templates/install/migrations/create_good_jobs.rb.erb
342
342
  - lib/generators/good_job/templates/update/migrations/01_create_good_jobs.rb.erb
343
+ - lib/generators/good_job/templates/update/migrations/02_add_jobs_finished_at_to_good_job_batches.rb.erb
343
344
  - lib/generators/good_job/update_generator.rb
344
345
  - lib/good_job.rb
345
346
  - lib/good_job/active_job_extensions/batches.rb