fix(python): make retries safe and deterministic
This commit is contained in:
1 parent
f64d00673c
commit
82e5807752
8 files changed
+567
-50
No files matched your search
@@ -0,0 +1,89 @@
|
||||
"""Asynchronous HTTP execution with the Elydora retry contract."""
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
from typing import Any, Awaitable, Callable, Dict, Optional
|
||||
|
||||
import aiohttp
|
||||
|
||||
from ._retry import can_retry, log_retry, retry_delay_seconds, should_retry_response
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def request_with_retries(
|
||||
session: aiohttp.ClientSession,
|
||||
method: str,
|
||||
url: str,
|
||||
*,
|
||||
path: str,
|
||||
max_retries: int,
|
||||
response_handler: Callable[[aiohttp.ClientResponse], Awaitable[Any]],
|
||||
json_body: Any = None,
|
||||
params: Optional[Dict[str, str]] = None,
|
||||
headers: Optional[Dict[str, str]] = None,
|
||||
) -> Any:
|
||||
retry_count = 0
|
||||
while True:
|
||||
try:
|
||||
delay: Optional[float] = None
|
||||
reason = ""
|
||||
async with session.request(
|
||||
method,
|
||||
url,
|
||||
json=json_body,
|
||||
params=params,
|
||||
headers=headers,
|
||||
timeout=aiohttp.ClientTimeout(total=30),
|
||||
) as response:
|
||||
if should_retry_response(
|
||||
method, response.status, retry_count, max_retries
|
||||
):
|
||||
delay = retry_delay_seconds(
|
||||
retry_count, response.headers.get("Retry-After")
|
||||
)
|
||||
reason = f"http_{response.status}"
|
||||
else:
|
||||
return await response_handler(response)
|
||||
if delay is not None:
|
||||
log_retry(logger, method, path, retry_count, max_retries, delay, reason)
|
||||
await asyncio.sleep(delay)
|
||||
retry_count += 1
|
||||
continue
|
||||
except aiohttp.ClientConnectorError as error:
|
||||
if can_retry(
|
||||
method,
|
||||
retry_count,
|
||||
max_retries,
|
||||
request_known_unprocessed=True,
|
||||
):
|
||||
delay = retry_delay_seconds(retry_count)
|
||||
log_retry(
|
||||
logger,
|
||||
method,
|
||||
path,
|
||||
retry_count,
|
||||
max_retries,
|
||||
delay,
|
||||
type(error).__name__,
|
||||
)
|
||||
await asyncio.sleep(delay)
|
||||
retry_count += 1
|
||||
continue
|
||||
raise
|
||||
except (aiohttp.ClientConnectionError, asyncio.TimeoutError) as error:
|
||||
if can_retry(method, retry_count, max_retries):
|
||||
delay = retry_delay_seconds(retry_count)
|
||||
log_retry(
|
||||
logger,
|
||||
method,
|
||||
path,
|
||||
retry_count,
|
||||
max_retries,
|
||||
delay,
|
||||
type(error).__name__,
|
||||
)
|
||||
await asyncio.sleep(delay)
|
||||
retry_count += 1
|
||||
continue
|
||||
raise
|
||||
Reference in new issue
Block a user