This commit is contained in:
oliver
2026-08-28 10:11:11 -03:00
parent f533ff8e36
commit 585951c8ce
1380 changed files with 1540268 additions and 0 deletions
+92
View File
@@ -0,0 +1,92 @@
"""
Retry utilities with exponential backoff.
"""
import asyncio
from functools import wraps
from typing import Callable, TypeVar, Any, Tuple, Type
import logging
from tenacity import (
retry,
stop_after_attempt,
wait_exponential,
retry_if_exception_type,
before_sleep_log,
)
logger = logging.getLogger(__name__)
T = TypeVar("T")
def retry_with_backoff(
max_attempts: int = 3,
min_wait: float = 1.0,
max_wait: float = 60.0,
exceptions: Tuple[Type[Exception], ...] = (Exception,),
) -> Callable:
"""
Decorator for retrying functions with exponential backoff.
Args:
max_attempts: Maximum number of retry attempts
min_wait: Minimum wait time between retries (seconds)
max_wait: Maximum wait time between retries (seconds)
exceptions: Tuple of exception types to retry on
Returns:
Decorated function with retry logic
"""
return retry(
stop=stop_after_attempt(max_attempts),
wait=wait_exponential(multiplier=1, min=min_wait, max=max_wait),
retry=retry_if_exception_type(exceptions),
before_sleep=before_sleep_log(logger, logging.WARNING),
reraise=True,
)
async def async_retry_with_backoff(
func: Callable[..., T],
*args,
max_attempts: int = 3,
min_wait: float = 1.0,
max_wait: float = 60.0,
exceptions: Tuple[Type[Exception], ...] = (Exception,),
**kwargs,
) -> T:
"""
Async function with retry logic and exponential backoff.
Args:
func: Async function to call
*args: Positional arguments for func
max_attempts: Maximum number of retry attempts
min_wait: Minimum wait time between retries (seconds)
max_wait: Maximum wait time between retries (seconds)
exceptions: Tuple of exception types to retry on
**kwargs: Keyword arguments for func
Returns:
Result of the function call
"""
last_exception = None
wait_time = min_wait
for attempt in range(1, max_attempts + 1):
try:
return await func(*args, **kwargs)
except exceptions as e:
last_exception = e
if attempt < max_attempts:
logger.warning(
f"Attempt {attempt}/{max_attempts} failed: {e}. "
f"Retrying in {wait_time:.1f}s..."
)
await asyncio.sleep(wait_time)
wait_time = min(wait_time * 2, max_wait)
else:
logger.error(f"All {max_attempts} attempts failed: {e}")
raise last_exception