mirror of
https://github.com/MISP/misp-galaxy.git
synced 2024-11-26 16:57:18 +00:00
115 lines
3.1 KiB
Python
115 lines
3.1 KiB
Python
|
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
|
||
|
|
||
|
|
||
|
class AbstractBackoff(ABC):
|
||
|
"""Backoff interface"""
|
||
|
|
||
|
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
|
||
|
|
||
|
@abstractmethod
|
||
|
def compute(self, failures: int) -> float:
|
||
|
"""Compute backoff in seconds upon failure"""
|
||
|
pass
|
||
|
|
||
|
|
||
|
class ConstantBackoff(AbstractBackoff):
|
||
|
"""Constant backoff upon failure"""
|
||
|
|
||
|
def __init__(self, backoff: float) -> None:
|
||
|
"""`backoff`: backoff time in seconds"""
|
||
|
self._backoff = backoff
|
||
|
|
||
|
def compute(self, failures: int) -> float:
|
||
|
return self._backoff
|
||
|
|
||
|
|
||
|
class NoBackoff(ConstantBackoff):
|
||
|
"""No backoff upon failure"""
|
||
|
|
||
|
def __init__(self) -> None:
|
||
|
super().__init__(0)
|
||
|
|
||
|
|
||
|
class ExponentialBackoff(AbstractBackoff):
|
||
|
"""Exponential backoff upon failure"""
|
||
|
|
||
|
def __init__(self, cap: float = DEFAULT_CAP, base: float = DEFAULT_BASE):
|
||
|
"""
|
||
|
`cap`: maximum backoff time in seconds
|
||
|
`base`: base backoff time in seconds
|
||
|
"""
|
||
|
self._cap = cap
|
||
|
self._base = base
|
||
|
|
||
|
def compute(self, failures: int) -> float:
|
||
|
return min(self._cap, self._base * 2**failures)
|
||
|
|
||
|
|
||
|
class FullJitterBackoff(AbstractBackoff):
|
||
|
"""Full jitter backoff upon failure"""
|
||
|
|
||
|
def __init__(self, cap: float = DEFAULT_CAP, base: float = DEFAULT_BASE) -> None:
|
||
|
"""
|
||
|
`cap`: maximum backoff time in seconds
|
||
|
`base`: base backoff time in seconds
|
||
|
"""
|
||
|
self._cap = cap
|
||
|
self._base = base
|
||
|
|
||
|
def compute(self, failures: int) -> float:
|
||
|
return random.uniform(0, min(self._cap, self._base * 2**failures))
|
||
|
|
||
|
|
||
|
class EqualJitterBackoff(AbstractBackoff):
|
||
|
"""Equal jitter backoff upon failure"""
|
||
|
|
||
|
def __init__(self, cap: float = DEFAULT_CAP, base: float = DEFAULT_BASE) -> None:
|
||
|
"""
|
||
|
`cap`: maximum backoff time in seconds
|
||
|
`base`: base backoff time in seconds
|
||
|
"""
|
||
|
self._cap = cap
|
||
|
self._base = base
|
||
|
|
||
|
def compute(self, failures: int) -> float:
|
||
|
temp = min(self._cap, self._base * 2**failures) / 2
|
||
|
return temp + random.uniform(0, temp)
|
||
|
|
||
|
|
||
|
class DecorrelatedJitterBackoff(AbstractBackoff):
|
||
|
"""Decorrelated jitter backoff upon failure"""
|
||
|
|
||
|
def __init__(self, cap: float = DEFAULT_CAP, base: float = DEFAULT_BASE) -> None:
|
||
|
"""
|
||
|
`cap`: maximum backoff time in seconds
|
||
|
`base`: base backoff time in seconds
|
||
|
"""
|
||
|
self._cap = cap
|
||
|
self._base = base
|
||
|
self._previous_backoff = 0
|
||
|
|
||
|
def reset(self) -> None:
|
||
|
self._previous_backoff = 0
|
||
|
|
||
|
def compute(self, failures: int) -> float:
|
||
|
max_backoff = max(self._base, self._previous_backoff * 3)
|
||
|
temp = random.uniform(self._base, max_backoff)
|
||
|
self._previous_backoff = min(self._cap, temp)
|
||
|
return self._previous_backoff
|
||
|
|
||
|
|
||
|
def default_backoff():
|
||
|
return EqualJitterBackoff()
|