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
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