good_job 3.99.1 → 4.0.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 +0 -61
- 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 +17 -22
- 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,66 +1,5 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
-
## [v3.99.1](https://github.com/bensheldon/good_job/tree/v3.99.1) (2024-07-10)
|
4
|
-
|
5
|
-
[Full Changelog](https://github.com/bensheldon/good_job/compare/v4.0.3...v3.99.1)
|
6
|
-
|
7
|
-
**Merged pull requests:**
|
8
|
-
|
9
|
-
- Add a little more wording to the v4 "ready to upgrade" instructions [\#1415](https://github.com/bensheldon/good_job/pull/1415) ([bensheldon](https://github.com/bensheldon))
|
10
|
-
|
11
|
-
## [v4.0.3](https://github.com/bensheldon/good_job/tree/v4.0.3) (2024-07-10)
|
12
|
-
|
13
|
-
[Full Changelog](https://github.com/bensheldon/good_job/compare/v4.0.2...v4.0.3)
|
14
|
-
|
15
|
-
**Fixed bugs:**
|
16
|
-
|
17
|
-
- Only set duration attribute to interval on Rails 6.1 [\#1412](https://github.com/bensheldon/good_job/pull/1412) ([bdewater-thatch](https://github.com/bdewater-thatch))
|
18
|
-
|
19
|
-
**Closed issues:**
|
20
|
-
|
21
|
-
- GoodJob error: TypeError: can't cast ActiveSupport::Duration \(on v4.0.0\) [\#1413](https://github.com/bensheldon/good_job/issues/1413)
|
22
|
-
- Question: is it possible to clean a set of scheduled jobs \(by class name\) from console? [\#1410](https://github.com/bensheldon/good_job/issues/1410)
|
23
|
-
- "Couldn't find GoodJob::BatchRecord" error [\#1387](https://github.com/bensheldon/good_job/issues/1387)
|
24
|
-
- Empty alerts "\[\]"? [\#1372](https://github.com/bensheldon/good_job/issues/1372)
|
25
|
-
- Release GoodJob 4.0 [\#764](https://github.com/bensheldon/good_job/issues/764)
|
26
|
-
|
27
|
-
**Merged pull requests:**
|
28
|
-
|
29
|
-
- Add Active Support load hooks for Job and \(Discrete\)Execution; move all outside class definition bodies [\#1414](https://github.com/bensheldon/good_job/pull/1414) ([bensheldon](https://github.com/bensheldon))
|
30
|
-
|
31
|
-
## [v4.0.2](https://github.com/bensheldon/good_job/tree/v4.0.2) (2024-07-08)
|
32
|
-
|
33
|
-
[Full Changelog](https://github.com/bensheldon/good_job/compare/v4.0.1...v4.0.2)
|
34
|
-
|
35
|
-
**Fixed bugs:**
|
36
|
-
|
37
|
-
- Fix a rails 6.1 deprecation warning for the duration attribute [\#1408](https://github.com/bensheldon/good_job/pull/1408) ([Earlopain](https://github.com/Earlopain))
|
38
|
-
- Actually remove deprecated configuration for `cleanup_interval_seconds`, `cleanup_interval_jobs`; remove deprecated `Lockable` [\#1406](https://github.com/bensheldon/good_job/pull/1406) ([bensheldon](https://github.com/bensheldon))
|
39
|
-
|
40
|
-
**Closed issues:**
|
41
|
-
|
42
|
-
- PG::UndefinedColumn: ERROR: column good\_job\_processes.lock\_type does not exist [\#1405](https://github.com/bensheldon/good_job/issues/1405)
|
43
|
-
- undefined method `duration=' for an instance of GoodJob::Execution [\#1404](https://github.com/bensheldon/good_job/issues/1404)
|
44
|
-
- v3 deprecations are still present [\#1399](https://github.com/bensheldon/good_job/issues/1399)
|
45
|
-
|
46
|
-
**Merged pull requests:**
|
47
|
-
|
48
|
-
- Fix Batch integration test to not exhaust database connection thread pool [\#1409](https://github.com/bensheldon/good_job/pull/1409) ([bensheldon](https://github.com/bensheldon))
|
49
|
-
- Discrete cleanup [\#1401](https://github.com/bensheldon/good_job/pull/1401) ([Earlopain](https://github.com/Earlopain))
|
50
|
-
|
51
|
-
## [v4.0.1](https://github.com/bensheldon/good_job/tree/v4.0.1) (2024-07-08)
|
52
|
-
|
53
|
-
[Full Changelog](https://github.com/bensheldon/good_job/compare/v4.0.0...v4.0.1)
|
54
|
-
|
55
|
-
**Fixed bugs:**
|
56
|
-
|
57
|
-
- Actually change `smaller_number_is_higher_priority` for v4 [\#1402](https://github.com/bensheldon/good_job/pull/1402) ([Earlopain](https://github.com/Earlopain))
|
58
|
-
|
59
|
-
**Merged pull requests:**
|
60
|
-
|
61
|
-
- Bump prism/rbi dev dependency [\#1403](https://github.com/bensheldon/good_job/pull/1403) ([Earlopain](https://github.com/Earlopain))
|
62
|
-
- Use index action for the performance controller [\#1398](https://github.com/bensheldon/good_job/pull/1398) ([Earlopain](https://github.com/Earlopain))
|
63
|
-
|
64
3
|
## [v4.0.0](https://github.com/bensheldon/good_job/tree/v4.0.0) (2024-07-07)
|
65
4
|
|
66
5
|
[Full Changelog](https://github.com/bensheldon/good_job/compare/v3.99.0...v4.0.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
|