good_job 4.2.0 → 4.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +34 -0
- data/README.md +10 -10
- data/app/controllers/good_job/jobs_controller.rb +7 -1
- data/app/models/good_job/base_record.rb +1 -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,39 @@
|
|
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
|
+
|
23
|
+
## [v4.2.1](https://github.com/bensheldon/good_job/tree/v4.2.1) (2024-08-29)
|
24
|
+
|
25
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v4.2.0...v4.2.1)
|
26
|
+
|
27
|
+
**Closed issues:**
|
28
|
+
|
29
|
+
- Issue with active\_record.strict\_loading\_by\_default [\#1474](https://github.com/bensheldon/good_job/issues/1474)
|
30
|
+
|
31
|
+
**Merged pull requests:**
|
32
|
+
|
33
|
+
- fix strict\_loading\_by\_default in BaseRecord [\#1475](https://github.com/bensheldon/good_job/pull/1475) ([emilsosa](https://github.com/emilsosa))
|
34
|
+
- Bump rexml from 3.3.3 to 3.3.6 [\#1473](https://github.com/bensheldon/good_job/pull/1473) ([dependabot[bot]](https://github.com/apps/dependabot))
|
35
|
+
- Bump fugit from 1.11.0 to 1.11.1 [\#1471](https://github.com/bensheldon/good_job/pull/1471) ([dependabot[bot]](https://github.com/apps/dependabot))
|
36
|
+
|
3
37
|
## [v4.2.0](https://github.com/bensheldon/good_job/tree/v4.2.0) (2024-08-16)
|
4
38
|
|
5
39
|
[Full Changelog](https://github.com/bensheldon/good_job/compare/v4.1.1...v4.2.0)
|
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')
|
@@ -61,7 +61,7 @@ module GoodJob
|
|
61
61
|
end
|
62
62
|
|
63
63
|
def show
|
64
|
-
@job = Job.includes_advisory_locks.find(params[:id])
|
64
|
+
@job = Job.includes(:executions).includes_advisory_locks.find(params[:id])
|
65
65
|
end
|
66
66
|
|
67
67
|
def discard
|
@@ -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
|