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.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +17 -0
  3. data/README.md +22 -0
  4. data/app/charts/good_job/scheduled_by_queue_chart.rb +2 -2
  5. data/app/controllers/good_job/cron_entries_controller.rb +0 -8
  6. data/app/controllers/good_job/metrics_controller.rb +1 -1
  7. data/app/controllers/good_job/performances_controller.rb +5 -9
  8. data/app/models/concerns/good_job/filterable.rb +1 -1
  9. data/app/models/good_job/base_execution.rb +104 -202
  10. data/app/models/good_job/cron_entry.rb +0 -2
  11. data/app/models/good_job/discrete_execution.rb +1 -31
  12. data/app/models/good_job/execution.rb +3 -7
  13. data/app/models/good_job/job.rb +37 -116
  14. data/app/models/good_job/process.rb +7 -20
  15. data/app/views/good_job/batches/index.html.erb +11 -15
  16. data/app/views/good_job/jobs/_executions.erb +1 -1
  17. data/app/views/good_job/jobs/_table.erb +1 -1
  18. data/app/views/good_job/jobs/show.html.erb +1 -8
  19. data/app/views/good_job/performances/show.html.erb +33 -40
  20. data/config/locales/de.yml +1 -4
  21. data/config/locales/en.yml +1 -4
  22. data/config/locales/es.yml +1 -4
  23. data/config/locales/fr.yml +1 -4
  24. data/config/locales/it.yml +1 -4
  25. data/config/locales/ja.yml +1 -4
  26. data/config/locales/ko.yml +1 -4
  27. data/config/locales/nl.yml +1 -4
  28. data/config/locales/pt-BR.yml +1 -4
  29. data/config/locales/ru.yml +1 -4
  30. data/config/locales/tr.yml +1 -4
  31. data/config/locales/uk.yml +1 -4
  32. data/lib/generators/good_job/templates/update/migrations/01_create_good_jobs.rb.erb +65 -3
  33. data/lib/good_job/active_job_extensions/batches.rb +1 -1
  34. data/lib/good_job/active_job_extensions/concurrency.rb +10 -10
  35. data/lib/good_job/adapter.rb +13 -24
  36. data/lib/good_job/current_thread.rb +6 -6
  37. data/lib/good_job/job_performer.rb +2 -2
  38. data/lib/good_job/log_subscriber.rb +2 -10
  39. data/lib/good_job/notifier.rb +3 -3
  40. data/lib/good_job/version.rb +1 -1
  41. data/lib/good_job.rb +22 -21
  42. metadata +16 -30
  43. data/lib/generators/good_job/templates/update/migrations/02_create_good_job_settings.rb.erb +0 -20
  44. data/lib/generators/good_job/templates/update/migrations/03_create_index_good_jobs_jobs_on_priority_created_at_when_unfinished.rb.erb +0 -19
  45. data/lib/generators/good_job/templates/update/migrations/04_create_good_job_batches.rb.erb +0 -35
  46. data/lib/generators/good_job/templates/update/migrations/05_create_good_job_executions.rb.erb +0 -33
  47. data/lib/generators/good_job/templates/update/migrations/06_create_good_jobs_error_event.rb.erb +0 -16
  48. data/lib/generators/good_job/templates/update/migrations/07_recreate_good_job_cron_indexes_with_conditional.rb.erb +0 -45
  49. data/lib/generators/good_job/templates/update/migrations/08_create_good_job_labels.rb.erb +0 -15
  50. data/lib/generators/good_job/templates/update/migrations/09_create_good_job_labels_index.rb.erb +0 -22
  51. data/lib/generators/good_job/templates/update/migrations/10_remove_good_job_active_id_index.rb.erb +0 -21
  52. data/lib/generators/good_job/templates/update/migrations/11_create_index_good_job_jobs_for_candidate_lookup.rb.erb +0 -19
  53. data/lib/generators/good_job/templates/update/migrations/12_create_good_job_execution_error_backtrace.rb.erb +0 -15
  54. data/lib/generators/good_job/templates/update/migrations/13_create_good_job_process_lock_ids.rb.erb +0 -18
  55. data/lib/generators/good_job/templates/update/migrations/14_create_good_job_process_lock_indexes.rb.erb +0 -38
  56. 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: 5ca44ad0fbf6d55478287f58fb49c9295ebcbbb5875ec8c3c2e94859dec23393
4
- data.tar.gz: ded000fbd60a45e9028a97bda6f1dc0240310a7b9281708d15543331a8a19706
3
+ metadata.gz: 3a4006c920a731f763e36a964e7eb161a292e8a4b34bffa4b60b95b75145c28c
4
+ data.tar.gz: f54b107db6ac1e2304a4ee9e2d34fa65032dc2b1ab0425e0be5ea42d39e5590b
5
5
  SHA512:
