good_job 3.11.1 → 3.12.0

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: 65cb545f93b489c6ee487cbd07924e7a9d4ad2df7d7039391c66f704b38a767d
4
- data.tar.gz: 6495c9a4b8da725051e44eb05e0b4c35b6aaa7363075ebb540316ec5a7d0b1b0
3
+ metadata.gz: eeb15f65ff801785a0df591dec95a0e044a4f5d7e61e6b2483003c4b2afd8cb3
4
+ data.tar.gz: e5c631765c1303d1fee0597313bbcb5d3844b85ad49600f2609c83149885872d
5
5
  SHA512:
6
- metadata.gz: 698fbb528da9f58d766ccf47880de2e270f30e2f2226e23adafc00ea7de8920194773fd1769eb40aaff96b2ff3d666c20b080f2ddda801e0979817714bcc93b6
7
- data.tar.gz: 42b54be4f42c185531a490d7eae1bc26701c341c1d708167f385505141f62cd860119f52fc32a2a1dab0c6f94817518af51cb629cdff26f5cabfff3a1c61227e
6
+ metadata.gz: bf0ed8436074ca82d718b64a76b4b1ce36f4bc2a085fe765b04eb101ae7886d0823bb50faa7816fd3a0dcb56d308dc568a7cdd86373d0afcb49bc2d22c045105
7
+ data.tar.gz: 8bd8e67ebfcb2b90a7e1ce6a542cf659e8d4cd37f31782a3a3cd5d26cfedba1e85e9a57835a8a9e78cd731f304b055c21802ca1c68fc2fcffb32ec9b3050ebdf
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## [v3.12.0](https://github.com/bensheldon/good_job/tree/v3.12.0) (2023-02-07)
4
+
5
+ [Full Changelog](https://github.com/bensheldon/good_job/compare/v3.11.1...v3.12.0)
6
+
7
+ **Merged pull requests:**
8
+
9
+ - Create `InterruptErrors` extension to raise an exception when an interrupted job is retried [\#830](https://github.com/bensheldon/good_job/pull/830) ([bensheldon](https://github.com/bensheldon))
10
+
3
11
  ## [v3.11.1](https://github.com/bensheldon/good_job/tree/v3.11.1) (2023-02-06)
4
12
 
5
13
  [Full Changelog](https://github.com/bensheldon/good_job/compare/v3.11.0...v3.11.1)
data/README.md CHANGED
@@ -55,6 +55,7 @@ For more of the story of GoodJob, read the [introductory blog post](https://isla
55
55
  - [Exceptions](#exceptions)
56
56
  - [Retries](#retries)
57
57
  - [ActionMailer retries](#actionmailer-retries)
58
+ - [Interrupts](#interrupts)
58
59
  - [Timeouts](#timeouts)
59
60
  - [Optimize queues, threads, and processes](#optimize-queues-threads-and-processes)
60
61
  - [Database connections](#database-connections)
@@ -829,6 +830,23 @@ end
829
830
  Note, that `ActionMailer::MailDeliveryJob` is a default since Rails 6.0. Be sure that your app is using that class, as it
830
831
  might also be configured to use (deprecated now) `ActionMailer::DeliveryJob`.
831
832
 
833
+ ### Interrupts
834
+
835
+ Jobs will be automatically retried if the process is interrupted while performing a job, for example as the result of a `SIGKILL` or power failure.
836
+
837
+ If you need more control over interrupt-caused retries, include the `GoodJob::ActiveJobExtensions::InterruptErrors` extension in your job closs. When an interrupted job is retried, the extension will raise a `GoodJob::InterruptError` exception within the job, which allows you to use ActiveJob's `retry_on` and `discard_on` to control the behavior of the job.
838
+
839
+ ```ruby
840
+ class MyJob < ApplicationJob
841
+ # The extension must be included before other extensions
842
+ include GoodJob::ActiveJobExtensions::InterruptErrors
843
+ # Discard the job if it is interrupted
844
+ discard_on InterruptError
845
+ # Retry the job if it is interrupted
846
+ retry_on InterruptError, wait: 0, attempts: Float::INFINITY
847
+ end
848
+ ```
849
+
832
850
  ### Timeouts
833
851
 
834
852
  Job timeouts can be configured with an `around_perform`:
@@ -315,10 +315,27 @@ module GoodJob
315
315
  run_callbacks(:perform) do
316
316
  raise PreviouslyPerformedError, 'Cannot perform a job that has already been performed' if finished_at
317
317
 
318
- self.performed_at = Time.current
319
- save! if GoodJob.preserve_job_records
318
+ result = GoodJob::CurrentThread.within do |current_thread|
319
+ current_thread.reset
320
+ current_thread.execution = self
320
321
 
321
- result = execute
322
+ current_thread.execution_interrupted = performed_at if performed_at
323
+ update!(performed_at: Time.current)
324
+
325
+ ActiveSupport::Notifications.instrument("perform_job.good_job", { execution: self, process_id: current_thread.process_id, thread_name: current_thread.thread_name }) do
326
+ value = ActiveJob::Base.execute(active_job_data)
327
+
328
+ if value.is_a?(Exception)
329
+ handled_error = value
330
+ value = nil
331
+ end
332
+ handled_error ||= current_thread.error_on_retry || current_thread.error_on_discard
333
+
334
+ ExecutionResult.new(value: value, handled_error: handled_error, retried: current_thread.error_on_retry.present?)
335
+ rescue StandardError => e
336
+ ExecutionResult.new(value: nil, unhandled_error: e)
337
+ end
338
+ end
322
339
 
323
340
  job_error = result.handled_error || result.unhandled_error
324
341
  self.error = [job_error.class, ERROR_MESSAGE_SEPARATOR, job_error.message].join if job_error
@@ -408,28 +425,6 @@ module GoodJob
408
425
  end
409
426
  end
410
427
 
411
- # @return [ExecutionResult]
412
- def execute
413
- GoodJob::CurrentThread.within do |current_thread|
414
- current_thread.reset
415
- current_thread.execution = self
416
-
417
- ActiveSupport::Notifications.instrument("perform_job.good_job", { execution: self, process_id: current_thread.process_id, thread_name: current_thread.thread_name }) do
418
- value = ActiveJob::Base.execute(active_job_data)
419
-
420
- if value.is_a?(Exception)
421
- handled_error = value
422
- value = nil
423
- end
424
- handled_error ||= current_thread.error_on_retry || current_thread.error_on_discard
425
-
426
- ExecutionResult.new(value: value, handled_error: handled_error, retried: current_thread.error_on_retry.present?)
427
- rescue StandardError => e
428
- ExecutionResult.new(value: nil, unhandled_error: e)
429
- end
430
- end
431
- end
432
-
433
428
  def reset_batch_values(&block)
434
429
  GoodJob::Batch.within_thread(batch_id: nil, batch_callback_id: nil, &block)
435
430
  end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+ module GoodJob
3
+ module ActiveJobExtensions
4
+ module InterruptErrors
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ around_perform do |_job, block|
9
+ raise InterruptError, "Interrupted after starting perform at '#{CurrentThread.execution_interrupted}'" if CurrentThread.execution_interrupted.present?
10
+
11
+ block.call
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -12,6 +12,7 @@ module GoodJob
12
12
  error_on_discard
13
13
  error_on_retry
14
14
  execution
15
+ execution_interrupted
15
16
  ].freeze
16
17
 
17
18
  # @!attribute [rw] cron_at
@@ -44,6 +45,12 @@ module GoodJob
44
45
  # @return [GoodJob::Execution, nil]
45
46
  thread_mattr_accessor :execution
46
47
 
48
+ # @!attribute [rw] execution_interrupted
49
+ # @!scope class
50
+ # Execution Interrupted
51
+ # @return [Boolean, nil]
52
+ thread_mattr_accessor :execution_interrupted
53
+
47
54
  # Resets attributes
48
55
  # @param [Hash] values to assign
49
56
  # @return [void]
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+ module GoodJob
3
+ # Exception raised when a job is interrupted by a SIGKILL or power failure.
4
+ class InterruptError < StandardError
5
+ end
6
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  module GoodJob
3
3
  # GoodJob gem version.
4
- VERSION = '3.11.1'
4
+ VERSION = '3.12.0'
5
5
  end
data/lib/good_job.rb CHANGED
@@ -9,6 +9,8 @@ require "good_job/adapter"
9
9
  require "active_job/queue_adapters/good_job_adapter"
10
10
  require "good_job/active_job_extensions/batches"
11
11
  require "good_job/active_job_extensions/concurrency"
12
+ require "good_job/interrupt_error"
13
+ require "good_job/active_job_extensions/interrupt_errors"
12
14
  require "good_job/active_job_extensions/notify_options"
13
15
 
14
16
  require "good_job/assignable_connection"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: good_job
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.11.1
4
+ version: 3.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Sheldon
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-02-06 00:00:00.000000000 Z
11
+ date: 2023-02-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activejob
@@ -403,6 +403,7 @@ files:
403
403
  - lib/good_job.rb
404
404
  - lib/good_job/active_job_extensions/batches.rb
405
405
  - lib/good_job/active_job_extensions/concurrency.rb
406
+ - lib/good_job/active_job_extensions/interrupt_errors.rb
406
407
  - lib/good_job/active_job_extensions/notify_options.rb
407
408
  - lib/good_job/adapter.rb
408
409
  - lib/good_job/assignable_connection.rb
@@ -415,6 +416,7 @@ files:
415
416
  - lib/good_job/daemon.rb
416
417
  - lib/good_job/dependencies.rb
417
418
  - lib/good_job/engine.rb
419
+ - lib/good_job/interrupt_error.rb
418
420
  - lib/good_job/job_performer.rb
419
421
  - lib/good_job/log_subscriber.rb
420
422
  - lib/good_job/multi_scheduler.rb