good_job 3.99.0 → 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 +8 -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 -201
- 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 +16 -21
- metadata +15 -29
- 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,13 @@
|
|
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
|
+
|
3
11
|
## [v3.99.0](https://github.com/bensheldon/good_job/tree/v3.99.0) (2024-07-07)
|
4
12
|
|
5
13
|
[Full Changelog](https://github.com/bensheldon/good_job/compare/v3.30.1...v3.99.0)
|
@@ -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,54 +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.in?([nil, false])
|
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
|
-
DEPRECATION
|
318
|
-
end
|
319
|
-
|
249
|
+
def self.enqueue_args(active_job, scheduled_at: nil)
|
320
250
|
execution_args = {
|
251
|
+
id: active_job.job_id,
|
321
252
|
active_job_id: active_job.job_id,
|
253
|
+
job_class: active_job.class.name,
|
322
254
|
queue_name: active_job.queue_name.presence || DEFAULT_QUEUE_NAME,
|
323
255
|
priority: active_job.priority || DEFAULT_PRIORITY,
|
324
256
|
serialized_params: active_job.serialize,
|
257
|
+
created_at: Time.current,
|
325
258
|
}
|
326
|
-
|
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
|
+
|
327
268
|
execution_args[:concurrency_key] = active_job.good_job_concurrency_key if active_job.respond_to?(:good_job_concurrency_key)
|
328
269
|
|
329
|
-
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?
|
330
271
|
labels = active_job.good_job_labels.dup
|
331
272
|
labels.map! { |label| label.to_s.strip.presence }
|
332
273
|
labels.tap(&:compact!).tap(&:uniq!)
|
333
274
|
execution_args[:labels] = labels
|
334
275
|
end
|
335
276
|
|
336
|
-
|
337
|
-
|
277
|
+
reenqueued_current_job = CurrentThread.active_job_id && CurrentThread.active_job_id == active_job.job_id
|
278
|
+
current_job = CurrentThread.job
|
338
279
|
|
339
|
-
if
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
end
|
344
|
-
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
|
345
284
|
else
|
346
|
-
|
347
|
-
|
348
|
-
execution_args[:batch_callback_id] = GoodJob::Batch.current_batch_callback_id
|
349
|
-
end
|
285
|
+
execution_args[:batch_id] = GoodJob::Batch.current_batch_id
|
286
|
+
execution_args[:batch_callback_id] = GoodJob::Batch.current_batch_callback_id
|
350
287
|
execution_args[:cron_key] = CurrentThread.cron_key
|
351
288
|
execution_args[:cron_at] = CurrentThread.cron_at
|
352
289
|
end
|
353
290
|
|
354
|
-
execution_args
|
291
|
+
execution_args
|
355
292
|
end
|
356
293
|
|
357
294
|
# Finds the next eligible Execution, acquire an advisory lock related to it, and
|
@@ -363,21 +300,23 @@ module GoodJob
|
|
363
300
|
# raised, if any (if the job raised, then the second array entry will be
|
364
301
|
# +nil+). If there were no jobs to execute, returns +nil+.
|
365
302
|
def self.perform_with_advisory_lock(lock_id:, parsed_queues: nil, queue_select_limit: nil)
|
366
|
-
|
303
|
+
job = nil
|
367
304
|
result = nil
|
368
305
|
|
369
|
-
unfinished.dequeueing_ordered(parsed_queues).only_scheduled.limit(1).with_advisory_lock(select_limit: queue_select_limit) do |
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
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)
|
374
313
|
else
|
375
|
-
|
314
|
+
job = nil
|
376
315
|
yield(nil) if block_given?
|
377
316
|
end
|
378
317
|
end
|
379
318
|
|
380
|
-
|
319
|
+
job&.run_callbacks(:perform_unlocked)
|
381
320
|
result
|
382
321
|
end
|
383
322
|
|
@@ -413,46 +352,38 @@ module GoodJob
|
|
413
352
|
# The new {Execution} instance representing the queued ActiveJob job.
|
414
353
|
def self.enqueue(active_job, scheduled_at: nil, create_with_advisory_lock: false)
|
415
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|
|
416
|
-
|
355
|
+
current_job = CurrentThread.job
|
417
356
|
|
418
|
-
retried =
|
357
|
+
retried = current_job && current_job.active_job_id == active_job.job_id
|
419
358
|
if retried
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
execution.finished_at = nil
|
428
|
-
else
|
429
|
-
execution = build_for_enqueue(active_job, { scheduled_at: scheduled_at })
|
430
|
-
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
|
431
366
|
else
|
432
|
-
|
433
|
-
execution.make_discrete if discrete_support?
|
367
|
+
job = build_for_enqueue(active_job, scheduled_at: scheduled_at)
|
434
368
|
end
|
435
369
|
|
436
370
|
if create_with_advisory_lock
|
437
|
-
if
|
438
|
-
|
371
|
+
if job.persisted?
|
372
|
+
job.advisory_lock
|
439
373
|
else
|
440
|
-
|
374
|
+
job.create_with_advisory_lock = true
|
441
375
|
end
|
442
376
|
end
|
443
377
|
|
444
|
-
instrument_payload[:
|
445
|
-
|
378
|
+
instrument_payload[:job] = job
|
379
|
+
job.save!
|
446
380
|
|
447
|
-
if retried
|
448
|
-
CurrentThread.execution_retried = execution
|
449
|
-
CurrentThread.execution.retried_good_job_id = execution.id unless current_execution.discrete?
|
450
|
-
else
|
451
|
-
CurrentThread.execution_retried = nil
|
452
|
-
end
|
381
|
+
CurrentThread.execution_retried = (job if retried)
|
453
382
|
|
454
|
-
active_job.provider_job_id =
|
455
|
-
|
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
|
456
387
|
end
|
457
388
|
end
|
458
389
|
|
@@ -476,64 +407,47 @@ module GoodJob
|
|
476
407
|
discrete_execution = nil
|
477
408
|
result = GoodJob::CurrentThread.within do |current_thread|
|
478
409
|
current_thread.reset
|
479
|
-
current_thread.
|
410
|
+
current_thread.job = self
|
480
411
|
|
481
412
|
existing_performed_at = performed_at
|
482
413
|
if existing_performed_at
|
483
414
|
current_thread.execution_interrupted = existing_performed_at
|
484
415
|
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
discrete_executions.where(finished_at: nil).where.not(performed_at: nil).update_all(discrete_execution_attrs) # rubocop:disable Rails/SkipsModelValidations
|
498
|
-
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
|
499
428
|
end
|
500
429
|
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
execution_attrs = {
|
513
|
-
performed_at: job_performed_at,
|
514
|
-
executions_count: ((executions_count || 0) + 1),
|
515
|
-
}
|
516
|
-
if GoodJob::Execution.columns_hash.key?("locked_by_id")
|
517
|
-
execution_attrs[:locked_by_id] = lock_id
|
518
|
-
execution_attrs[:locked_at] = Time.current
|
519
|
-
end
|
520
|
-
|
521
|
-
discrete_execution = discrete_executions.create!(discrete_execution_attrs)
|
522
|
-
update!(execution_attrs)
|
523
|
-
end
|
524
|
-
else
|
525
|
-
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 = {
|
526
440
|
performed_at: job_performed_at,
|
441
|
+
executions_count: ((executions_count || 0) + 1),
|
442
|
+
locked_by_id: lock_id,
|
443
|
+
locked_at: Time.current,
|
527
444
|
}
|
528
|
-
if GoodJob::Execution.columns_hash.key?("locked_by_id")
|
529
|
-
execution_attrs[:locked_by_id] = lock_id
|
530
|
-
execution_attrs[:locked_at] = Time.current
|
531
|
-
end
|
532
445
|
|
533
|
-
|
446
|
+
discrete_execution = discrete_executions.create!(discrete_execution_attrs)
|
447
|
+
update!(job_attrs)
|
534
448
|
end
|
535
449
|
|
536
|
-
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|
|
537
451
|
value = ActiveJob::Base.execute(active_job_data)
|
538
452
|
|
539
453
|
if value.is_a?(Exception)
|
@@ -584,50 +498,39 @@ module GoodJob
|
|
584
498
|
error_string = self.class.format_error(job_error)
|
585
499
|
|
586
500
|
job_attributes[:error] = error_string
|
587
|
-
job_attributes[:error_event] = result.error_event
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
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
|
593
506
|
else
|
594
507
|
job_attributes[:error] = nil
|
595
508
|
job_attributes[:error_event] = nil
|
596
509
|
end
|
597
|
-
job_attributes.delete(:error_event) unless self.class.error_event_migrated?
|
598
510
|
|
599
511
|
job_finished_at = Time.current
|
600
512
|
monotonic_duration = (::Process.clock_gettime(::Process::CLOCK_MONOTONIC) - monotonic_start).seconds
|
601
513
|
job_attributes[:finished_at] = job_finished_at
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
end
|
514
|
+
|
515
|
+
discrete_execution.finished_at = job_finished_at
|
516
|
+
discrete_execution.duration = monotonic_duration
|
606
517
|
|
607
518
|
retry_unhandled_error = result.unhandled_error && GoodJob.retry_on_unhandled_error
|
608
519
|
reenqueued = result.retried? || retried_good_job_id.present? || retry_unhandled_error
|
609
520
|
if reenqueued
|
610
|
-
|
611
|
-
|
612
|
-
job_attributes[:finished_at] = nil
|
613
|
-
else
|
614
|
-
job_attributes[:retried_good_job_id] = retried_good_job_id
|
615
|
-
job_attributes[:finished_at] = nil if retry_unhandled_error
|
616
|
-
end
|
521
|
+
job_attributes[:performed_at] = nil
|
522
|
+
job_attributes[:finished_at] = nil
|
617
523
|
end
|
618
524
|
|
525
|
+
assign_attributes(job_attributes)
|
619
526
|
preserve_unhandled = (result.unhandled_error && (GoodJob.retry_on_unhandled_error || GoodJob.preserve_job_records == :on_unhandled_error))
|
620
|
-
if GoodJob.preserve_job_records == true || reenqueued || preserve_unhandled || cron_key.present?
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
update!(job_attributes)
|
625
|
-
end
|
626
|
-
else
|
627
|
-
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!
|
628
531
|
end
|
629
532
|
else
|
630
|
-
|
533
|
+
destroy!
|
631
534
|
end
|
632
535
|
|
633
536
|
result
|
@@ -708,7 +611,7 @@ module GoodJob
|
|
708
611
|
end
|
709
612
|
|
710
613
|
def continue_discard_or_finish_batch
|
711
|
-
batch._continue_discard_or_finish(self) if
|
614
|
+
batch._continue_discard_or_finish(self) if batch.present?
|
712
615
|
end
|
713
616
|
|
714
617
|
def active_job_data
|
@@ -716,7 +619,7 @@ module GoodJob
|
|
716
619
|
.tap do |job_data|
|
717
620
|
job_data["provider_job_id"] = id
|
718
621
|
job_data["good_job_concurrency_key"] = concurrency_key if concurrency_key
|
719
|
-
job_data["good_job_labels"] = Array(labels) if
|
622
|
+
job_data["good_job_labels"] = Array(labels) if labels.present?
|
720
623
|
end
|
721
624
|
end
|
722
625
|
end
|
@@ -14,31 +14,6 @@ module GoodJob # :nodoc:
|
|
14
14
|
|
15
15
|
alias_attribute :performed_at, :created_at
|
16
16
|
|
17
|
-
def self.error_event_migrated?
|
18
|
-
return true if columns_hash["error_event"].present?
|
19
|
-
|
20
|
-
migration_pending_warning!
|
21
|
-
false
|
22
|
-
end
|
23
|
-
|
24
|
-
def self.backtrace_migrated?
|
25
|
-
return true if columns_hash["error_backtrace"].present?
|
26
|
-
|
27
|
-
migration_pending_warning!
|
28
|
-
false
|
29
|
-
end
|
30
|
-
|
31
|
-
def self.duration_interval_migrated?
|
32
|
-
return true if columns_hash["duration"].present?
|
33
|
-
|
34
|
-
migration_pending_warning!
|
35
|
-
false
|
36
|
-
end
|
37
|
-
|
38
|
-
def self.duration_interval_usable?
|
39
|
-
duration_interval_migrated? && Gem::Version.new(Rails.version) >= Gem::Version.new('6.1.0.a')
|
40
|
-
end
|
41
|
-
|
42
17
|
def number
|
43
18
|
serialized_params.fetch('executions', 0) + 1
|
44
19
|
end
|
@@ -50,12 +25,7 @@ module GoodJob # :nodoc:
|
|
50
25
|
|
51
26
|
# Monotonic time between when this job started and finished
|
52
27
|
def runtime_latency
|
53
|
-
|
54
|
-
if self.class.duration_interval_usable?
|
55
|
-
duration
|
56
|
-
elsif performed_at
|
57
|
-
(finished_at || Time.current) - performed_at
|
58
|
-
end
|
28
|
+
duration
|
59
29
|
end
|
60
30
|
|
61
31
|
def last_status_at
|
@@ -1,12 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module GoodJob
|
4
|
-
#
|
5
|
-
#
|
6
|
-
|
7
|
-
class Execution < BaseExecution
|
8
|
-
self.table_name = 'good_jobs'
|
9
|
-
|
10
|
-
belongs_to :job, class_name: 'GoodJob::Job', foreign_key: 'active_job_id', primary_key: 'active_job_id', optional: true, inverse_of: :executions
|
4
|
+
# Created at the time a Job begins executing.
|
5
|
+
# Behavior from +DiscreteExecution+ will be merged into this class.
|
6
|
+
class Execution < DiscreteExecution
|
11
7
|
end
|
12
8
|
end
|