6
- metadata.gz: 4d09ea0dcb8801f3ca577b349e9f671bdf70c4df38ace8b96e9b5bda8f22bc2bc4791a261c4816e2beca82aa17f188c9521f89be4460f4097d5e8a03a4f2c281
7
- data.tar.gz: 73ab002472752c47682c508b4828dfd359e9aebebc997b76f69c91f6cf5dd0ab12a4bc171fbc6f3d967e17b2a4b26a52f6afe311d43dbc90331c565a79443e25
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::Execution.pg_or_jdbc_query(<<~SQL.squish))
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::Execution.connection.exec_query(GoodJob::Execution.pg_or_jdbc_query(count_query), "GoodJob Dashboard Chart", binds)
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.migrated? ? GoodJob::BatchRecord.all.size : 0
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
- if GoodJob::DiscreteExecution.duration_interval_migrated?
7
- @performances = GoodJob::DiscreteExecution
8
- .where.not(job_class: nil)
9
- .group(:job_class)
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
- .order("job_class")
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, ''))#{" || to_tsvector('english', COALESCE(array_to_string(labels, ' '), ''))" if labels_migrated?})"
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
- GoodJob::DiscreteExecution.migrated?
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
- self.class.discrete_support? && is_discrete?
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, overrides = {})
308
- new(**enqueue_args(active_job, overrides))
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, overrides = {})
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
- execution_args[:scheduled_at] = Time.zone.at(active_job.scheduled_at) if active_job.scheduled_at
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? && labels_migrated?
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
- reenqueued_current_execution = CurrentThread.active_job_id && CurrentThread.active_job_id == active_job.job_id
338
- current_execution = CurrentThread.execution
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 reenqueued_current_execution
341
- if GoodJob::BatchRecord.migrated?
342
- execution_args[:batch_id] = current_execution.batch_id
343
- execution_args[:batch_callback_id] = current_execution.batch_callback_id
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
- if GoodJob::BatchRecord.migrated?
348
- execution_args[:batch_id] = GoodJob::Batch.current_batch_id
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.merge(overrides)
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
- execution = nil
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 |executions|
371
- execution = executions.first
372
- if execution&.executable?
373
- yield(execution) if block_given?
374
- result = execution.perform(lock_id: lock_id)
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
- execution = nil
314
+ job = nil
377
315
  yield(nil) if block_given?
378
316
  end
379
317
  end
380
318
 
381
- execution&.run_callbacks(:perform_unlocked)
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
- current_execution = CurrentThread.execution
355
+ current_job = CurrentThread.job
418
356
 
419
- retried = current_execution && current_execution.active_job_id == active_job.job_id
357
+ retried = current_job && current_job.active_job_id == active_job.job_id
420
358
  if retried
421
- if current_execution.discrete?
422
- execution = current_execution
423
- execution.assign_attributes(enqueue_args(active_job, { scheduled_at: scheduled_at }))
424
- execution.scheduled_at ||= Time.current
425
- # TODO: these values ideally shouldn't be persisted until the current_execution is finished
426
- # which will require handling `retry_job` being called from outside the execution context.
427
- execution.performed_at = nil
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
- execution = build_for_enqueue(active_job, { scheduled_at: scheduled_at })
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 execution.persisted?
439
- execution.advisory_lock
371
+ if job.persisted?
372
+ job.advisory_lock
440
373
  else
441
- execution.create_with_advisory_lock = true
374
+ job.create_with_advisory_lock = true
442
375
  end
443
376
  end
444
377
 
445
- instrument_payload[:execution] = execution
446
- execution.save!
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 = execution.id
456
- execution
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.execution = self
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
- if discrete?
487
- interrupt_error_string = self.class.format_error(GoodJob::InterruptError.new("Interrupted after starting perform at '#{existing_performed_at}'"))
488
- self.error = interrupt_error_string
489
- self.error_event = ERROR_EVENT_INTERRUPTED if self.class.error_event_migrated?
490
- monotonic_duration = (::Process.clock_gettime(::Process::CLOCK_MONOTONIC) - monotonic_start).seconds
491
-
492
- discrete_execution_attrs = {
493
- error: interrupt_error_string,
494
- finished_at: job_performed_at,
495
- }
496
- discrete_execution_attrs[:error_event] = GoodJob::ErrorEvents::ERROR_EVENT_ENUMS[GoodJob::ErrorEvents::ERROR_EVENT_INTERRUPTED] if self.class.error_event_migrated?
497
- discrete_execution_attrs[:duration] = monotonic_duration if GoodJob::DiscreteExecution.duration_interval_usable?
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
- if discrete?
503
- transaction do
504
- discrete_execution_attrs = {
505
- job_class: job_class,
506
- queue_name: queue_name,
507
- serialized_params: serialized_params,
508
- scheduled_at: (scheduled_at || created_at),
509
- created_at: job_performed_at,
510
- }
511
- discrete_execution_attrs[:process_id] = lock_id if GoodJob::DiscreteExecution.columns_hash.key?("process_id")
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
- update!(execution_attrs)
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", { execution: self, process_id: current_thread.process_id, thread_name: current_thread.thread_name }) do |instrument_payload|
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 if self.class.error_event_migrated?
589
- if discrete_execution
590
- discrete_execution.error = error_string
591
- discrete_execution.error_event = result.error_event
592
- discrete_execution.error_backtrace = job_error.backtrace if discrete_execution.class.backtrace_migrated?
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
- if discrete_execution
604
- discrete_execution.finished_at = job_finished_at
605
- discrete_execution.duration = monotonic_duration if GoodJob::DiscreteExecution.duration_interval_usable?
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
- if discrete_execution
612
- job_attributes[:performed_at] = nil
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
- if discrete_execution
623
- transaction do
624
- discrete_execution.save!
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
- destroy_job
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 GoodJob::BatchRecord.migrated? && batch.present?
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 self.class.labels_migrated? && labels.present?
622
+ job_data["good_job_labels"] = Array(labels) if labels.present?
721
623
  end
722
624
  end
723
625
  end
@@ -72,8 +72,6 @@ module GoodJob # :nodoc:
72
72
  end
73
73
 
74
74
  def enabled?
75
- return true unless GoodJob::Setting.migrated?
76
-
77
75
  GoodJob::Setting.cron_key_enabled?(key, default: enabled_by_default?)
78
76
  end
79
77