good_job 3.15.3 → 3.15.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 85c6903e5f8838cb72f4687e364fa20cf9c461f0c4b79412bb8b61549bbf6333
4
- data.tar.gz: d00fecc5a4fe561bc1a7f21a79e46245cb5ab603fafbbb4369344395c9c0fac0
3
+ metadata.gz: ee08a99ae7d26df57e3f0a7a64b737e987041815805117ac6d4e6119df229932
4
+ data.tar.gz: fd747fed38d2227bde7898438b6d597d55c5cb72f6f7125171b5637aff168d5a
5
5
  SHA512:
6
- metadata.gz: a1116f1e94e07b62bc316a3ec76a1b0768d22b5a511a65cf18e40bc13d4e8c25290c7c879d5cdfea1f1a988e3809b130462b57bd25a7c2405b0152cbe9434f51
7
- data.tar.gz: 227c89386f14a783260fff68c55f0f4b0d10db331ab705b922f02fdce79a8ec4cf0f969e554f02e55f030dbcda6efbd9439bcaafd7bc471028c35375a7b58ccf
6
+ metadata.gz: e32e66f000d6d7cd0a84fd6ab5a7bdf08f2a5f996bd5533ffc11f68ba33ee96e9d4ddc90a64dea1dbfcad43b07b75fcb34ed66c38546cda52b2cd5c0da90a329
7
+ data.tar.gz: 8f2a8c72500ee21f634ae6436d12ddc0641e730281b8b9534189f5892b795c6dc3a214e30e4a348878285a8912342dea3fbc74a6ab615a4de5b38f025767db3f
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## [v3.15.4](https://github.com/bensheldon/good_job/tree/v3.15.4) (2023-04-22)
4
+
5
+ [Full Changelog](https://github.com/bensheldon/good_job/compare/v3.15.3...v3.15.4)
6
+
7
+ **Merged pull requests:**
8
+
9
+ - Create "discrete" `good_job_executions` table to separate Job records from Execution records and have a 1-to-1 correspondence between `good_jobs` records and Active Job jobs [\#928](https://github.com/bensheldon/good_job/pull/928) ([bensheldon](https://github.com/bensheldon))
10
+
3
11
  ## [v3.15.3](https://github.com/bensheldon/good_job/tree/v3.15.3) (2023-04-22)
4
12
 
5
13
  [Full Changelog](https://github.com/bensheldon/good_job/compare/v3.15.2...v3.15.3)
@@ -33,12 +33,25 @@ module GoodJob
33
33
  def coalesce_scheduled_at_created_at
34
34
  arel_table.coalesce(arel_table['scheduled_at'], arel_table['created_at'])
35
35
  end
36
+
37
+ def discrete_support?
38
+ if connection.table_exists?('good_job_executions')
39
+ true
40
+ else
41
+ migration_pending_warning!
42
+ false
43
+ end
44
+ end
36
45
  end
37
46
 
38
47
  # The ActiveJob job class, as a string
39
48
  # @return [String]
40
49
  def job_class
41
- serialized_params['job_class']
50
+ discrete? ? attributes['job_class'] : serialized_params['job_class']
51
+ end
52
+
53
+ def discrete?
54
+ self.class.discrete_support? && is_discrete?
42
55
  end
43
56
  end
44
57
  end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GoodJob # :nodoc:
4
+ class DiscreteExecution < BaseRecord
5
+ self.table_name = 'good_job_executions'
6
+
7
+ belongs_to :execution, class_name: 'GoodJob::Execution', foreign_key: 'active_job_id', primary_key: 'active_job_id', inverse_of: :discrete_executions, optional: true
8
+ belongs_to :job, class_name: 'GoodJob::Job', foreign_key: 'active_job_id', primary_key: 'active_job_id', inverse_of: :discrete_executions, optional: true
9
+
10
+ scope :finished, -> { where.not(finished_at: nil) }
11
+
12
+ alias_attribute :performed_at, :created_at
13
+
14
+ def number
15
+ serialized_params.fetch('executions', 0) + 1
16
+ end
17
+
18
+ # Time between when this job was expected to run and when it started running
19
+ def queue_latency
20
+ created_at - scheduled_at
21
+ end
22
+
23
+ # Time between when this job started and finished
24
+ def runtime_latency
25
+ (finished_at || Time.current) - performed_at if performed_at
26
+ end
27
+
28
+ def last_status_at
29
+ finished_at || created_at
30
+ end
31
+
32
+ def status
33
+ if finished_at.present?
34
+ if error.present?
35
+ :retried
36
+ elsif error.present? && job.finished_at.present?
37
+ :discarded
38
+ else
39
+ :succeeded
40
+ end
41
+ else
42
+ :running
43
+ end
44
+ end
45
+
46
+ def display_serialized_params
47
+ serialized_params.merge({
48
+ _good_job_execution: attributes.except('serialized_params'),
49
+ })
50
+ end
51
+ end
52
+ end
@@ -70,9 +70,13 @@ module GoodJob
70
70
  end
71
71
 
72
72
  belongs_to :batch, class_name: 'GoodJob::BatchRecord', optional: true, inverse_of: :executions
73
-
74
73
  belongs_to :job, class_name: 'GoodJob::Job', foreign_key: 'active_job_id', primary_key: 'active_job_id', optional: true, inverse_of: :executions
75
- after_destroy -> { self.class.active_job_id(active_job_id).delete_all }, if: -> { @_destroy_job }
74
+ 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
75
+
76
+ after_destroy lambda {
77
+ GoodJob::DiscreteExecution.where(active_job_id: active_job_id).delete_all if discrete? # TODO: move into association `dependent: :delete_all` after v4
78
+ self.class.active_job_id(active_job_id).delete_all
79
+ }, if: -> { @_destroy_job }
76
80
 
77
81
  # Get executions with given ActiveJob ID
78
82
  # @!method active_job_id
@@ -201,8 +205,12 @@ module GoodJob
201
205
  end
202
206
  end)
203
207
 
204
- # Construct a GoodJob::Execution from an ActiveJob instance.
205
208
  def self.build_for_enqueue(active_job, overrides = {})
209
+ new(**enqueue_args(active_job, overrides))
210
+ end
211
+
212
+ # Construct arguments for GoodJob::Execution from an ActiveJob instance.
213
+ def self.enqueue_args(active_job, overrides = {})
206
214
  if active_job.priority && GoodJob.configuration.smaller_number_is_higher_priority.nil?
207
215
  ActiveSupport::Deprecation.warn(<<~DEPRECATION)
208
216
  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.
@@ -218,6 +226,7 @@ module GoodJob
218
226
  serialized_params: active_job.serialize,
219
227
  scheduled_at: active_job.scheduled_at,
220
228
  }
229
+
221
230
  execution_args[:concurrency_key] = active_job.good_job_concurrency_key if active_job.respond_to?(:good_job_concurrency_key)
222
231
 
223
232
  reenqueued_current_execution = CurrentThread.active_job_id && CurrentThread.active_job_id == active_job.job_id
@@ -238,7 +247,7 @@ module GoodJob
238
247
  execution_args[:cron_at] = CurrentThread.cron_at
239
248
  end
240
249
 
241
- new(**execution_args.merge(overrides))
250
+ execution_args.merge(overrides)
242
251
  end
243
252
 
244
253
  # Finds the next eligible Execution, acquire an advisory lock related to it, and
@@ -298,19 +307,47 @@ module GoodJob
298
307
  # The new {Execution} instance representing the queued ActiveJob job.
299
308
  def self.enqueue(active_job, scheduled_at: nil, create_with_advisory_lock: false)
300
309
  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|
301
- execution = build_for_enqueue(active_job, { scheduled_at: scheduled_at })
310
+ current_execution = CurrentThread.execution
311
+
312
+ retried = current_execution && current_execution.active_job_id == active_job.job_id
313
+ if retried
314
+ if current_execution.discrete?
315
+ execution = current_execution
316
+ execution.assign_attributes(enqueue_args(active_job, { scheduled_at: scheduled_at }))
317
+ execution.scheduled_at ||= Time.current
318
+ execution.performed_at = nil
319
+ execution.finished_at = nil
320
+ else
321
+ execution = build_for_enqueue(active_job, { scheduled_at: scheduled_at })
322
+ end
323
+ else
324
+ execution = build_for_enqueue(active_job, { scheduled_at: scheduled_at })
325
+ execution.make_discrete if discrete_support?
326
+ end
302
327
 
303
- execution.create_with_advisory_lock = create_with_advisory_lock
304
- instrument_payload[:execution] = execution
328
+ if create_with_advisory_lock
329
+ if execution.persisted?
330
+ execution.advisory_lock
331
+ else
332
+ execution.create_with_advisory_lock = true
333
+ end
334
+ end
305
335
 
336
+ instrument_payload[:execution] = execution
306
337
  execution.save!
307
- active_job.provider_job_id = execution.id
308
- CurrentThread.execution.retried_good_job_id = execution.id if CurrentThread.active_job_id && CurrentThread.active_job_id == active_job.job_id
309
338
 
339
+ CurrentThread.execution.retried_good_job_id = execution.id if retried && !CurrentThread.execution.discrete?
340
+ active_job.provider_job_id = execution.id
310
341
  execution
311
342
  end
312
343
  end
313
344
 
345
+ def self.format_error(error)
346
+ raise ArgumentError unless error.is_a?(Exception)
347
+
348
+ [error.class.to_s, ERROR_MESSAGE_SEPARATOR, error.message].join
349
+ end
350
+
314
351
  # Execute the ActiveJob job this {Execution} represents.
315
352
  # @return [ExecutionResult]
316
353
  # An array of the return value of the job's +#perform+ method and the
@@ -320,12 +357,39 @@ module GoodJob
320
357
  run_callbacks(:perform) do
321
358
  raise PreviouslyPerformedError, 'Cannot perform a job that has already been performed' if finished_at
322
359
 
360
+ discrete_execution = nil
323
361
  result = GoodJob::CurrentThread.within do |current_thread|
324
362
  current_thread.reset
325
363
  current_thread.execution = self
326
364
 
327
- current_thread.execution_interrupted = performed_at if performed_at
328
- update!(performed_at: Time.current)
365
+ if performed_at
366
+ current_thread.execution_interrupted = performed_at
367
+
368
+ if discrete?
369
+ interrupt_error_string = self.class.format_error(GoodJob::InterruptError.new("Interrupted after starting perform at '#{performed_at}'"))
370
+ self.error = interrupt_error_string
371
+ discrete_executions.where(finished_at: nil).where.not(performed_at: nil).update_all( # rubocop:disable Rails/SkipsModelValidations
372
+ error: interrupt_error_string,
373
+ finished_at: Time.current
374
+ )
375
+ end
376
+ end
377
+
378
+ if discrete?
379
+ transaction do
380
+ now = Time.current
381
+ discrete_execution = discrete_executions.create!(
382
+ job_class: job_class,
383
+ queue_name: queue_name,
384
+ serialized_params: serialized_params,
385
+ scheduled_at: (scheduled_at || created_at),
386
+ created_at: now
387
+ )
388
+ update!(performed_at: now, executions_count: ((executions_count || 0) + 1))
389
+ end
390
+ else
391
+ update!(performed_at: Time.current)
392
+ end
329
393
 
330
394
  ActiveSupport::Notifications.instrument("perform_job.good_job", { execution: self, process_id: current_thread.process_id, thread_name: current_thread.thread_name }) do |instrument_payload|
331
395
  value = ActiveJob::Base.execute(active_job_data)
@@ -349,14 +413,42 @@ module GoodJob
349
413
  end
350
414
 
351
415
  job_error = result.handled_error || result.unhandled_error
352
- self.error = [job_error.class, ERROR_MESSAGE_SEPARATOR, job_error.message].join if job_error
416
+
417
+ if job_error
418
+ error_string = self.class.format_error(job_error)
419
+ self.error = error_string
420
+ discrete_execution.error = error_string if discrete_execution
421
+ else
422
+ self.error = nil
423
+ end
353
424
 
354
425
  reenqueued = result.retried? || retried_good_job_id.present?
355
426
  if result.unhandled_error && GoodJob.retry_on_unhandled_error
356
- save!
427
+ if discrete_execution
428
+ transaction do
429
+ discrete_execution.update!(finished_at: Time.current)
430
+ update!(performed_at: nil, finished_at: nil, retried_good_job_id: nil)
431
+ end
432
+ else
433
+ save!
434
+ end
357
435
  elsif GoodJob.preserve_job_records == true || reenqueued || (result.unhandled_error && GoodJob.preserve_job_records == :on_unhandled_error) || cron_key.present?
358
- self.finished_at = Time.current
359
- save!
436
+ now = Time.current
437
+ if discrete_execution
438
+ if reenqueued
439
+ self.performed_at = nil
440
+ else
441
+ self.finished_at = now
442
+ end
443
+ discrete_execution.finished_at = now
444
+ transaction do
445
+ discrete_execution.save!
446
+ save!
447
+ end
448
+ else
449
+ self.finished_at = now
450
+ save!
451
+ end
360
452
  else
361
453
  destroy_job
362
454
  end
@@ -371,6 +463,17 @@ module GoodJob
371
463
  self.class.unscoped.unfinished.owns_advisory_locked.exists?(id: id)
372
464
  end
373
465
 
466
+ def make_discrete
467
+ self.is_discrete = true
468
+ self.id = active_job_id
469
+ self.job_class = serialized_params['job_class']
470
+ self.executions_count ||= 0
471
+
472
+ current_time = Time.current
473
+ self.created_at ||= current_time
474
+ self.scheduled_at ||= current_time
475
+ end
476
+
374
477
  # Build an ActiveJob instance and deserialize the arguments, using `#active_job_data`.
375
478
  #
376
479
  # @param ignore_deserialization_errors [Boolean]
@@ -30,6 +30,11 @@ module GoodJob
30
30
 
31
31
  belongs_to :batch, class_name: 'GoodJob::BatchRecord', inverse_of: :jobs, optional: true
32
32
  has_many :executions, -> { order(created_at: :asc) }, class_name: 'GoodJob::Execution', foreign_key: 'active_job_id', inverse_of: :job # rubocop:disable Rails/HasManyOrHasOneDependent
33
+ has_many :discrete_executions, -> { order(created_at: :asc) }, class_name: 'GoodJob::DiscreteExecution', foreign_key: 'active_job_id', primary_key: :active_job_id, inverse_of: :job # rubocop:disable Rails/HasManyOrHasOneDependent
34
+
35
+ after_destroy lambda {
36
+ GoodJob::DiscreteExecution.where(active_job_id: active_job_id).delete_all if discrete? # TODO: move into association `dependent: :delete_all` after v4
37
+ }
33
38
 
34
39
  # Only the most-recent unretried execution represents a "Job"
35
40
  default_scope { where(retried_good_job_id: nil) }
@@ -56,6 +61,8 @@ module GoodJob
56
61
  # Errored but will not be retried
57
62
  scope :discarded, -> { finished.where.not(error: nil) }
58
63
 
64
+ scope :unfinished_undiscrete, -> { where(finished_at: nil, retried_good_job_id: nil, is_discrete: [nil, false]) }
65
+
59
66
  # The job's ActiveJob UUID
60
67
  # @return [String]
61
68
  def id
@@ -191,9 +198,10 @@ module GoodJob
191
198
 
192
199
  execution.class.transaction(joinable: false, requires_new: true) do
193
200
  new_active_job = active_job.retry_job(wait: 0, error: execution.error)
194
- execution.save
201
+ execution.save!
195
202
  end
196
203
  end
204
+
197
205
  new_active_job
198
206
  end
199
207
  end
@@ -213,7 +221,7 @@ module GoodJob
213
221
  update_execution = proc do
214
222
  execution.update(
215
223
  finished_at: Time.current,
216
- error: [job_error.class, GoodJob::Execution::ERROR_MESSAGE_SEPARATOR, job_error.message].join
224
+ error: GoodJob::Execution.format_error(job_error)
217
225
  )
218
226
  end
219
227
 
@@ -3,7 +3,12 @@
3
3
  <nav aria-label="breadcrumb">
4
4
  <ol class="breadcrumb small mb-0">
5
5
  <li class="breadcrumb-item"><%= link_to t(".jobs"), jobs_path %></li>
6
- <li class="breadcrumb-item active" aria-current="page"><%= tag.code @job.id, class: "text-muted" %></li>
6
+ <li class="breadcrumb-item active" aria-current="page">
7
+ <%= tag.code @job.id, class: "text-muted" %>
8
+ <% if @job.discrete? %>
9
+ <span class="badge bg-info text-dark">Discrete</span>
10
+ <% end %>
11
+ </li>
7
12
  </ol>
8
13
  </nav>
9
14
  <div class="row align-items-center">
@@ -21,6 +26,10 @@
21
26
  <div class="font-monospace fw-bold small my-2"><%= tag.strong @job.priority %></div>
22
27
  </div>
23
28
  <div class="col text-end">
29
+ <div class="mb-2">
30
+ <%= tag.span relative_time(@job.last_status_at), class: "small" %>
31
+ <%= status_badge @job.status %>
32
+ </div>
24
33
  <% if @job.status.in? [:scheduled, :retried, :queued] %>
25
34
  <%= button_to reschedule_job_path(@job.id), method: :put,
26
35
  class: "btn btn-sm btn-outline-primary",
@@ -59,8 +68,25 @@
59
68
  </div>
60
69
 
61
70
  <div class="my-4">
62
- <h5><%= t "good_job.models.job.arguments" %></h5>
63
- <%= tag.pre @job.serialized_params["arguments"].map(&:inspect).join(', '), class: 'text-wrap text-break' %>
71
+ <h5>
72
+ <%= t "good_job.models.job.arguments" %>
73
+ <%= tag.button type: "button", class: "btn btn-sm text-muted", role: "button",
74
+ title: t("good_job.actions.inspect"),
75
+ data: { bs_toggle: "collapse", bs_target: "##{dom_id(@job, 'params')}" },
76
+ aria: { expanded: false, controls: dom_id(@job, "params") } do %>
77
+ <%= render_icon "info" %>
78
+ <span class="visually-hidden"><%= t "good_job.actions.inspect" %></span>
79
+ <% end %>
80
+ </h5>
64
81
  </div>
82
+ <%= tag.pre @job.serialized_params["arguments"].map(&:inspect).join(', '), class: 'text-wrap text-break' %>
83
+
84
+ <%= tag.div id: dom_id(@job, "params"), class: "list-group-item collapse small bg-dark text-light" do %>
85
+ <%= tag.pre JSON.pretty_generate(@job.display_serialized_params) %>
86
+ <% end %>
65
87
 
66
- <%= render 'executions', executions: @job.executions.includes_advisory_locks.reverse %>
88
+ <% if @job.discrete? %>
89
+ <%= render 'executions', executions: @job.discrete_executions.reverse %>
90
+ <% else %>
91
+ <%= render 'executions', executions: @job.executions.includes_advisory_locks.reverse %>
92
+ <% end %>
@@ -22,6 +22,10 @@ class CreateGoodJobs < ActiveRecord::Migration<%= migration_version %>
22
22
 
23
23
  t.uuid :batch_id
24
24
  t.uuid :batch_callback_id
25
+
26
+ t.boolean :is_discrete
27
+ t.integer :executions_count
28
+ t.text :job_class
25
29
  end
26
30
 
27
31
  create_table :good_job_batches, id: :uuid do |t|
@@ -38,6 +42,18 @@ class CreateGoodJobs < ActiveRecord::Migration<%= migration_version %>
38
42
  t.datetime :finished_at
39
43
  end
40
44
 
45
+ create_table :good_job_executions, id: :uuid do |t|
46
+ t.timestamps
47
+
48
+ t.uuid :active_job_id, null: false
49
+ t.text :job_class
50
+ t.text :queue_name
51
+ t.jsonb :serialized_params
52
+ t.datetime :scheduled_at
53
+ t.datetime :finished_at
54
+ t.text :error
55
+ end
56
+
41
57
  create_table :good_job_processes, id: :uuid do |t|
42
58
  t.timestamps
43
59
  t.jsonb :state
@@ -62,5 +78,7 @@ class CreateGoodJobs < ActiveRecord::Migration<%= migration_version %>
62
78
  where: "finished_at IS NULL", name: :index_good_jobs_jobs_on_priority_created_at_when_unfinished
63
79
  add_index :good_jobs, [:batch_id], where: "batch_id IS NOT NULL"
64
80
  add_index :good_jobs, [:batch_callback_id], where: "batch_callback_id IS NOT NULL"
81
+
82
+ add_index :good_job_executions, [:active_job_id, :created_at], name: :index_good_job_executions_on_active_job_id_and_created_at
65
83
  end
66
84
  end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+ class CreateGoodJobExecutions < ActiveRecord::Migration<%= migration_version %>
3
+ def change
4
+ reversible do |dir|
5
+ dir.up do
6
+ # Ensure this incremental update migration is idempotent
7
+ # with monolithic install migration.
8
+ return if connection.table_exists?(:good_job_executions)
9
+ end
10
+ end
11
+
12
+ create_table :good_job_executions, id: :uuid do |t|
13
+ t.timestamps
14
+
15
+ t.uuid :active_job_id, null: false
16
+ t.text :job_class
17
+ t.text :queue_name
18
+ t.jsonb :serialized_params
19
+ t.datetime :scheduled_at
20
+ t.datetime :finished_at
21
+ t.text :error
22
+
23
+ t.index [:active_job_id, :created_at], name: :index_good_job_executions_on_active_job_id_and_created_at
24
+ end
25
+
26
+ change_table :good_jobs do |t|
27
+ t.boolean :is_discrete
28
+ t.integer :executions_count
29
+ t.text :job_class
30
+ end
31
+ end
32
+ end
@@ -50,11 +50,15 @@ module GoodJob
50
50
 
51
51
  current_time = Time.current
52
52
  executions = active_jobs.map do |active_job|
53
- GoodJob::Execution.build_for_enqueue(active_job, {
54
- id: SecureRandom.uuid,
55
- created_at: current_time,
56
- updated_at: current_time,
57
- })
53
+ GoodJob::Execution.build_for_enqueue(active_job).tap do |execution|
54
+ if GoodJob::Execution.discrete_support?
55
+ execution.make_discrete
56
+ execution.scheduled_at = current_time if execution.scheduled_at == execution.created_at
57
+ end
58
+
59
+ execution.created_at = current_time
60
+ execution.updated_at = current_time
61
+ end
58
62
  end
59
63
 
60
64
  inline_executions = []
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  module GoodJob
3
3
  # GoodJob gem version.
4
- VERSION = '3.15.3'
4
+ VERSION = '3.15.4'
5
5
  end
data/lib/good_job.rb CHANGED
@@ -170,14 +170,19 @@ module GoodJob
170
170
  ActiveSupport::Notifications.instrument("cleanup_preserved_jobs.good_job", { older_than: older_than, timestamp: timestamp }) do |payload|
171
171
  deleted_executions_count = 0
172
172
  deleted_batches_count = 0
173
+ deleted_discrete_executions_count = 0
173
174
 
174
175
  jobs_query = GoodJob::Job.where('finished_at <= ?', timestamp).order(finished_at: :asc).limit(in_batches_of)
175
176
  jobs_query = jobs_query.succeeded unless include_discarded
176
177
  loop do
177
- deleted = GoodJob::Execution.where(job: jobs_query).delete_all
178
- break if deleted.zero?
178
+ active_job_ids = jobs_query.pluck(:active_job_id)
179
+ break if active_job_ids.empty?
179
180
 
180
- deleted_executions_count += deleted
181
+ deleted_discrete_executions = GoodJob::DiscreteExecution.where(active_job_id: active_job_ids).delete_all
182
+ deleted_discrete_executions_count += deleted_discrete_executions
183
+
184
+ deleted_executions = GoodJob::Execution.where(active_job_id: active_job_ids).delete_all
185
+ deleted_executions_count += deleted_executions
181
186
  end
182
187
 
183
188
  if GoodJob::BatchRecord.migrated?
@@ -191,9 +196,14 @@ module GoodJob
191
196
  end
192
197
  end
193
198
 
194
- payload[:destroyed_executions_count] = deleted_executions_count
195
199
  payload[:destroyed_batches_count] = deleted_batches_count
196
- payload[:destroyed_records_count] = deleted_executions_count + deleted_batches_count
200
+ payload[:destroyed_discrete_executions_count] = deleted_discrete_executions_count
201
+ payload[:destroyed_executions_count] = deleted_executions_count
202
+
203
+ destroyed_records_count = deleted_batches_count + deleted_discrete_executions_count + deleted_executions_count
204
+ payload[:destroyed_records_count] = destroyed_records_count
205
+
206
+ destroyed_records_count
197
207
  end
198
208
  end
199
209
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: good_job
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.15.3
4
+ version: 3.15.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Sheldon
@@ -350,6 +350,7 @@ files:
350
350
  - app/models/good_job/batch.rb
351
351
  - app/models/good_job/batch_record.rb
352
352
  - app/models/good_job/cron_entry.rb
353
+ - app/models/good_job/discrete_execution.rb
353
354
  - app/models/good_job/execution.rb
354
355
  - app/models/good_job/execution_result.rb
355
356
  - app/models/good_job/job.rb
@@ -401,6 +402,7 @@ files:
401
402
  - lib/generators/good_job/templates/update/migrations/02_create_good_job_settings.rb.erb
402
403
  - lib/generators/good_job/templates/update/migrations/03_create_index_good_jobs_jobs_on_priority_created_at_when_unfinished.rb.erb
403
404
  - lib/generators/good_job/templates/update/migrations/04_create_good_job_batches.rb.erb
405
+ - lib/generators/good_job/templates/update/migrations/05_create_good_job_executions.rb.erb
404
406
  - lib/generators/good_job/update_generator.rb
405
407
  - lib/good_job.rb
406
408
  - lib/good_job/active_job_extensions/batches.rb