Last active
September 16, 2020 23:33
-
-
Save lee-pai-long/4c622e4460ee0b97e3022fbe9e176aa1 to your computer and use it in GitHub Desktop.
Retry an action with exponential backoff
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"""Retry an action with exponential backoff. | |
Inspired by: | |
- https://www.toptal.com/python/python-parameterized-design-patterns#classes-as-exceptions | |
- https://stackoverflow.com/a/33577016/3775614 | |
""" | |
# WARNING: Never used yet | |
# TODO: Add a unit test module | |
import time | |
class MaxAttemptsReached(Exception): | |
"""Raised when retry_with_backoff max attempt is reach.""" | |
def __init__(self, action, max_attempts): | |
"""MaxAttemptsReached | |
Arguments: | |
- action (callable): the attempted action | |
- max_attempts (int): the number of attempts tried before faulting to this | |
""" | |
self.message = "Max attempts {0} reached for action {1}".format( | |
max_attempts, getattr(action, '__name__', repr(action)) | |
) | |
super().__init__(self.message) | |
def retry_with_backoff( | |
action, | |
exceptions, | |
max_attempts=10, | |
attempts_so_far=0 | |
): | |
"""Retry an action with exponential backoff. | |
Attempt an action that could fail, | |
and retry with exponential backoff, | |
until a maximum number of attempts is reached. | |
Arguments: | |
- action (callable): The action to attempt | |
- exceptions (Exception or tuple): Can be any exception | |
or a tuple of exceptions to catch. | |
- max_attempts (int): The maximum attempt to try, | |
default: 10 | |
- attempts_so_far (int): The current number of attempts, | |
mainly use for recursiveness, | |
default: 0 | |
Returns: | |
- the result of {action} if it succeed, | |
or retries with a sleep time of {attempts_so_far ** 2} | |
""" | |
try: | |
return action() | |
except exceptions: | |
attempts_so_far += 1 | |
if attempts_so_far >= max_attempts: | |
raise MaxAttemptsReached(action, max_attempts) | |
else: | |
time.sleep(attempts_so_far ** 2) | |
return retry_with_backoff( | |
action, exceptions, max_attempts, attempts_so_far | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment