Files

39 lines
1.1 KiB
Python

"""Bounded retry with exponential backoff + jitter."""
from __future__ import annotations
import random
import time
from typing import Callable, Optional, Tuple, Type
def retry(
fn: Callable,
*,
max_attempts: int = 3,
base_delay: float = 0.1,
max_delay: float = 10.0,
retryable: Tuple[Type[BaseException], ...] = (Exception,),
sleep: Callable[[float], None] = time.sleep,
rng: Optional[random.Random] = None,
) -> object:
if max_attempts < 1:
raise ValueError("max_attempts must be >= 1")
if base_delay < 0 or max_delay < 0:
raise ValueError("delays must be >= 0")
r = rng if rng is not None else random.Random()
last_exc = None
for attempt in range(max_attempts):
try:
return fn()
except retryable as e:
last_exc = e
if attempt == max_attempts - 1:
break
exp = min(max_delay, base_delay * (2 ** attempt))
delay = r.uniform(0, exp) if exp > 0 else 0.0
sleep(delay)
except BaseException:
raise
assert last_exc is not None
raise last_exc