diff options
author | Eric Lemoine <eric.lemoine@getalma.eu> | 2022-06-19 03:56:53 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-06-19 04:56:53 +0300 |
commit | bea72995fd39b01e2f0a1682b16b6c7690933f36 (patch) | |
tree | 477b9093e9664a13add96681a2012ded0ffbc798 /redis/asyncio | |
parent | 33702983b8b0a55d29189babb631ea108ee8404f (diff) | |
download | redis-py-bea72995fd39b01e2f0a1682b16b6c7690933f36.tar.gz |
Fix retries in async mode (#2180)
* Avoid mutating a global retry_on_error list
* Make retries config consistent in sync and async
* Fix async retries
* Add new TestConnectionConstructorWithRetry tests
Diffstat (limited to 'redis/asyncio')
-rw-r--r-- | redis/asyncio/client.py | 17 | ||||
-rw-r--r-- | redis/asyncio/connection.py | 17 | ||||
-rw-r--r-- | redis/asyncio/retry.py | 8 |
3 files changed, 39 insertions, 3 deletions
diff --git a/redis/asyncio/client.py b/redis/asyncio/client.py index 6db5489..3d59016 100644 --- a/redis/asyncio/client.py +++ b/redis/asyncio/client.py @@ -158,6 +158,7 @@ class Redis( encoding_errors: str = "strict", decode_responses: bool = False, retry_on_timeout: bool = False, + retry_on_error: Optional[list] = None, ssl: bool = False, ssl_keyfile: Optional[str] = None, ssl_certfile: Optional[str] = None, @@ -176,8 +177,10 @@ class Redis( ): """ Initialize a new Redis client. - To specify a retry policy, first set `retry_on_timeout` to `True` - then set `retry` to a valid `Retry` object + To specify a retry policy for specific errors, first set + `retry_on_error` to a list of the error/s to retry on, then set + `retry` to a valid `Retry` object. + To retry on TimeoutError, `retry_on_timeout` can also be set to `True`. """ kwargs: Dict[str, Any] # auto_close_connection_pool only has an effect if connection_pool is @@ -188,6 +191,10 @@ class Redis( auto_close_connection_pool if connection_pool is None else False ) if not connection_pool: + if not retry_on_error: + retry_on_error = [] + if retry_on_timeout is True: + retry_on_error.append(TimeoutError) kwargs = { "db": db, "username": username, @@ -197,6 +204,7 @@ class Redis( "encoding_errors": encoding_errors, "decode_responses": decode_responses, "retry_on_timeout": retry_on_timeout, + "retry_on_error": retry_on_error, "retry": copy.deepcopy(retry), "max_connections": max_connections, "health_check_interval": health_check_interval, @@ -461,7 +469,10 @@ class Redis( is not a TimeoutError """ await conn.disconnect() - if not (conn.retry_on_timeout and isinstance(error, TimeoutError)): + if ( + conn.retry_on_error is None + or isinstance(error, tuple(conn.retry_on_error)) is False + ): raise error # COMMAND EXECUTION AND PROTOCOL PARSING diff --git a/redis/asyncio/connection.py b/redis/asyncio/connection.py index 38465fc..35536fc 100644 --- a/redis/asyncio/connection.py +++ b/redis/asyncio/connection.py @@ -578,6 +578,7 @@ class Connection: "socket_type", "redis_connect_func", "retry_on_timeout", + "retry_on_error", "health_check_interval", "next_health_check", "last_active_at", @@ -606,6 +607,7 @@ class Connection: socket_keepalive_options: Optional[Mapping[int, Union[int, bytes]]] = None, socket_type: int = 0, retry_on_timeout: bool = False, + retry_on_error: Union[list, _Sentinel] = SENTINEL, encoding: str = "utf-8", encoding_errors: str = "strict", decode_responses: bool = False, @@ -631,12 +633,19 @@ class Connection: self.socket_keepalive_options = socket_keepalive_options or {} self.socket_type = socket_type self.retry_on_timeout = retry_on_timeout + if retry_on_error is SENTINEL: + retry_on_error = [] if retry_on_timeout: + retry_on_error.append(TimeoutError) + self.retry_on_error = retry_on_error + if retry_on_error: if not retry: self.retry = Retry(NoBackoff(), 1) else: # deep-copy the Retry object as it is mutable self.retry = copy.deepcopy(retry) + # Update the retry's supported errors with the specified errors + self.retry.update_supported_errors(retry_on_error) else: self.retry = Retry(NoBackoff(), 0) self.health_check_interval = health_check_interval @@ -1169,6 +1178,7 @@ class UnixDomainSocketConnection(Connection): # lgtm [py/missing-call-to-init] encoding_errors: str = "strict", decode_responses: bool = False, retry_on_timeout: bool = False, + retry_on_error: Union[list, _Sentinel] = SENTINEL, parser_class: Type[BaseParser] = DefaultParser, socket_read_size: int = 65536, health_check_interval: float = 0.0, @@ -1190,12 +1200,19 @@ class UnixDomainSocketConnection(Connection): # lgtm [py/missing-call-to-init] self.socket_timeout = socket_timeout self.socket_connect_timeout = socket_connect_timeout or socket_timeout or None self.retry_on_timeout = retry_on_timeout + if retry_on_error is SENTINEL: + retry_on_error = [] if retry_on_timeout: + retry_on_error.append(TimeoutError) + self.retry_on_error = retry_on_error + if retry_on_error: if retry is None: self.retry = Retry(NoBackoff(), 1) else: # deep-copy the Retry object as it is mutable self.retry = copy.deepcopy(retry) + # Update the retry's supported errors with the specified errors + self.retry.update_supported_errors(retry_on_error) else: self.retry = Retry(NoBackoff(), 0) self.health_check_interval = health_check_interval diff --git a/redis/asyncio/retry.py b/redis/asyncio/retry.py index 0934ad0..7c5e3b0 100644 --- a/redis/asyncio/retry.py +++ b/redis/asyncio/retry.py @@ -35,6 +35,14 @@ class Retry: self._retries = retries self._supported_errors = supported_errors + def update_supported_errors(self, specified_errors: list): + """ + Updates the supported errors with the specified error types + """ + self._supported_errors = tuple( + set(self._supported_errors + tuple(specified_errors)) + ) + async def call_with_retry( self, do: Callable[[], Awaitable[T]], fail: Callable[[RedisError], Any] ) -> T: |