good_job 4.2.1 → 4.3.0
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 +10 -10
- data/app/controllers/good_job/jobs_controller.rb +6 -0
- data/app/models/good_job/batch.rb +10 -2
- data/app/models/good_job/batch_record.rb +21 -5
- data/app/models/good_job/job.rb +2 -0
- data/config/routes.rb +1 -1
- data/lib/generators/good_job/templates/install/migrations/create_good_jobs.rb.erb +1 -0
- data/lib/generators/good_job/templates/update/migrations/02_add_jobs_finished_at_to_good_job_batches.rb.erb +17 -0
- data/lib/good_job/adapter.rb +1 -1
- data/lib/good_job/version.rb +1 -1
- data/lib/good_job.rb +4 -4
- metadata +3 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: ec5d4c89f52824a8023774ca2179f67579c21ae7772737a367ee912bc58db9bd
         | 
| 4 | 
            +
              data.tar.gz: f8526687220d9bd8ec7222f897f2302ab7d42f8883627c43195390b7ab9cf0be
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 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,  | 
| 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 ` | 
| 687 | 
            -
                  def perform(batch,  | 
| 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 | 
            -
                    #  | 
| 692 | 
            -
                     | 
| 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 ` | 
| 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,  | 
| 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 | 
            -
                #  | 
| 772 | 
            -
                 | 
| 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,  | 
| 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 | 
            -
             | 
| 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 | 
            -
                           | 
| 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( | 
| 56 | 
            -
                   | 
| 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 | 
            -
             | 
| 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)
         | 
    
        data/app/models/good_job/job.rb
    CHANGED
    
    | @@ -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
    
    
| @@ -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
         | 
    
        data/lib/good_job/adapter.rb
    CHANGED
    
    
    
        data/lib/good_job/version.rb
    CHANGED
    
    
    
        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 [ | 
| 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 | 
            -
                 | 
| 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. | 
| 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- | 
| 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
         |