Skip to content

Commit b1060b4

Browse files
committed
PYCO-69: Update error handling within retry logic
Changes ======= * Handle wider range of httpx errors * Allow BaseException to be raised if not a timeout or cancel scenario
1 parent fafcff1 commit b1060b4

2 files changed

Lines changed: 36 additions & 14 deletions

File tree

acouchbase_analytics/protocol/_core/retries.py

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
from functools import wraps
2020
from typing import TYPE_CHECKING, Any, Callable, Coroutine, Optional, Union
2121

22-
from httpx import ConnectError, ConnectTimeout, ReadTimeout, WriteError, WriteTimeout
22+
from httpx import ConnectError, ConnectTimeout, CookieConflict, HTTPError, InvalidURL, ReadTimeout, StreamError
2323

2424
from acouchbase_analytics.protocol._core.anyio_utils import sleep
2525
from couchbase_analytics.common.errors import AnalyticsError, InternalSDKError, TimeoutError
@@ -39,7 +39,7 @@ class AsyncRetryHandler:
3939

4040
@staticmethod
4141
async def handle_httpx_retry(
42-
ex: Union[ConnectError, ConnectTimeout, WriteError, WriteTimeout], ctx: AsyncRequestContext
42+
ex: Union[ConnectError, ConnectTimeout], ctx: AsyncRequestContext
4343
) -> Optional[Exception]:
4444
err_str = str(ex)
4545
if 'SSL:' in err_str:
@@ -107,7 +107,7 @@ async def wrapped_fn(self: AsyncHttpStreamingResponse) -> None: # noqa: C901
107107
continue
108108
await self._request_context.shutdown(type(ex), ex, ex.__traceback__)
109109
raise err from None
110-
except (ConnectError, ConnectTimeout, WriteError, WriteTimeout) as ex:
110+
except (ConnectError, ConnectTimeout) as ex:
111111
err = await AsyncRetryHandler.handle_httpx_retry(ex, self._request_context)
112112
if err is None:
113113
continue
@@ -120,6 +120,12 @@ async def wrapped_fn(self: AsyncHttpStreamingResponse) -> None: # noqa: C901
120120
raise TimeoutError(
121121
message='Request timed out.', context=str(self._request_context.error_context)
122122
) from None
123+
except (CookieConflict, HTTPError, StreamError, InvalidURL) as ex:
124+
# these are not retriable errors, so we just shutdown the request context and raise the error
125+
await self._request_context.shutdown(type(ex), ex, ex.__traceback__)
126+
raise AnalyticsError(
127+
cause=ex, message=str(ex), context=str(self._request_context.error_context)
128+
) from None
123129
except AnalyticsError:
124130
# if an AnalyticsError is raised, we have already shut down the request context
125131
raise
@@ -142,9 +148,15 @@ async def wrapped_fn(self: AsyncHttpStreamingResponse) -> None: # noqa: C901
142148
raise CancelledError('Request was cancelled.') from None
143149
if self._request_context.request_error is not None:
144150
raise self._request_context.request_error from None
145-
raise InternalSDKError(
146-
cause=ex, message=str(ex), context=str(self._request_context.error_context)
147-
) from None
151+
if isinstance(ex, Exception):
152+
# If the exception is an Exception, we raise it as an InternalSDKError as this is
153+
# an unexpected error in the SDK
154+
raise InternalSDKError(
155+
cause=ex, message=str(ex), context=str(self._request_context.error_context)
156+
) from None
157+
# we should have handled CancelledError and TimeoutError above, so if we get here,
158+
# raise the BaseException as is (most likely a KeyboardInterrupt)
159+
raise ex
148160
finally:
149161
if not RequestState.is_okay(self._request_context.request_state):
150162
await self.close()

couchbase_analytics/protocol/_core/retries.py

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from time import sleep
2121
from typing import TYPE_CHECKING, Callable, Optional, Union
2222

23-
from httpx import ConnectError, ConnectTimeout, ReadTimeout, WriteError, WriteTimeout
23+
from httpx import ConnectError, ConnectTimeout, CookieConflict, HTTPError, InvalidURL, ReadTimeout, StreamError
2424

2525
from couchbase_analytics.common.errors import AnalyticsError, InternalSDKError, TimeoutError
2626
from couchbase_analytics.common.logging import LogLevel
@@ -38,9 +38,7 @@ class RetryHandler:
3838
"""
3939

4040
@staticmethod
41-
def handle_httpx_retry(
42-
ex: Union[ConnectError, ConnectTimeout, WriteError, WriteTimeout], ctx: RequestContext
43-
) -> Optional[Exception]:
41+
def handle_httpx_retry(ex: Union[ConnectError, ConnectTimeout], ctx: RequestContext) -> Optional[Exception]:
4442
err_str = str(ex)
4543
if 'SSL:' in err_str:
4644
message = 'TLS connection error occurred.'
@@ -105,7 +103,7 @@ def wrapped_fn(self: HttpStreamingResponse) -> None: # noqa: C901
105103
continue
106104
self._request_context.shutdown(ex)
107105
raise err from None
108-
except (ConnectError, ConnectTimeout, WriteError, WriteTimeout) as ex:
106+
except (ConnectError, ConnectTimeout) as ex:
109107
err = RetryHandler.handle_httpx_retry(ex, self._request_context)
110108
if err is None:
111109
continue
@@ -118,6 +116,12 @@ def wrapped_fn(self: HttpStreamingResponse) -> None: # noqa: C901
118116
raise TimeoutError(
119117
message='Request timed out.', context=str(self._request_context.error_context)
120118
) from None
119+
except (CookieConflict, HTTPError, StreamError, InvalidURL) as ex:
120+
# these are not retriable errors, so we just shutdown the request context and raise the error
121+
self._request_context.shutdown(ex)
122+
raise AnalyticsError(
123+
cause=ex, message=str(ex), context=str(self._request_context.error_context)
124+
) from None
121125
except AnalyticsError:
122126
# if an AnalyticsError is raised, we have already shut down the request context
123127
raise
@@ -138,9 +142,15 @@ def wrapped_fn(self: HttpStreamingResponse) -> None: # noqa: C901
138142
) from None
139143
if self._request_context.cancelled:
140144
raise CancelledError('Request was cancelled.') from None
141-
raise InternalSDKError(
142-
cause=ex, message=str(ex), context=str(self._request_context.error_context)
143-
) from None
145+
if isinstance(ex, Exception):
146+
# If the exception is an Exception, we raise it as an InternalSDKError as this is
147+
# an unexpected error in the SDK
148+
raise InternalSDKError(
149+
cause=ex, message=str(ex), context=str(self._request_context.error_context)
150+
) from None
151+
# we should have handled CancelledError and TimeoutError above, so if we get here,
152+
# raise the BaseException as is (most likely a KeyboardInterrupt)
153+
raise ex
144154
finally:
145155
if not RequestState.is_okay(self._request_context.request_state):
146156
self.close()

0 commit comments

Comments
 (0)