aboutsummaryrefslogtreecommitdiffstats
path: root/activejob/lib/active_job
diff options
context:
space:
mode:
authorAlberto Almagro <albertoalmagro@gmail.com>2018-11-23 20:31:14 +0100
committerDavid Heinemeier Hansson <david@loudthinking.com>2018-11-23 11:31:14 -0800
commit95d9c3b3d6828b8ce37591e68d4239ce8c18460b (patch)
tree83c6f7d77c74d1924d018451fee02ea25496e51f /activejob/lib/active_job
parenta190e8cc9fd7c459631ffd98e254adb3638afd18 (diff)
downloadrails-95d9c3b3d6828b8ce37591e68d4239ce8c18460b.tar.gz
rails-95d9c3b3d6828b8ce37591e68d4239ce8c18460b.tar.bz2
rails-95d9c3b3d6828b8ce37591e68d4239ce8c18460b.zip
Keep executions for each specific exception (#34352)
* Keep executions for each specific declaration Fixes #34337 ActiveJob used the global executions counter to control the number of times a job should be retried. The problem with this approach was that in case a job raised different exceptions during its executions they weren't retried the number of times defined by their `attemps` number. **Example:** Having the following job: ```ruby class BuggyJob < ActiveJob::Base retry_on CustomException, attemps: 3 retry_on OtherException, attempts: 3 end ``` If the job raised `CustomException` in the first two executions and then it raised `OtherException`, the job wasn't retried anymore because the global executions counter was already indicating 3 attempts. With this patch each `retry_on` declaration has its specific counter so that the first two executions that raise `CustomException` don't affect the retries count that future exceptions may have. * Revert "clarifies documentation around the attempts arugment to retry_on" This reverts commit 86aa8f8c5631f77ed9a208e5107003c01512133e.
Diffstat (limited to 'activejob/lib/active_job')
-rw-r--r--activejob/lib/active_job/core.rb9
-rw-r--r--activejob/lib/active_job/exceptions.rb8
2 files changed, 13 insertions, 4 deletions
diff --git a/activejob/lib/active_job/core.rb b/activejob/lib/active_job/core.rb
index 698153636b..4ab62f89b0 100644
--- a/activejob/lib/active_job/core.rb
+++ b/activejob/lib/active_job/core.rb
@@ -28,6 +28,12 @@ module ActiveJob
# Number of times this job has been executed (which increments on every retry, like after an exception).
attr_accessor :executions
+ # Hash that contains the number of times this job handled errors for each specific retry_on declaration.
+ # Keys are the string representation of the exceptions listed in the retry_on declaration,
+ # while its associated value holds the number of executions where the corresponding retry_on
+ # declaration handled one of its listed exceptions.
+ attr_accessor :exception_executions
+
# I18n.locale to be used during the job.
attr_accessor :locale
@@ -75,6 +81,7 @@ module ActiveJob
@queue_name = self.class.queue_name
@priority = self.class.priority
@executions = 0
+ @exception_executions = Hash.new(0)
end
# Returns a hash with the job data that can safely be passed to the
@@ -88,6 +95,7 @@ module ActiveJob
"priority" => priority,
"arguments" => serialize_arguments_if_needed(arguments),
"executions" => executions,
+ "exception_executions" => exception_executions,
"locale" => I18n.locale.to_s,
"timezone" => Time.zone.try(:name)
}
@@ -126,6 +134,7 @@ module ActiveJob
self.priority = job_data["priority"]
self.serialized_arguments = job_data["arguments"]
self.executions = job_data["executions"]
+ self.exception_executions = job_data["exception_executions"]
self.locale = job_data["locale"] || I18n.locale.to_s
self.timezone = job_data["timezone"] || Time.zone.try(:name)
end
diff --git a/activejob/lib/active_job/exceptions.rb b/activejob/lib/active_job/exceptions.rb
index bc9e168971..53984a4e49 100644
--- a/activejob/lib/active_job/exceptions.rb
+++ b/activejob/lib/active_job/exceptions.rb
@@ -9,7 +9,6 @@ module ActiveJob
module ClassMethods
# Catch the exception and reschedule job for re-execution after so many seconds, for a specific number of attempts.
- # The number of attempts includes the total executions of a job, not just the retried executions.
# 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.
@@ -22,8 +21,7 @@ module ActiveJob
# as a computing proc that the number of executions so far as an argument, or as a symbol reference of
# <tt>:exponentially_longer</tt>, which applies the wait algorithm of <tt>(executions ** 4) + 2</tt>
# (first wait 3s, then 18s, then 83s, etc)
- # * <tt>:attempts</tt> - Re-enqueues the job the specified number of times (default: 5 attempts),
- # attempts here refers to the total number of times the job is executed, not just retried executions
+ # * <tt>:attempts</tt> - Re-enqueues the job the specified number of times (default: 5 attempts)
# * <tt>:queue</tt> - Re-enqueues the job on a different queue
# * <tt>:priority</tt> - Re-enqueues the job with a different priority
#
@@ -46,7 +44,9 @@ module ActiveJob
# end
def retry_on(*exceptions, wait: 3.seconds, attempts: 5, queue: nil, priority: nil)
rescue_from(*exceptions) do |error|
- if executions < attempts
+ exception_executions[exceptions.to_s] += 1
+
+ if exception_executions[exceptions.to_s] < attempts
retry_job wait: determine_delay(wait), queue: queue, priority: priority, error: error
else
if block_given?