Skip to content

Commit 4603cd4

Browse files
authored
Add support for retrying failed requests (#17)
1 parent 7953b0f commit 4603cd4

File tree

1 file changed

+43
-15
lines changed

1 file changed

+43
-15
lines changed

hubitatmaker/hub.py

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from typing import Any, Callable, Dict, Iterator, List, Mapping, Optional, Union
99
from urllib.parse import ParseResult, quote, urlparse
1010

11+
import asyncio
1112
import aiohttp
1213
import getmac
1314

@@ -18,6 +19,8 @@
1819

1920
Listener = Callable[[Event], None]
2021

22+
MAX_REQUEST_ATTEMPT_COUNT = 3
23+
REQUEST_RETRY_DELAY_INTERVAL = 0.5
2124

2225
_LOGGER = getLogger(__name__)
2326

@@ -391,22 +394,47 @@ async def _load_modes(self) -> None:
391394
async def _api_request(self, path: str, method="GET") -> Any:
392395
"""Make a Maker API request."""
393396
params = {"access_token": self.token}
394-
conn = aiohttp.TCPConnector(ssl=False)
395-
try:
396-
async with aiohttp.request(
397-
method, f"{self.api_url}/{path}", params=params, connector=conn
398-
) as resp:
399-
if resp.status >= 400:
400-
if resp.status == 401:
401-
raise InvalidToken()
402-
else:
397+
398+
attempt = 0
399+
while attempt <= MAX_REQUEST_ATTEMPT_COUNT:
400+
attempt += 1
401+
conn = aiohttp.TCPConnector(ssl=False)
402+
try:
403+
async with aiohttp.request(
404+
method, f"{self.api_url}/{path}", params=params, connector=conn
405+
) as resp:
406+
if resp.status >= 400:
407+
# retry on server errors or request timeout w/ increasing delay
408+
if resp.status >= 500 or resp.status == 408:
409+
if attempt < MAX_REQUEST_ATTEMPT_COUNT:
410+
_LOGGER.debug(
411+
"%s request to %s failed with code %d: %s. Retrying...",
412+
method, path, resp.status, resp.reason
413+
)
414+
await asyncio.sleep(attempt * REQUEST_RETRY_DELAY_INTERVAL)
415+
continue
416+
417+
if resp.status == 401:
418+
raise InvalidToken()
419+
else:
420+
raise RequestError(resp)
421+
json = await resp.json()
422+
if "error" in json and json["error"]:
403423
raise RequestError(resp)
404-
json = await resp.json()
405-
if "error" in json and json["error"]:
406-
raise RequestError(resp)
407-
return json
408-
finally:
409-
await conn.close()
424+
return json
425+
except (aiohttp.ClientConnectionError, asyncio.TimeoutError) as e:
426+
# catch connection exceptions to retry w/ increasing delay
427+
if attempt < MAX_REQUEST_ATTEMPT_COUNT:
428+
_LOGGER.debug(
429+
"%s request to %s failed with %s. Retrying...",
430+
method, path, str(e)
431+
)
432+
await asyncio.sleep(attempt * REQUEST_RETRY_DELAY_INTERVAL)
433+
continue
434+
else:
435+
raise e
436+
finally:
437+
await conn.close()
410438

411439
async def _start_server(self) -> None:
412440
"""Start an event listener server."""

0 commit comments

Comments
 (0)