kamui/retriable

Allow retrying based on the "cause" exception

jeremyhaile opened this issue · 5 comments

Sometimes APIs wrap exceptions so that the top-level exception by itself isn't enough to determine whether or not to retry the code. However, the cause exception could be used to determine that the code should be retried.

Currently retriable only allows retrying based on the raised exception, and doesn't provide a way to examine the caused exception.

Here is an example. In this case, we want to retry based on the RootCauseError, but since the API we're using actually raises DemoError, the code block doesn't get retried.

class RootCauseError < StandardError; end
class DemoError < StandardError; end
begin
  i = 0
  Retriable.retriable on: [RootCauseError] do
    begin
      puts "Try #{i += 1}..."
      raise RootCauseError.new("BUT THIS IS THE CAUSE")
    rescue => e
      raise DemoError.new("RETRIABLE ONLY LOOKS AT THIS")
    end
  end
rescue => e
  puts "Raised error: #{e.inspect}"
  puts "Cause error: #{e.cause.inspect}"
end

Two ideas for solving this:

  1. Make retriable examine the cause of errors as well as the errors itself. This could either only go one level deep, or recurse...perhaps with a limit on recursion depth.
  2. Allow passing in a proc or method to the on option that would then be invoked with the error and would return true or false to determine if the error should be retried. This proc could then examine the error class and its cause, and would have the benefit of also supporting lots of advanced cases that are not possible right now.
kamui commented

Sorry I never replied to this ticket. I wanted to think about solutions for this and see if other users had similar issues. I haven't come to any conclusions, but wanted to comment to let you know I've read this issue.

you can sort of do this with a regex in the :on parameter? often the "cause" gets stringified into the message of the actually raised parameter - that's a common pattern i've seen with Faraday, for instance.

other than that i don't see any way to deal with the example posted; Retriable can't reach into the block and figure out that someone rescued one exception and chose to raise a different type of exception.

re-reading my comment, i actually had no idea about the Exception#cause feature in ruby 2.1. This issue makes a lot more sense to me now and seems like it might be worth implementing, though it will require some hackery depending on the ruby version.

It would be nice if the on accepted a block and retry only if the result of the block is true

@rafaelsales i think that would be pretty straightforward to implement if you wanted to try to open a PR, though i'm not sure it's that relevant to this issue