import random
from abc import ABC, abstractmethod
# Maximum backoff between each retry in seconds
DEFAULT_CAP = 0.512
# Minimum backoff between each retry in seconds
DEFAULT_BASE = 0.008
[docs]class AbstractBackoff(ABC):
"""Backoff interface"""
[docs] def reset(self):
"""
Reset internal state before an operation.
`reset` is called once at the beginning of
every call to `Retry.call_with_retry`
"""
pass
[docs] @abstractmethod
def compute(self, failures):
"""Compute backoff in seconds upon failure"""
pass
[docs]class ConstantBackoff(AbstractBackoff):
"""Constant backoff upon failure"""
def __init__(self, backoff):
"""`backoff`: backoff time in seconds"""
self._backoff = backoff
[docs] def compute(self, failures):
return self._backoff
[docs]class NoBackoff(ConstantBackoff):
"""No backoff upon failure"""
def __init__(self):
super().__init__(0)
[docs]class ExponentialBackoff(AbstractBackoff):
"""Exponential backoff upon failure"""
def __init__(self, cap=DEFAULT_CAP, base=DEFAULT_BASE):
"""
`cap`: maximum backoff time in seconds
`base`: base backoff time in seconds
"""
self._cap = cap
self._base = base
[docs] def compute(self, failures):
return min(self._cap, self._base * 2**failures)
[docs]class FullJitterBackoff(AbstractBackoff):
"""Full jitter backoff upon failure"""
def __init__(self, cap=DEFAULT_CAP, base=DEFAULT_BASE):
"""
`cap`: maximum backoff time in seconds
`base`: base backoff time in seconds
"""
self._cap = cap
self._base = base
[docs] def compute(self, failures):
return random.uniform(0, min(self._cap, self._base * 2**failures))
[docs]class EqualJitterBackoff(AbstractBackoff):
"""Equal jitter backoff upon failure"""
def __init__(self, cap=DEFAULT_CAP, base=DEFAULT_BASE):
"""
`cap`: maximum backoff time in seconds
`base`: base backoff time in seconds
"""
self._cap = cap
self._base = base
[docs] def compute(self, failures):
temp = min(self._cap, self._base * 2**failures) / 2
return temp + random.uniform(0, temp)
def default_backoff():
return EqualJitterBackoff()