Implementation guidance

Is there any way to implement a common Rescue and retry code, and call it anywhere when error.

I have multiple job files calling an API, instead of writing same rescue and retry code everywhere, i want to write it at one place and call everywhere

Maybe just wrap it in a module and include it where needed? Something like:

module WithRetry
  def with_retry attempts: 2, exception: StandardError
    yield
  rescue exception
    attempts -= 1
    retry if attempts > 1
  end
end

Usage would look something like:

class MyObject
  include WithRetry

  def some_method
    with_retry do
      something_flaky
    end
  end
end

You could override what exception to look for and the number of attempts to try with the arguments. If you want the behavior to be more global you could add with_retry to Kernel instead of making a custom module (not recommended as it pollutes the global namespace but wanted to mention it since you said you wanted to do it everywhere).

Disclaimer: I haven’t run the above code so there is probably an logic or syntax error hiding in there but the general idea should work


You mentioned this is for calling an API. Immediately retrying may not be the best approach as whatever the failure condition (rate limiting, outage, etc) likely has not resolved. Consider instead using a background job (e.g. ActiveJob). This way you can have a back-off delay between each attempt increasing your chance of success.

If you retry must be inline also consider just choosing a HTTP client that will do this for you. For example faraday-retry is some middleware for the Faraday HTTP client will retry requests and can even do a backoff delay as well as honor things such as the Retry-After HTTP header.

Actually regarding that back-off delay between each attempt, i was thinking off adding retry_on method (ActiveJob::Exceptions::ClassMethods)

something like this , i am not sure if this would work: module ErrorHelper

module ErrorHelper
    
    def retry_on exception: ZeroDivisionError, wait: ->(executions) {executions * Random.rand(1..2.minutes)}, attempts: 2
        rescue exception
            attempts -= 1
            retry if attempts > 1
    end
  end