import abc import time import random class RetryPolicy(abc.ABC): @abc.abstractmethod def sleep(self, retry_num): """ How long to sleep in milliseconds. :param retry_num: the number of retry (starting from zero) """ assert retry_num >= 0 @abc.abstractmethod def time_out_message(self): """ Generate a error message for user on time out. """ pass @abc.abstractmethod def num_retries(self): """ Max number retries. """ pass class FixedRetryPolicy(RetryPolicy): def __init__(self, sleep_time=1000, max_num_retries=10): """ Create a fixed time retry policy. :param sleep_time: time to sleep in milliseconds. :param max_num_retries: max number of retries. """ self.sleep_time = sleep_time self.max_num_retries = max_num_retries def sleep(self, retry_num): time.sleep(self.sleep_time / 1000) def time_out_message(self): return "Tried {} times with {} milliseconds apart.".format( self.max_num_retries, self.sleep_time ) def num_retries(self): return self.max_num_retries class ExponentialBackoffRetryPolicy(RetryPolicy): def __init__(self, min_backoff=200, max_backoff=10000, max_num_retries=20): """ Create a truncated exponential backoff policy. See: https://en.wikipedia.org/wiki/Exponential_backoff :param min_backoff: minimum time to sleep in milliseconds. :param max_backoff: maximum time to sleep in milliseconds. :param max_num_retries: max number of retries. """ self.min_backoff = min_backoff self.max_backoff = max_backoff self.max_num_retries = max_num_retries self.multiplier = 2 def sleep(self, retry_num): # Add a random component to avoid synchronized retries wiggle = random.randint(0, 100) sleep_ms = min( self.min_backoff + self.multiplier ** retry_num + wiggle, self.max_backoff ) time.sleep(sleep_ms / 1000) def time_out_message(self): return ( "Tried {} times with increasing delay (from {} to {} milliseconds).".format( self.max_num_retries, self.min_backoff, self.max_backoff ) ) def num_retries(self): return self.max_num_retries