One of my clients sends quite a few emails a day ... I would think over 500. They are mostly reports to clients or employees. The volume has become sufficient that intermittent issues with gmail (like the occasional and annoying "please try again later" error, or "too many logins" error) are causing missed reports and headaches. We also have some issues with the MapQuest API and this might help.
So I'm writing this class to encapsulate the process of the immediate retry (as opposed to the "give it back to Resque to try again later" process.)
Dependencies:
- Uses my gem Valuable which makes modeling easier.
Feedback welcome.
class Retryer < Valuable
has_value :attempts, :default => 3
has_value :logger, default: lambda { Rails.logger }
has_value :tries, :default => 0
has_collection :reraise, :default => [NotImplementedError]
def fire
raise "You must provide a block of code to try" unless block_given?
begin
self.tries += 1
yield
rescue Timeout::Error => err
logger && logger.warn(" - Timeout!")
sleep(0.5)
retry if self.tries < self.attempts
rescue => err
if reraise.include? err.class
logger && logger.warn(" - try #{self.tries} of #{attempts} failed and will be reraised: #{err}")
raise err
else
logger && logger.warn(" - try #{self.tries} of #{attempts} failed with #{err}")
retry if self.tries < self.attempts
end
end
end
end
One thing I'd like to add is the option to run another proc/block if all attempts raise
. For instance, notify Rollbar. I imagine it working somewhat like js promises. But I would only do it if I could figure out a readable interface...
self.
prefixes superfluous,fire
being an instance method. also, where isretry
defined? \$\endgroup\$self
to be a useful reminder to me that it's a class method. But they are superfluous with respect to the compiler.retry
is a Ruby keyword. \$\endgroup\$retry
. Re:self
, I'm struggling to see how this would be a class method. Aren't you creating Retryer instances? Doesn't each individual instance have its owntries
count, eg? \$\endgroup\$