good_job 3.30.1 → 4.0.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 +17 -0
- data/README.md +22 -0
- data/app/charts/good_job/scheduled_by_queue_chart.rb +2 -2
- data/app/controllers/good_job/cron_entries_controller.rb +0 -8
- data/app/controllers/good_job/metrics_controller.rb +1 -1
- data/app/controllers/good_job/performances_controller.rb +5 -9
- data/app/models/concerns/good_job/filterable.rb +1 -1
- data/app/models/good_job/base_execution.rb +104 -202
- data/app/models/good_job/cron_entry.rb +0 -2
- data/app/models/good_job/discrete_execution.rb +1 -31
- data/app/models/good_job/execution.rb +3 -7
- data/app/models/good_job/job.rb +37 -116
- data/app/models/good_job/process.rb +7 -20
- data/app/views/good_job/batches/index.html.erb +11 -15
- data/app/views/good_job/jobs/_executions.erb +1 -1
- data/app/views/good_job/jobs/_table.erb +1 -1
- data/app/views/good_job/jobs/show.html.erb +1 -8
- data/app/views/good_job/performances/show.html.erb +33 -40
- data/config/locales/de.yml +1 -4
- data/config/locales/en.yml +1 -4
- data/config/locales/es.yml +1 -4
- data/config/locales/fr.yml +1 -4
- data/config/locales/it.yml +1 -4
- data/config/locales/ja.yml +1 -4
- data/config/locales/ko.yml +1 -4
- data/config/locales/nl.yml +1 -4
- data/config/locales/pt-BR.yml +1 -4
- data/config/locales/ru.yml +1 -4
- data/config/locales/tr.yml +1 -4
- data/config/locales/uk.yml +1 -4
- data/lib/generators/good_job/templates/update/migrations/01_create_good_jobs.rb.erb +65 -3
- data/lib/good_job/active_job_extensions/batches.rb +1 -1
- data/lib/good_job/active_job_extensions/concurrency.rb +10 -10
- data/lib/good_job/adapter.rb +13 -24
- data/lib/good_job/current_thread.rb +6 -6
- data/lib/good_job/job_performer.rb +2 -2
- data/lib/good_job/log_subscriber.rb +2 -10
- data/lib/good_job/notifier.rb +3 -3
- data/lib/good_job/version.rb +1 -1
- data/lib/good_job.rb +22 -21
- metadata +16 -30
- data/lib/generators/good_job/templates/update/migrations/02_create_good_job_settings.rb.erb +0 -20
- data/lib/generators/good_job/templates/update/migrations/03_create_index_good_jobs_jobs_on_priority_created_at_when_unfinished.rb.erb +0 -19
- data/lib/generators/good_job/templates/update/migrations/04_create_good_job_batches.rb.erb +0 -35
- data/lib/generators/good_job/templates/update/migrations/05_create_good_job_executions.rb.erb +0 -33
- data/lib/generators/good_job/templates/update/migrations/06_create_good_jobs_error_event.rb.erb +0 -16
- data/lib/generators/good_job/templates/update/migrations/07_recreate_good_job_cron_indexes_with_conditional.rb.erb +0 -45
- data/lib/generators/good_job/templates/update/migrations/08_create_good_job_labels.rb.erb +0 -15
- data/lib/generators/good_job/templates/update/migrations/09_create_good_job_labels_index.rb.erb +0 -22
- data/lib/generators/good_job/templates/update/migrations/10_remove_good_job_active_id_index.rb.erb +0 -21
- data/lib/generators/good_job/templates/update/migrations/11_create_index_good_job_jobs_for_candidate_lookup.rb.erb +0 -19
- data/lib/generators/good_job/templates/update/migrations/12_create_good_job_execution_error_backtrace.rb.erb +0 -15
- data/lib/generators/good_job/templates/update/migrations/13_create_good_job_process_lock_ids.rb.erb +0 -18
- data/lib/generators/good_job/templates/update/migrations/14_create_good_job_process_lock_indexes.rb.erb +0 -38
- data/lib/generators/good_job/templates/update/migrations/15_create_good_job_execution_duration.rb.erb +0 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3a4006c920a731f763e36a964e7eb161a292e8a4b34bffa4b60b95b75145c28c
|
4
|
+
data.tar.gz: f54b107db6ac1e2304a4ee9e2d34fa65032dc2b1ab0425e0be5ea42d39e5590b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4474aaa0540dc5ff1bba65e8807a6fa69a9901f2e68051c57cf954e30270e9e6d7eaf52a3ce8ec7de6f48b16fc60fa05e796f595f14dfda41a76cbeb5b042543
|
7
|
+
data.tar.gz: cf8103c84fceffe91e4f7b0f5083c3989d2312a17ce96b1db01d7726a99b651c7d850f83fe23075c5a01628a825ef71d335d0eb42e91a478a62dd7772ee95e54
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,22 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [v4.0.0](https://github.com/bensheldon/good_job/tree/v4.0.0) (2024-07-07)
|
4
|
+
|
5
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v3.99.0...v4.0.0)
|
6
|
+
|
7
|
+
**Implemented enhancements:**
|
8
|
+
|
9
|
+
- Release GoodJob v4 [\#1394](https://github.com/bensheldon/good_job/pull/1394) ([bensheldon](https://github.com/bensheldon))
|
10
|
+
|
11
|
+
## [v3.99.0](https://github.com/bensheldon/good_job/tree/v3.99.0) (2024-07-07)
|
12
|
+
|
13
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v3.30.1...v3.99.0)
|
14
|
+
|
15
|
+
**Merged pull requests:**
|
16
|
+
|
17
|
+
- Remove deprecation silencers for v3.99 release [\#1395](https://github.com/bensheldon/good_job/pull/1395) ([bensheldon](https://github.com/bensheldon))
|
18
|
+
- Add instructions and `GoodJob.v4_ready?` for upgrading to v4 [\#1356](https://github.com/bensheldon/good_job/pull/1356) ([bensheldon](https://github.com/bensheldon))
|
19
|
+
|
3
20
|
## [v3.30.1](https://github.com/bensheldon/good_job/tree/v3.30.1) (2024-07-06)
|
4
21
|
|
5
22
|
[Full Changelog](https://github.com/bensheldon/good_job/compare/v3.30.0...v3.30.1)
|
data/README.md
CHANGED
@@ -53,6 +53,7 @@ For more of the story of GoodJob, read the [introductory blog post](https://isla
|
|
53
53
|
- [Batches](#batches)
|
54
54
|
- [Updating](#updating)
|
55
55
|
- [Upgrading minor versions](#upgrading-minor-versions)
|
56
|
+
- [Upgrading v3 to v4](#upgrading-v3-to-v4)
|
56
57
|
- [Upgrading v2 to v3](#upgrading-v2-to-v3)
|
57
58
|
- [Upgrading v1 to v2](#upgrading-v1-to-v2)
|
58
59
|
- [Go deeper](#go-deeper)
|
@@ -873,6 +874,27 @@ To perform upgrades to the GoodJob database tables:
|
|
873
874
|
1. Commit the migration files and resulting `db/schema.rb` changes.
|
874
875
|
1. Deploy the code, run the migrations against the production database, and restart server/worker processes.
|
875
876
|
|
877
|
+
#### Upgrading v3 to v4
|
878
|
+
|
879
|
+
GoodJob v4 changes how job and job execution records are stored in the database; moving from job and executions being commingled in the `good_jobs` table to separately and discretely storing job executions in `good_job_executions`. To safely upgrade, all unfinished jobs must use the new format. This change was introduced in GoodJob [v3.15.4 (April 2023)](https://github.com/bensheldon/good_job/releases/tag/v3.15.4), so your application is likely ready-to-upgrade already if you have kept up with GoodJob updates.
|
880
|
+
|
881
|
+
To upgrade:
|
882
|
+
|
883
|
+
1. Upgrade to v3.99.x, following the minor version upgrade process, running any remaining database migrations (rails g good_job:update) and addressing deprecation warnings.
|
884
|
+
1. Check if your application is safe to upgrade to the new job record format by running either:
|
885
|
+
- In a production console, run `GoodJob.v4_ready?` which should return `true` when safely upgradable.
|
886
|
+
- Or, when connected to the production database verify that `SELECT COUNT(*) FROM "good_jobs" WHERE finished_at IS NULL AND is_discrete IS NOT TRUE` returns `0`
|
887
|
+
|
888
|
+
If not all unfinished jobs are stored in the new format, either wait to upgrade until those jobs finish or discard them. Not waiting could prevent those jobs from successfully running when upgrading to v4.
|
889
|
+
1. Upgrade from v3.99.x to v4.x.
|
890
|
+
|
891
|
+
Notable changes:
|
892
|
+
|
893
|
+
- Only supports Rails 6.1+, CRuby 3.0+ and JRuby 9.4+, Postgres 12+. Rails 6.0 is no longer supported. CRuby 2.6 and 2.7 are no longer supported. JRuby 9.3 is no longer supported.
|
894
|
+
- Changes job `priority` to give smaller numbers higher priority (default: `0`), in accordance with Active Job's definition of priority.
|
895
|
+
- Enqueues and executes jobs via the `GoodJob::Job` model instead of `GoodJob::Execution`
|
896
|
+
- Setting `config.good_job.cleanup_interval_jobs`, `GOOD_JOB_CLEANUP_INTERVAL_JOBS`, `config.good_job.cleanup_interval_seconds`, or `GOOD_JOB_CLEANUP_INTERVAL_SECONDS` to `nil` or `""` no longer disables count- or time-based cleanups. Set to `false` to disable, or `-1` to run a cleanup after every job execution.
|
897
|
+
|
876
898
|
#### Upgrading v2 to v3
|
877
899
|
|
878
900
|
GoodJob v3 is operationally identical to v2; upgrading to GoodJob v3 should be simple. If you are already using `>= v2.9+` no other changes are necessary.
|
@@ -11,7 +11,7 @@ module GoodJob
|
|
11
11
|
start_time = end_time - 1.day
|
12
12
|
table_name = GoodJob::Job.table_name
|
13
13
|
|
14
|
-
count_query = Arel.sql(GoodJob::
|
14
|
+
count_query = Arel.sql(GoodJob::Job.pg_or_jdbc_query(<<~SQL.squish))
|
15
15
|
SELECT *
|
16
16
|
FROM generate_series(
|
17
17
|
date_trunc('hour', $1::timestamp),
|
@@ -35,7 +35,7 @@ module GoodJob
|
|
35
35
|
ActiveRecord::Relation::QueryAttribute.new('start_time', start_time, ActiveRecord::Type::DateTime.new),
|
36
36
|
ActiveRecord::Relation::QueryAttribute.new('end_time', end_time, ActiveRecord::Type::DateTime.new),
|
37
37
|
]
|
38
|
-
executions_data = GoodJob::
|
38
|
+
executions_data = GoodJob::Job.connection.exec_query(GoodJob::Job.pg_or_jdbc_query(count_query), "GoodJob Dashboard Chart", binds)
|
39
39
|
|
40
40
|
queue_names = executions_data.reject { |d| d['count'].nil? }.map { |d| d['queue_name'] || BaseFilter::EMPTY }.uniq
|
41
41
|
labels = []
|
@@ -2,8 +2,6 @@
|
|
2
2
|
|
3
3
|
module GoodJob
|
4
4
|
class CronEntriesController < GoodJob::ApplicationController
|
5
|
-
before_action :check_settings_migration!, only: [:enable, :disable]
|
6
|
-
|
7
5
|
def index
|
8
6
|
@cron_entries = CronEntry.all
|
9
7
|
end
|
@@ -30,11 +28,5 @@ module GoodJob
|
|
30
28
|
@cron_entry.disable
|
31
29
|
redirect_back(fallback_location: cron_entries_path, notice: t(".notice"))
|
32
30
|
end
|
33
|
-
|
34
|
-
private
|
35
|
-
|
36
|
-
def check_settings_migration!
|
37
|
-
redirect_back(fallback_location: cron_entries_path, alert: t("good_job.cron_entries.pending_migrations")) unless GoodJob::Setting.migrated?
|
38
|
-
end
|
39
31
|
end
|
40
32
|
end
|
@@ -4,7 +4,7 @@ module GoodJob
|
|
4
4
|
class MetricsController < ApplicationController
|
5
5
|
def primary_nav
|
6
6
|
jobs_count = GoodJob::Job.count
|
7
|
-
batches_count = GoodJob::BatchRecord.
|
7
|
+
batches_count = GoodJob::BatchRecord.all.size
|
8
8
|
cron_entries_count = GoodJob::CronEntry.all.size
|
9
9
|
processes_count = GoodJob::Process.active.count
|
10
10
|
|
@@ -3,21 +3,17 @@
|
|
3
3
|
module GoodJob
|
4
4
|
class PerformancesController < ApplicationController
|
5
5
|
def show
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
.select("
|
6
|
+
@performances = GoodJob::DiscreteExecution
|
7
|
+
.where.not(job_class: nil)
|
8
|
+
.group(:job_class)
|
9
|
+
.select("
|
11
10
|
job_class,
|
12
11
|
COUNT(*) AS executions_count,
|
13
12
|
AVG(duration) AS avg_duration,
|
14
13
|
MIN(duration) AS min_duration,
|
15
14
|
MAX(duration) AS max_duration
|
16
15
|
")
|
17
|
-
|
18
|
-
else
|
19
|
-
@needs_upgrade = true
|
20
|
-
end
|
16
|
+
.order("job_class")
|
21
17
|
end
|
22
18
|
end
|
23
19
|
end
|
@@ -35,7 +35,7 @@ module GoodJob
|
|
35
35
|
next if query.blank?
|
36
36
|
|
37
37
|
# TODO: turn this into proper bind parameters in Arel
|
38
|
-
tsvector = "(to_tsvector('english', id::text) || to_tsvector('english', COALESCE(active_job_id::text, '')) || to_tsvector('english', serialized_params) || to_tsvector('english', COALESCE(error, ''))
|
38
|
+
tsvector = "(to_tsvector('english', id::text) || to_tsvector('english', COALESCE(active_job_id::text, '')) || to_tsvector('english', serialized_params) || to_tsvector('english', COALESCE(error, '')) || to_tsvector('english', COALESCE(array_to_string(labels, ' '), '')))"
|
39
39
|
to_tsquery_function = database_supports_websearch_to_tsquery? ? 'websearch_to_tsquery' : 'plainto_tsquery'
|
40
40
|
where("#{tsvector} @@ #{to_tsquery_function}(?)", query)
|
41
41
|
.order(sanitize_sql_for_order([Arel.sql("ts_rank(#{tsvector}, #{to_tsquery_function}(?))"), query]) => 'DESC')
|
@@ -74,9 +74,6 @@ module GoodJob
|
|
74
74
|
end
|
75
75
|
end
|
76
76
|
|
77
|
-
belongs_to :batch, class_name: 'GoodJob::BatchRecord', optional: true, inverse_of: :executions
|
78
|
-
has_many :discrete_executions, class_name: 'GoodJob::DiscreteExecution', foreign_key: 'active_job_id', primary_key: 'active_job_id', inverse_of: :execution # rubocop:disable Rails/HasManyOrHasOneDependent
|
79
|
-
|
80
77
|
# With a given class name
|
81
78
|
# @!method job_class(name)
|
82
79
|
# @!scope class
|
@@ -84,11 +81,6 @@ module GoodJob
|
|
84
81
|
# @return [ActiveRecord::Relation]
|
85
82
|
scope :job_class, ->(name) { where(params_job_class.eq(name)) }
|
86
83
|
|
87
|
-
after_destroy lambda {
|
88
|
-
GoodJob::DiscreteExecution.where(active_job_id: active_job_id).delete_all if discrete? # TODO: move into association `dependent: :delete_all` after v4
|
89
|
-
self.class.active_job_id(active_job_id).delete_all
|
90
|
-
}, if: -> { @_destroy_job }
|
91
|
-
|
92
84
|
# Get jobs with given ActiveJob ID
|
93
85
|
# @!method active_job_id(active_job_id)
|
94
86
|
# @!scope class
|
@@ -226,67 +218,12 @@ module GoodJob
|
|
226
218
|
end
|
227
219
|
|
228
220
|
def discrete_support?
|
229
|
-
|
230
|
-
end
|
231
|
-
|
232
|
-
def error_event_migrated?
|
233
|
-
return true if columns_hash["error_event"].present?
|
234
|
-
|
235
|
-
migration_pending_warning!
|
236
|
-
false
|
237
|
-
end
|
238
|
-
|
239
|
-
def cron_indices_migrated?
|
240
|
-
return true if connection.index_name_exists?(:good_jobs, :index_good_jobs_on_cron_key_and_created_at_cond)
|
241
|
-
|
242
|
-
migration_pending_warning!
|
243
|
-
false
|
244
|
-
end
|
245
|
-
|
246
|
-
def labels_migrated?
|
247
|
-
return true if columns_hash["labels"].present?
|
248
|
-
|
249
|
-
migration_pending_warning!
|
250
|
-
false
|
251
|
-
end
|
252
|
-
|
253
|
-
def labels_indices_migrated?
|
254
|
-
return true if connection.index_name_exists?(:good_jobs, :index_good_jobs_on_labels)
|
255
|
-
|
256
|
-
migration_pending_warning!
|
257
|
-
false
|
221
|
+
true
|
258
222
|
end
|
259
|
-
|
260
|
-
def active_job_id_index_removal_migrated?
|
261
|
-
return true unless connection.index_name_exists?(:good_jobs, :index_good_jobs_on_active_job_id)
|
262
|
-
|
263
|
-
migration_pending_warning!
|
264
|
-
false
|
265
|
-
end
|
266
|
-
|
267
|
-
def candidate_lookup_index_migrated?
|
268
|
-
return true if connection.index_name_exists?(:good_jobs, :index_good_job_jobs_for_candidate_lookup)
|
269
|
-
|
270
|
-
migration_pending_warning!
|
271
|
-
false
|
272
|
-
end
|
273
|
-
|
274
|
-
def process_lock_migrated?
|
275
|
-
return true if connection.index_name_exists?(:good_job_executions, :index_good_job_executions_on_process_id_and_created_at)
|
276
|
-
|
277
|
-
migration_pending_warning!
|
278
|
-
false
|
279
|
-
end
|
280
|
-
end
|
281
|
-
|
282
|
-
# The ActiveJob job class, as a string
|
283
|
-
# @return [String]
|
284
|
-
def job_class
|
285
|
-
discrete? ? attributes['job_class'] : serialized_params['job_class']
|
286
223
|
end
|
287
224
|
|
288
225
|
def discrete?
|
289
|
-
|
226
|
+
is_discrete?
|
290
227
|
end
|
291
228
|
|
292
229
|
# Build an ActiveJob instance and deserialize the arguments, using `#active_job_data`.
|
@@ -304,55 +241,54 @@ module GoodJob
|
|
304
241
|
raise unless ignore_deserialization_errors
|
305
242
|
end
|
306
243
|
|
307
|
-
def self.build_for_enqueue(active_job,
|
308
|
-
new(**enqueue_args(active_job,
|
244
|
+
def self.build_for_enqueue(active_job, scheduled_at: nil)
|
245
|
+
new(**enqueue_args(active_job, scheduled_at: scheduled_at))
|
309
246
|
end
|
310
247
|
|
311
248
|
# Construct arguments for GoodJob::Execution from an ActiveJob instance.
|
312
|
-
def self.enqueue_args(active_job,
|
313
|
-
if active_job.priority && GoodJob.configuration.smaller_number_is_higher_priority.nil?
|
314
|
-
GoodJob.deprecator.warn(<<~DEPRECATION)
|
315
|
-
The next major version of GoodJob (v4.0) will change job `priority` to give smaller numbers higher priority (default: `0`), in accordance with Active Job's definition of priority.
|
316
|
-
To opt-in to this behavior now, set `config.good_job.smaller_number_is_higher_priority = true` in your GoodJob initializer or application.rb.
|
317
|
-
To not opt-in yet, but silence this deprecation warning, set `config.good_job.smaller_number_is_higher_priority = false`.
|
318
|
-
DEPRECATION
|
319
|
-
end
|
320
|
-
|
249
|
+
def self.enqueue_args(active_job, scheduled_at: nil)
|
321
250
|
execution_args = {
|
251
|
+
id: active_job.job_id,
|
322
252
|
active_job_id: active_job.job_id,
|
253
|
+
job_class: active_job.class.name,
|
323
254
|
queue_name: active_job.queue_name.presence || DEFAULT_QUEUE_NAME,
|
324
255
|
priority: active_job.priority || DEFAULT_PRIORITY,
|
325
256
|
serialized_params: active_job.serialize,
|
257
|
+
created_at: Time.current,
|
326
258
|
}
|
327
|
-
|
259
|
+
|
260
|
+
execution_args[:scheduled_at] = if scheduled_at
|
261
|
+
scheduled_at
|
262
|
+
elsif active_job.scheduled_at
|
263
|
+
Time.zone.at(active_job.scheduled_at)
|
264
|
+
else
|
265
|
+
execution_args[:created_at]
|
266
|
+
end
|
267
|
+
|
328
268
|
execution_args[:concurrency_key] = active_job.good_job_concurrency_key if active_job.respond_to?(:good_job_concurrency_key)
|
329
269
|
|
330
|
-
if active_job.respond_to?(:good_job_labels) && active_job.good_job_labels.any?
|
270
|
+
if active_job.respond_to?(:good_job_labels) && active_job.good_job_labels.any?
|
331
271
|
labels = active_job.good_job_labels.dup
|
332
272
|
labels.map! { |label| label.to_s.strip.presence }
|
333
273
|
labels.tap(&:compact!).tap(&:uniq!)
|
334
274
|
execution_args[:labels] = labels
|
335
275
|
end
|
336
276
|
|
337
|
-
|
338
|
-
|
277
|
+
reenqueued_current_job = CurrentThread.active_job_id && CurrentThread.active_job_id == active_job.job_id
|
278
|
+
current_job = CurrentThread.job
|
339
279
|
|
340
|
-
if
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
end
|
345
|
-
execution_args[:cron_key] = current_execution.cron_key
|
280
|
+
if reenqueued_current_job
|
281
|
+
execution_args[:batch_id] = current_job.batch_id
|
282
|
+
execution_args[:batch_callback_id] = current_job.batch_callback_id
|
283
|
+
execution_args[:cron_key] = current_job.cron_key
|
346
284
|
else
|
347
|
-
|
348
|
-
|
349
|
-
execution_args[:batch_callback_id] = GoodJob::Batch.current_batch_callback_id
|
350
|
-
end
|
285
|
+
execution_args[:batch_id] = GoodJob::Batch.current_batch_id
|
286
|
+
execution_args[:batch_callback_id] = GoodJob::Batch.current_batch_callback_id
|
351
287
|
execution_args[:cron_key] = CurrentThread.cron_key
|
352
288
|
execution_args[:cron_at] = CurrentThread.cron_at
|
353
289
|
end
|
354
290
|
|
355
|
-
execution_args
|
291
|
+
execution_args
|
356
292
|
end
|
357
293
|
|
358
294
|
# Finds the next eligible Execution, acquire an advisory lock related to it, and
|
@@ -364,21 +300,23 @@ module GoodJob
|
|
364
300
|
# raised, if any (if the job raised, then the second array entry will be
|
365
301
|
# +nil+). If there were no jobs to execute, returns +nil+.
|
366
302
|
def self.perform_with_advisory_lock(lock_id:, parsed_queues: nil, queue_select_limit: nil)
|
367
|
-
|
303
|
+
job = nil
|
368
304
|
result = nil
|
369
305
|
|
370
|
-
unfinished.dequeueing_ordered(parsed_queues).only_scheduled.limit(1).with_advisory_lock(select_limit: queue_select_limit) do |
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
306
|
+
unfinished.dequeueing_ordered(parsed_queues).only_scheduled.limit(1).with_advisory_lock(select_limit: queue_select_limit) do |jobs|
|
307
|
+
job = jobs.first
|
308
|
+
|
309
|
+
if job&.executable?
|
310
|
+
yield(job) if block_given?
|
311
|
+
|
312
|
+
result = job.perform(lock_id: lock_id)
|
375
313
|
else
|
376
|
-
|
314
|
+
job = nil
|
377
315
|
yield(nil) if block_given?
|
378
316
|
end
|
379
317
|
end
|
380
318
|
|
381
|
-
|
319
|
+
job&.run_callbacks(:perform_unlocked)
|
382
320
|
result
|
383
321
|
end
|
384
322
|
|
@@ -414,46 +352,38 @@ module GoodJob
|
|
414
352
|
# The new {Execution} instance representing the queued ActiveJob job.
|
415
353
|
def self.enqueue(active_job, scheduled_at: nil, create_with_advisory_lock: false)
|
416
354
|
ActiveSupport::Notifications.instrument("enqueue_job.good_job", { active_job: active_job, scheduled_at: scheduled_at, create_with_advisory_lock: create_with_advisory_lock }) do |instrument_payload|
|
417
|
-
|
355
|
+
current_job = CurrentThread.job
|
418
356
|
|
419
|
-
retried =
|
357
|
+
retried = current_job && current_job.active_job_id == active_job.job_id
|
420
358
|
if retried
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
execution.finished_at = nil
|
429
|
-
else
|
430
|
-
execution = build_for_enqueue(active_job, { scheduled_at: scheduled_at })
|
431
|
-
end
|
359
|
+
job = current_job
|
360
|
+
job.assign_attributes(enqueue_args(active_job, scheduled_at: scheduled_at))
|
361
|
+
job.scheduled_at ||= Time.current
|
362
|
+
# TODO: these values ideally shouldn't be persisted until the current_job is finished
|
363
|
+
# which will require handling `retry_job` being called from outside the job context.
|
364
|
+
job.performed_at = nil
|
365
|
+
job.finished_at = nil
|
432
366
|
else
|
433
|
-
|
434
|
-
execution.make_discrete if discrete_support?
|
367
|
+
job = build_for_enqueue(active_job, scheduled_at: scheduled_at)
|
435
368
|
end
|
436
369
|
|
437
370
|
if create_with_advisory_lock
|
438
|
-
if
|
439
|
-
|
371
|
+
if job.persisted?
|
372
|
+
job.advisory_lock
|
440
373
|
else
|
441
|
-
|
374
|
+
job.create_with_advisory_lock = true
|
442
375
|
end
|
443
376
|
end
|
444
377
|
|
445
|
-
instrument_payload[:
|
446
|
-
|
378
|
+
instrument_payload[:job] = job
|
379
|
+
job.save!
|
447
380
|
|
448
|
-
if retried
|
449
|
-
CurrentThread.execution_retried = execution
|
450
|
-
CurrentThread.execution.retried_good_job_id = execution.id unless current_execution.discrete?
|
451
|
-
else
|
452
|
-
CurrentThread.execution_retried = nil
|
453
|
-
end
|
381
|
+
CurrentThread.execution_retried = (job if retried)
|
454
382
|
|
455
|
-
active_job.provider_job_id =
|
456
|
-
|
383
|
+
active_job.provider_job_id = job.id
|
384
|
+
raise "These should be equal" if active_job.provider_job_id != active_job.job_id
|
385
|
+
|
386
|
+
job
|
457
387
|
end
|
458
388
|
end
|
459
389
|
|
@@ -477,64 +407,47 @@ module GoodJob
|
|
477
407
|
discrete_execution = nil
|
478
408
|
result = GoodJob::CurrentThread.within do |current_thread|
|
479
409
|
current_thread.reset
|
480
|
-
current_thread.
|
410
|
+
current_thread.job = self
|
481
411
|
|
482
412
|
existing_performed_at = performed_at
|
483
413
|
if existing_performed_at
|
484
414
|
current_thread.execution_interrupted = existing_performed_at
|
485
415
|
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
discrete_executions.where(finished_at: nil).where.not(performed_at: nil).update_all(discrete_execution_attrs) # rubocop:disable Rails/SkipsModelValidations
|
499
|
-
end
|
416
|
+
interrupt_error_string = self.class.format_error(GoodJob::InterruptError.new("Interrupted after starting perform at '#{existing_performed_at}'"))
|
417
|
+
self.error = interrupt_error_string
|
418
|
+
self.error_event = ERROR_EVENT_INTERRUPTED
|
419
|
+
monotonic_duration = (::Process.clock_gettime(::Process::CLOCK_MONOTONIC) - monotonic_start).seconds
|
420
|
+
|
421
|
+
discrete_execution_attrs = {
|
422
|
+
error: interrupt_error_string,
|
423
|
+
finished_at: job_performed_at,
|
424
|
+
}
|
425
|
+
discrete_execution_attrs[:error_event] = GoodJob::ErrorEvents::ERROR_EVENT_ENUMS[GoodJob::ErrorEvents::ERROR_EVENT_INTERRUPTED]
|
426
|
+
discrete_execution_attrs[:duration] = monotonic_duration
|
427
|
+
discrete_executions.where(finished_at: nil).where.not(performed_at: nil).update_all(discrete_execution_attrs) # rubocop:disable Rails/SkipsModelValidations
|
500
428
|
end
|
501
429
|
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
execution_attrs = {
|
514
|
-
performed_at: job_performed_at,
|
515
|
-
executions_count: ((executions_count || 0) + 1),
|
516
|
-
}
|
517
|
-
if GoodJob::Execution.columns_hash.key?("locked_by_id")
|
518
|
-
execution_attrs[:locked_by_id] = lock_id
|
519
|
-
execution_attrs[:locked_at] = Time.current
|
520
|
-
end
|
521
|
-
|
522
|
-
discrete_execution = discrete_executions.create!(discrete_execution_attrs)
|
523
|
-
update!(execution_attrs)
|
524
|
-
end
|
525
|
-
else
|
526
|
-
execution_attrs = {
|
430
|
+
transaction do
|
431
|
+
discrete_execution_attrs = {
|
432
|
+
job_class: job_class,
|
433
|
+
queue_name: queue_name,
|
434
|
+
serialized_params: serialized_params,
|
435
|
+
scheduled_at: (scheduled_at || created_at),
|
436
|
+
created_at: job_performed_at,
|
437
|
+
process_id: lock_id,
|
438
|
+
}
|
439
|
+
job_attrs = {
|
527
440
|
performed_at: job_performed_at,
|
441
|
+
executions_count: ((executions_count || 0) + 1),
|
442
|
+
locked_by_id: lock_id,
|
443
|
+
locked_at: Time.current,
|
528
444
|
}
|
529
|
-
if GoodJob::Execution.columns_hash.key?("locked_by_id")
|
530
|
-
execution_attrs[:locked_by_id] = lock_id
|
531
|
-
execution_attrs[:locked_at] = Time.current
|
532
|
-
end
|
533
445
|
|
534
|
-
|
446
|
+
discrete_execution = discrete_executions.create!(discrete_execution_attrs)
|
447
|
+
update!(job_attrs)
|
535
448
|
end
|
536
449
|
|
537
|
-
ActiveSupport::Notifications.instrument("perform_job.good_job", {
|
450
|
+
ActiveSupport::Notifications.instrument("perform_job.good_job", { job: self, execution: discrete_execution, process_id: current_thread.process_id, thread_name: current_thread.thread_name }) do |instrument_payload|
|
538
451
|
value = ActiveJob::Base.execute(active_job_data)
|
539
452
|
|
540
453
|
if value.is_a?(Exception)
|
@@ -585,50 +498,39 @@ module GoodJob
|
|
585
498
|
error_string = self.class.format_error(job_error)
|
586
499
|
|
587
500
|
job_attributes[:error] = error_string
|
588
|
-
job_attributes[:error_event] = result.error_event
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
end
|
501
|
+
job_attributes[:error_event] = result.error_event
|
502
|
+
|
503
|
+
discrete_execution.error = error_string
|
504
|
+
discrete_execution.error_event = result.error_event
|
505
|
+
discrete_execution.error_backtrace = job_error.backtrace
|
594
506
|
else
|
595
507
|
job_attributes[:error] = nil
|
596
508
|
job_attributes[:error_event] = nil
|
597
509
|
end
|
598
|
-
job_attributes.delete(:error_event) unless self.class.error_event_migrated?
|
599
510
|
|
600
511
|
job_finished_at = Time.current
|
601
512
|
monotonic_duration = (::Process.clock_gettime(::Process::CLOCK_MONOTONIC) - monotonic_start).seconds
|
602
513
|
job_attributes[:finished_at] = job_finished_at
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
end
|
514
|
+
|
515
|
+
discrete_execution.finished_at = job_finished_at
|
516
|
+
discrete_execution.duration = monotonic_duration
|
607
517
|
|
608
518
|
retry_unhandled_error = result.unhandled_error && GoodJob.retry_on_unhandled_error
|
609
519
|
reenqueued = result.retried? || retried_good_job_id.present? || retry_unhandled_error
|
610
520
|
if reenqueued
|
611
|
-
|
612
|
-
|
613
|
-
job_attributes[:finished_at] = nil
|
614
|
-
else
|
615
|
-
job_attributes[:retried_good_job_id] = retried_good_job_id
|
616
|
-
job_attributes[:finished_at] = nil if retry_unhandled_error
|
617
|
-
end
|
521
|
+
job_attributes[:performed_at] = nil
|
522
|
+
job_attributes[:finished_at] = nil
|
618
523
|
end
|
619
524
|
|
525
|
+
assign_attributes(job_attributes)
|
620
526
|
preserve_unhandled = (result.unhandled_error && (GoodJob.retry_on_unhandled_error || GoodJob.preserve_job_records == :on_unhandled_error))
|
621
|
-
if GoodJob.preserve_job_records == true || reenqueued || preserve_unhandled || cron_key.present?
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
update!(job_attributes)
|
626
|
-
end
|
627
|
-
else
|
628
|
-
update!(job_attributes)
|
527
|
+
if finished_at.blank? || GoodJob.preserve_job_records == true || reenqueued || preserve_unhandled || cron_key.present?
|
528
|
+
transaction do
|
529
|
+
discrete_execution.save!
|
530
|
+
save!
|
629
531
|
end
|
630
532
|
else
|
631
|
-
|
533
|
+
destroy!
|
632
534
|
end
|
633
535
|
|
634
536
|
result
|
@@ -709,7 +611,7 @@ module GoodJob
|
|
709
611
|
end
|
710
612
|
|
711
613
|
def continue_discard_or_finish_batch
|
712
|
-
batch._continue_discard_or_finish(self) if
|
614
|
+
batch._continue_discard_or_finish(self) if batch.present?
|
713
615
|
end
|
714
616
|
|
715
617
|
def active_job_data
|
@@ -717,7 +619,7 @@ module GoodJob
|
|
717
619
|
.tap do |job_data|
|
718
620
|
job_data["provider_job_id"] = id
|
719
621
|
job_data["good_job_concurrency_key"] = concurrency_key if concurrency_key
|
720
|
-
job_data["good_job_labels"] = Array(labels) if
|
622
|
+
job_data["good_job_labels"] = Array(labels) if labels.present?
|
721
623
|
end
|
722
624
|
end
|
723
625
|
end
|