aboutsummaryrefslogtreecommitdiffstats
path: root/activejob/lib/active_job/exceptions.rb
diff options
context:
space:
mode:
authorDavid Heinemeier Hansson <david@loudthinking.com>2016-07-29 13:54:55 -0700
committerDavid Heinemeier Hansson <david@loudthinking.com>2016-07-29 13:54:55 -0700
commit8b5c04e423cbfd90a15ed6fbab3f111803bc6544 (patch)
treee96cb1cbcf2a7732c97a01a345dc384321505f92 /activejob/lib/active_job/exceptions.rb
parent3916656f8e9700eb5f1cfc441ff66e1f12173683 (diff)
downloadrails-8b5c04e423cbfd90a15ed6fbab3f111803bc6544.tar.gz
rails-8b5c04e423cbfd90a15ed6fbab3f111803bc6544.tar.bz2
rails-8b5c04e423cbfd90a15ed6fbab3f111803bc6544.zip
Add retry_on/discard_on for better exception handling
Diffstat (limited to 'activejob/lib/active_job/exceptions.rb')
-rw-r--r--activejob/lib/active_job/exceptions.rb78
1 files changed, 78 insertions, 0 deletions
diff --git a/activejob/lib/active_job/exceptions.rb b/activejob/lib/active_job/exceptions.rb
new file mode 100644
index 0000000000..4217b5dc65
--- /dev/null
+++ b/activejob/lib/active_job/exceptions.rb
@@ -0,0 +1,78 @@
+require 'active_job/arguments'
+
+module ActiveJob
+ # Provides behavior for retrying and discarding jobs on exceptions.
+ module Exceptions
+ extend ActiveSupport::Concern
+
+ module ClassMethods
+ # Catch the exception and reschedule job for re-execution after so many seconds, for a specific number of attempts.
+ # If the exception keeps getting raised beyond the specified number of attempts, the exception is allowed to
+ # bubble up to the underlying queuing system, which may have its own retry mechanism or place it in a
+ # holding queue for inspection.
+ #
+ # ==== Options
+ # * <tt>:wait</tt> - Re-enqueues the job with the specified delay in seconds
+ # * <tt>:attempts</tt> - Re-enqueues the job the specified number of times
+ #
+ # ==== Examples
+ #
+ # class RemoteServiceJob < ActiveJob::Base
+ # retry_on Net::OpenTimeout, wait: 30.seconds, attempts: 10
+ #
+ # def perform(*args)
+ # # Might raise Net::OpenTimeout when the remote service is down
+ # end
+ # end
+ def retry_on(exception, wait: 3.seconds, attempts: 5)
+ rescue_from exception do |error|
+ logger.error "Retrying #{self.class} in #{wait} seconds, due to a #{exception}. The original exception was #{error.cause.inspect}."
+ retry_job wait: wait if executions < attempts
+ end
+ end
+
+ # Discard the job with no attempts to retry, if the exception is raised. This is useful when the subject of the job,
+ # like an Active Record, is no longer available, and the job is thus no longer relevant.
+ #
+ # ==== Example
+ #
+ # class SearchIndexingJob < ActiveJob::Base
+ # discard_on ActiveJob::DeserializationError
+ #
+ # def perform(record)
+ # # Will raise ActiveJob::DeserializationError if the record can't be deserialized
+ # end
+ # end
+ def discard_on(exception)
+ rescue_from exception do |error|
+ logger.error "Discarded #{self.class} due to a #{exception}. The original exception was #{error.cause.inspect}."
+ end
+ end
+ end
+
+ # Reschedules the job to be re-executed. This is useful in combination
+ # with the +rescue_from+ option. When you rescue an exception from your job
+ # you can ask Active Job to retry performing your job.
+ #
+ # ==== Options
+ # * <tt>:wait</tt> - Enqueues the job with the specified delay in seconds
+ # * <tt>:wait_until</tt> - Enqueues the job at the time specified
+ # * <tt>:queue</tt> - Enqueues the job on the specified queue
+ # * <tt>:priority</tt> - Enqueues the job with the specified priority
+ #
+ # ==== Examples
+ #
+ # class SiteScraperJob < ActiveJob::Base
+ # rescue_from(ErrorLoadingSite) do
+ # retry_job queue: :low_priority
+ # end
+ #
+ # def perform(*args)
+ # # raise ErrorLoadingSite if cannot scrape
+ # end
+ # end
+ def retry_job(options = {})
+ enqueue options
+ end
+ end
+end